你好,欢迎!我已经迫不及待地要跟你一起学习命令行界面这一个神秘而有趣的交互方式了。无论你是因为什么打开这篇文章、也不管你对命令行界面的了解几何,我都希望你能够有收获。
让我们开始吧!
终端、命令行还有 Shell 程序(上)
来看看一些名词吧:终端、命令行、虚拟控制台(tty)、shell 程序。是不是有点晕。很多教程会选择在一开始就向读者表明这些名词的真正含义和区别,但我从来就没看懂他们在说些什么。所以,在这里,我们暂时将这些名词都看做同一个东西(除了 shell 程序稍有不同),相信我,现在这不会有什么害处。
所谓终端,就是一个只能通过键盘输入命令来控制计算机的交互方式。你可以用各种各样的方式找到它:如果你使用 Windows 10 或 11,你可以通过在搜索栏搜索“终端”或者 Powershell 来打开它;如果你使用 macOS,你可以在启动台搜索“终端”(或者试试英文的 Terminal)来打开终端;如果你使用 GNU/Linux,你应该知道如何找到你的终端。通常,搜索你使用的“操作系统 + 终端”能够在搜索引擎里找到打开终端的方法。
下面是一种典型的终端显示:
user@hostname ~>
其中,user
表示用户名,也就是在操作计算机的“人”。通常跟你给电脑开机然后解锁电脑那个画面看到的一致。hostname
是计算机名,也就是计算机的名字。而 ~
表示我们“在哪”;这有一点难以理解,就是我们现在位于哪个磁盘哪个文件夹下面。>
表示终端处于一个等待我们输入命令的状态,这个符号会根据你使用的终端不同而改变,可能是 %
或者其他符号。
我是谁、我在哪、我旁边都有啥
让我们来回答这三个问题。
通过输入 whoami
并按下回车(Enter,Return,↵)键,我们可以知道我们的用户名:
user@hostname ~> whoami
user
user@hostname ~>
你可以打开你电脑上的终端跟着我一起操作,这会帮助你理解到底发生了什么。如果你正在使用一个无法打开终端的设备的话,让我来告诉你我们做了什么:输入命令并按下回车后,终端为我们新起了一行,并且在那一行上展示了命令的结果;之后,终端又为我们新起了一行,重新回到了等待输入的状态。
在我们输入 whoami
后,终端给出了我们的用户名。
通过输入 pwd
并按下回车键,我们可以知道我们在哪:
user@hostname ~> pwd
/home/user
user@hostname ~>
我这里演示的是一个典型的 *nix 系统的终端,如果你使用 Windows,终端给出的回复可能会是你更熟悉的样子。
为什么明明尖括号左边写的是 ~
而不是 /home/user
呢?这是一个常用的缩写。这个目录表示用户的“主目录”,通俗地说,就是你存放“文档”、“下载”和“图片”那些文件夹的地方。由于它太常用了,而且不同的用户有自己独立的文件夹,所以一般会显示这么一个缩写。
通过输入 ls
并按下回车键,我们可以知道我们所在的地方有什么文件:
user@hostname ~> ls
Desktop
Documents
Downloads
Library
Movies
Music
Pictures
user@hostname ~>
可以看到,终端向我们回复了我们所在的位置,也就是主目录下的文件。可以看到里面包括了我们熟悉的下载等文件夹。
在终端中导航
到这里,我们已经对终端操作这种“一问一答”式的操作逻辑比较了解了。接下来,再次提及终端命令时,我不会强调需要按下回车键,只要记住这一点就好。让我们继续吧。
既然终端的基本属性里面包括一个“我在哪”,那么我们肯定就需要一个移动我们的位置的能力。通过在终端中输入 cd
命令,我们可以转到我们需要去的地方。
user@hostname ~> ls
Desktop
Documents
Downloads
Library
Movies
Music
Pictures
Projects
Public
user@hostname ~> cd Music
user@hostname ~/Music>
终端并没有给出命令的结果,而是直接变成了等待输入模式。等等,这个命令后面的单词是什么?
让我们来理解我们做了什么。对于一些简单的问题,我们可以很直接地向终端索要答案,就比如之前提到的“我在哪”,而对于一些比较复杂的问题,终端内置的命令没办法直接得到我们想要的东西,我们必须要给终端一些提示,也就是额外输入一些东西。拿我们这里的移动举例子,如果我们只告诉终端我们要移动,也就是仅仅只输入 cd
这个命令,终端完全不知道我们要去哪,所以我们得给终端一点提示:我们选择在命令后面加上我们的目的地,也就是 Music
文件夹。随后,终端得知我们的目的地后,就把我们带到我们的目的地。还记得尖括号左边的内容表示我们的位置吗,对比一下是不是有变化。
事实上,大部分终端命令都需要我们给它一点提示才能正常工作,不然的话要么完全罢工,要么就会由着终端自己的想法做出一些我们看起来很滑稽甚至很危险的事情。通常而言,命令的格式是这样的:
command arg1 arg2
其中第一个单词是我们希望终端做的事情,也就是命令。第二个和之后的单词是我们指导终端具体该怎么做的提示,在这里我们把它称为参数。参数可以是零个、一个甚至多个,取决于具体的命令是什么。每个单词之间使用空格分隔。
现在,我们只要记住我们跟终端约定好了, cd
后面紧跟着的一个唯一的参数是我们的目的地。
相对路径和绝对路径
假如我们都在 A 小区,当我问你 2 幢 3 单元在哪的时候,你一定不会会意成 B 小区的 2 幢 3 单元,而是会告诉我 A 小区的 2 幢 3 单元在哪。在这里,我们说“2 幢 3 单元”是相对于 A 小区的相对地址,如果我们都在 B 小区,那“2 幢 3 单元”就是相对于 B 小区的相对地址。这些地址的相对性体现在我们隐含了一些信息,导致我们必须要通过一定的推断才能知道这个地址具体指的到底是什么地方。
但是,我们考虑这么一个情景:我们都在 A 小区,当我问你海南省海口市 B 小区 2 幢 3 单元在哪,你一定能理解我指的到底是什么地方,而不会认为我说的是我们共同所在的 A 小区的 2 幢 3 单元。这种地址没有隐含任何信息,我们不需要推断就能明白这个地址指的是什么地方,那我们称这种地址是绝对地址(事实上还是隐含了我们所在的国家之类的信息的,所以绝对地址其实没有那么绝对)。
简单地回顾一下:相对地址是隐含了一些必要的信息的地址,我们需要进行一定量的推断才能知道这个地址指的到底是什么地方,同时这个地址的含义可能会根据上下文发生变化;绝对地址是没有任何隐含信息的地址,我们不需要推断就能得知这个地址的含义,也不随上下文的变化而变化。
回到终端的话题来,我们之前提到我们需要为 cd
命令指定一个目的地,就跟在现实中一样,这个目的地分为相对路径和绝对路径,而相对路径的隐含信息是我们终端所在的位置。
来看一些例子吧:
Music -- 相对路径,指本文件夹下的 Music 文件夹
Music/love.mp3 -- 相对路径,指本文件夹下的 Music 文件夹下的 love.mp3 文件
/home/user/Music -- 绝对路径
/home/user/Music/love.mp3 -- 绝对路径
难度提高一点,我们来看看两个新的缩写:
. -- 相对路径,指本文件夹
.. -- 相对路径,指本文件夹的上层文件夹
./Music -- 相对路径,指本文件夹下的 Music 文件夹
在 *nix 世界里(比如 macOS )文件系统的最上层是一个叫做 /
的文件夹,或者说目录。所有的文件都在这个目录下方。不要把它和分割文件夹的斜线搞混。在 Windows 世界里,驱动器是最上层的目录。比如说 C:\
,可以发现,Windows 世界的绝对地址需要特别注明驱动器号,尤其是你有多个驱动器的时候。换句话说,下面的都是 Windows 世界的合法绝对地址。
C:\Windows\
D:\Loves\rust.txt
你可能会惊讶地发现在 Windows 世界里,路径中的斜线方向与 *nix 世界里的方向正好相反。这是 Windows 系统的“特色”之一。你需要使用 Windows 系统的反斜线来注明路径。
与终端的约定(上)
我们先前提到,我们与终端约定好了 cd
命令后面的第一个单词是一个指向目的地的路径。这个路径可以是相对路径也可以是绝对路径。那这个约定从何说起呢?
对于大多数常用的终端命令,你可以通过在命令后面输入 --help
来查看这个命令到底怎么用。下面是一个示例:
user@hostname ~> git --help
usage: git [-v | --version] [-h | --help] [-C <path>] [-c <name>=<value>]
[--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
[-p | --paginate | -P | --no-pager] [--no-replace-objects] [--bare]
[--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
[--config-env=<name>=<envvar>] <command> [<args>]
These are common Git commands used in various situations:
start a working area (see also: git help tutorial)
clone Clone a repository into a new directory
init Create an empty Git repository or reinitialize an existing one
work on the current change (see also: git help everyday)
add Add file contents to the index
mv Move or rename a file, a directory, or a symlink
restore Restore working tree files
rm Remove files from the working tree and from the index
为了可读性,这里省略了一部分终端输出。
不过要注意的是, --help
只是约定俗称的习惯用法,并不是每个终端应用程序都允许你通过这个参数来查看它的用法。通常而言,当你错误地使用一个命令也会得到它的帮助信息。
或许更正确的做法是使用 man page。不过这个我们后面再聊,至于现在,你可以通过搜索引擎来搜索这个一个命令的正确用法。
查看文件
ls
不仅可以查看当前的工作目录(也就是我们所在的目录的另一个说法)有什么内容,你也可以在后面紧跟着你要查看的目录来查看那个目录的内容。下面是一个简单的示例:
user@hostname ~>ls
vinegar_runtime
user@hostname ~>ls vinegar_runtime
CMakeLists.txt
main.cpp
user@hostname ~>ls
可以看到,ls
命令正确地显示了 vinegar_runtime
这个文件夹下面的内容。
你可以使用 cat
来查看一个文件的内容。用法也很简单,在 cat
后面紧跟你要查看的那个文件就可以了。:
user@hostname ~> cat ./vinegar_runtime/CMakeLists.txt
cmake_minimum_required(VERSION 3.28)
project(vinegar_runtime)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
这个文件的内容就显示在终端的输出里面了。不过要注意的是,如果你的文件内容太多,终端可能会显示不全,最开头的一部分会丢失。至于怎么解决这个问题,我们后面再聊。
清除
你可以通过输入命令 clear
来清除屏幕上的内容,这不会删除任何东西,只是会让你的终端恢复到刚刚打开它的样子。
user@hostname ~> cat CMakeLists.txt
cmake_minimum_required(VERSION 3.28)
project(vinegar_runtime)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
user@hostname ~>
在我们输入完 clear
之后,你的界面应该会变成这个样子:
user@hostname ~>
如果你发现输入 clear
没有用的话,可以试试 ctrl + l
组合键。
提示1:请不要害怕终端
或许在本文开始的时候你会对这个这个通过黑色背景和闪烁光标交互的交互方式感到害怕。我希望你可以通过这篇文章获得在命令行界面里面站稳脚跟所需要的所有能力。A 面的所有命令都是无害的,尽情地在终端里尝试它们吧。
那么,A 面的内容就结束了。期待在后面的 B 面和 C 面见到你。到那时,我们会一起学习更多能让你把终端用起来的东西。你或许会发现,有些事情通过终端操作会更加顺手。
A 面小抄
A 面涉及的命令不多,我们快速过一遍吧。
- whoami 查看当前的用户
- pwd 查看当前的工作目录
- ls 展示目录下的内容
- cd 在文件系统中导航去到别的目录
- cat 查看文件内容
- clear 清除终端屏幕内容