- Shell 允许你执行程序,输入并获取某种半结构化的输出。
Lecture 1 Course Overview + The Shell
shell 是什么?
如今的计算机有着多种多样的交互接口让我们可以进行指令的的输入,从炫酷的图像用户界面(GUI),语音输入甚至是 AR/VR 都已经无处不在。 这些交互接口可以覆盖 80% 的使用场景,但是它们也从根本上限制了您的操作方式 —— 你不能点击一个不存在的按钮或者是用语音输入一个还没有被录入的指令。 为了充分利用计算机的能力,我们不得不回到最根本的方式,使用文字接口:Shell。
几乎所有您能够接触到的平台都支持某种形式的 shell,有些甚至还提供了多种 shell 供您选择。虽然它们之间有些细节上的差异,但是其核心功能都是一样的:它允许你执行程序,输入并获取某种半结构化的输出。
该笔记会使用 ubuntu-20.04.4-desktop-amd64 操作系统的终端作为使用的 Shell。 它的语法和其他的 shell 类似的。打开shell 提示符(您输入指令的地方),您首先需要打开终端 。您的设备通常都已经内置了终端,或者您也可以安装一个,非常简单。
★使用 shell
当您打开终端时,您会看到一个提示符,它看起来一般是这个样子的:
1
god@god-virtual-machine:~$
这是 shell 最主要的文本接口。它告诉你,你的用户名是
god
,你的主机名是god-virtual-machine
,并且你的当前工作目录(”current working directory”)或者说你当前所在的位置是~
(~ 表示”home“),$
符号表示您现在的身份不是 root 用户。在这个提示符中,您可以输入命令 ,命令最终会被 shell 解析。最简单的命令是执行一个程序:
1
2
3god@god-virtual-machine:~$ date
2023年 01月 22日 星期日 19:32:09 CST
god@god-virtual-machine:~$这里,我们执行了
date
这个程序,不出意料地,它打印出了当前的日期和时间。然后,shell 等待我们输入其他命令。我们还可以在执行命令的同时向程序传递参数 :
1
2god@god-virtual-machine:~$ echo hello
hello上例中,我们让 shell 执行
echo
,同时指定参数hello
。echo
程序将该参数打印出来。shell 基于空格分割命令并进行解析,然后执行第一个单词代表的程序,并将后续的单词作为程序可以访问的参数。
如果您希望传递的参数中包含空格(例如一个名为
My Photos
的文件夹),您要么用使用单引号,双引号将其包裹起来,要么使用转义符号\
进行处理(My\ Photos
)。例如1
god@god-virtual-machine:~$ mkdir "My Photos"
或者
1
god@god-virtual-machine:~$ mkdir My\ Photos
★但是,shell 是如何知道去哪里寻找
date
或echo
函数的呢?其实,类似于 Python 或 Ruby,shell 是一个编程环境,所以它具备变量、条件、循环和函数(下一课进行讲解)。当你在 shell 中执行命令时,您实际上是在执行一段 shell 可以解释执行的简短代码。如果你要求 shell 执行某个指令,但是该指令并不是 shell 所了解的编程关键字,那么它会去咨询环境变量$PATH
,它会列出当 shell 接到某条指令时,进行程序搜索的路径:1
2
3
4
5
6god@god-virtual-machine:~$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
god@god-virtual-machine:~$ which echo
/usr/bin/echo
god@god-virtual-machine:~$ /usr/bin/echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin当我们执行
echo
命令时,shell 了解到需要执行echo
这个程序,随后它便会在$PATH
中搜索由:
所分割的一系列目录,基于名字搜索该程序。当找到该程序时便执行(假定该文件是可执行程序,后续课程将详细讲解)。确定某个程序名代表的是哪个具体的程序,可以使用which
程序。我们也可以绕过$PATH
,通过直接指定需要执行的程序的路径来执行该程序。
★在shell中导航
pwd、cd、ls、which
shell 中的路径是一组被分割的目录,在 Linux 和 macOS 上使用
/
分割,而在Windows上是\
。路径/
代表的是系统的根目录,所有的文件夹都包括在这个路径之下,在Windows上每个盘都有一个根目录(例如:C:\
)。 我们假设您在学习本课程时使用的是 Linux 文件系统。如果某个路径以/
开头,那么它是一个绝对路径,其他的都是相对路径 。绝对路径是指能完全确定文件位置的路径,而相对路径是指相对于当前工作目录的路径。例如,
/usr/bin
就是一个绝对路径,/usr/bin/echo
就能完全确定echo
程序的地址;而god@god-virtual-machine:/$ cd ./usr/bin/
中的./usr/bin/
就是一个相对路径,是相对于当前的工作位置(/
)而言的。为了获得当前工作目录,可以使用
pwd
命令来获取。(pwd -> print working directory)1
2god@god-virtual-machine:~$ pwd
/home/god注意,shell 会实时显示当前的路径信息。您可以通过配置 shell 提示符来显示各种有用的信息,这一内容我们会在后面的课程中进行讨论。
一般来说,当我们运行一个程序时,如果我们没有指定路径,则该程序会在当前目录下执行。例如,我们常常会搜索文件,并在需要时创建文件。
此外,切换目录需要使用
cd
命令。(cd -> change directory)在路径中,
.
表示的是当前目录,而..
表示上级目录。cd -
可以返回进入此目录之前所在目录;cd /
可以进入根目录;cd ~
对于root用户来说,相当于cd /root
;而对于普通用户来说,相当于cd /home/当前用户名
。为了查看指定目录下包含哪些文件,我们使用
ls
命令:(ls -> list)1
2
3
4god@god-virtual-machine:/$ ls
bin dev lib libx32 mnt root snap sys var
boot etc lib32 lost+found opt run srv tmp
cdrom home lib64 media proc sbin swapfile usr除非我们利用第一个参数指定目录,否则
ls
会打印当前目录下的文件。大多数的命令接受标记和选项(带有值的标记),它们以
-
开头,并可以改变程序的行为。通常,在执行程序时使用
-h
或--help
标记可以打印帮助信息,以便了解有哪些可用的标记或选项。例如,ls --help
的输出如下:1
2
3
4
5
6
7
8
9
10
11
12god@god-virtual-machine:/$ ls --help
用法:ls [选项]... [文件]...
列出给定文件(默认为当前目录)的信息。
如果不指定 -cftuvSUX 中任意一个或--sort 选项,则根据字母大小排序。
必选参数对长短选项同时适用。
-a, --all 不隐藏任何以 . 开始的项目
-A, --almost-all 列出除 . 及 .. 以外的任何项目
--author 与 -l 同时使用时,列出每个文件的作者
......
-l 使用较长格式列出信息
......对于
ls -l
来说,1
2
3god@god-virtual-machine:/$ ls -l home/
总用量 4
drwxr-xr-x 15 god god 4096 1月 22 19:38 god这个参数
-l
可以更加详细地列出目录下文件或文件夹的信息。首先,本行第一个字符
d
表示god
是一个目录,若为-
则表示是一个普通文件。然后接下来的九个字符,每三个字符构成一组。 (rwx
) 它们分别代表了文件所有者,用户组以及其他所有人具有的权限。其中-
表示该用户不具备相应的权限。再接下来的内容分别表示当前这个目录下面的一级目录个数(若为目录)(个数包含两个我们看不见的一级目录:.
和..
)或硬链接个数(若为文件)、所有者owner、用户组group、大小(以字节为单位)、时间、名称。从上面的信息来看,只有文件所有者可以修改(
w
)god
文件夹 (例如,添加或删除文件夹中的文件)。该文件夹包含15
个一级目录,所有者为god
、用户组为god
、大小为4096B
,文件夹名称为god
。为了进入某个文件夹,用户需要具备该文件夹以及其父文件夹的“搜索”权限(以“可执行”:
x
)权限表示。为了列出它的包含的内容,用户必须对该文件夹具备读权限(
r
)。r
:可以读文件的内容或者列出文件夹所包含的内容;w
:可以更改文件的内容或者更改文件夹所包含的内容(重命名、创建或删除文件夹的文件);x
:可以执行文件或者进入某个文件夹(同时也应该具备其父文件夹的“搜索”权限)。注意,
/bin
目录下的程序在最后一组,即表示所有人的用户组中,均包含x
权限,也就是说任何人都可以执行这些程序。which
:用于在 PATH 变量指定的路径中,搜索某个系统命令的位置,并且返回第一个搜索结果。1
2god@god-virtual-machine:~/桌面$ which python
/usr/bin/python
mv、rm、cp、mvdir、rmdir、man、clear、chmod、touch
在这个阶段,还有几个趁手的命令是您需要掌握的,如下所示:
mv
:用来为文件或目录改名、或将文件或目录移入其它位置。(mv -> move)1
2god@god-virtual-machine:~/桌面$ mv 1.txt 2.txt
god@god-virtual-machine:~/桌面$ mv ./2.txt ./HIT-Linux-0.11/上面的两个命令分别是:将
1.txt
文件重命名为2.txt
文件;将当前目录下的2.txt
文件移到当前目录下的HIT-Linux-0.11
文件夹中。rm
:用于删除一个文件或者目录。(rm -> remove)1
god@god-virtual-machine:~/桌面$ rm 1.txt
上面的命令用于将当前目录下的
1.txt
文件删除。**注意:若要用
rm
命令删除目录,则必须配合选项 “-r”**(默认时,rm 不会删除目录。使用–recursive(-r 或-R)选项可删除每个给定的目录,以及其下所有的内容。):1
god@god-virtual-machine:~/桌面$ rm -r test/
上面的命令用于将
test
目录及其里面的文件递归删除。cp
:用于复制文件或目录。(cp -> copy)1
god@god-virtual-machine:~/桌面$ cp ./1.txt ./HIT-Linux-0.11/
上面的命令用于将当前目录下的
1.txt
文件复制到当前目录下的HIT-Linux-0.11
文件夹中。mkdir
:用于创建目录。(mkdir -> make directory)1
god@god-virtual-machine:~/桌面$ mkdir ./HIT-Linux-0.11/test
上面的命令用于在当前目录下的
HIT-Linux-0.11
文件夹中创建文件夹test
。rmdir
:用于删除空的目录。(rmdir -> remove directory)1
god@god-virtual-machine:~/桌面$ rmdir test
上面的命令用于删除
test
目录。注意:只有当
test
目录为空时,才能删除。**
man
**:作用类似于--help
,它会接受一个程序名作为参数,然后将它的文档(用户手册)展现给您。注意,使用q
可以退出该程序。(man -> manual)1
god@god-virtual-machine:~/桌面$ man ls
**
clear
**:用于清空终端显示屏。1
god@god-virtual-machine:~/桌面$ clear
注意:
ctrl + l
的效果与这个命令类似。**
chmod
**:改变用户对文件的权限。(chmod -> change mode)只有文件所有者和超级用户可以修改文件或目录的权限。可以使用绝对模式(八进制数字模式)或符号模式指定文件的权限。

1
2
3
4
5
6
7god@god-virtual-machine:/tmp/missing$ ls -l
总用量 4
-rw-rw-r-- 1 god god 61 1月 23 16:18 semester
god@god-virtual-machine:/tmp/missing$ chmod ugo+x semester
god@god-virtual-machine:/tmp/missing$ ls -l
总用量 4
-rwxrwxr-x 1 god god 61 1月 23 16:18 semester上面的命令为 u(user, 文件所有人)、g(group, 用户组)、o(other, 其他所有人)都添加了对
semester
文件的执行权限。下面是
chmod
一些例子:命令 说明 chmod a+r file
给file的所有用户增加读权限,a 等价于 ugo chmod +rwx file
给file的所有用户增加读写执行权限 chmod u=rw,go= file
对file的所有者设置读写权限,清空该用户组和其他用户对file的所有权限(空格代表无权限) chmod 664 file
对file的所有者和用户组设置读写权限, 为其他用户设置读权限 touch
:用于修改文件或者目录的时间属性,包括存取时间和更改时间。若文件不存在,系统会建立一个新的文件。1
god@god-virtual-machine:~/桌面$ touch 1.txt
上面的命令在
~/桌面
目录下新建了一个1.txt
文件。1
2
3
4
5
6
7
8
9
10
11god@god-virtual-machine:~/桌面$ ls -l
总用量 8
-rw-rw-r-- 1 god god 0 1月 23 17:05 1.txt
drwxrwxr-x 12 god god 4096 1月 22 20:24 HIT-Linux-0.11
drwxr-xr-x 9 root root 4096 3月 25 2020 vmware-tools-distrib
god@god-virtual-machine:~/桌面$ touch 1.txt
god@god-virtual-machine:~/桌面$ ls -l
总用量 8
-rw-rw-r-- 1 god god 0 1月 23 17:10 1.txt
drwxrwxr-x 12 god god 4096 1月 22 20:24 HIT-Linux-0.11
drwxr-xr-x 9 root root 4096 3月 25 2020 vmware-tools-distrib当
1.txt
文件存在时,上面的命令将1.txt
文件的时间属性修改为当前的系统时间,即由17:05
修改为17:10
。
★在程序间创建连接
<、>、<<、>>、2>、&>、cat、tail、管道 |
在 shell 中,程序有两个主要的“流”:它们的输入流和输出流。 当程序尝试读取信息时,它们会从输入流中进行读取,当程序打印信息时,它们会将信息输出到输出流中。 通常,一个程序的输入输出流都是您的终端。也就是,您的键盘作为输入,显示器作为输出。 但是,我们也可以重定向这些流!
最简单的重定向是
< file
和> file
。这两个命令可以将程序的输入输出流分别重定向到文件:1
god@god-virtual-machine:~/桌面$ echo hello\ world > hello.txt
上面的命令将 ”hello world“ 输出流重定向到
hello.txt
文件,即:若hello.txt
文件不存在,则创建该文件并向文件中写入 ”hello world“;若hello.txt
文件存在,则将文件中之前的内容覆盖,写入 ”hello world“ 内容。cat
:用于连接文件并将文件内容打印到标准输出设备上。(cat -> concatenate)1
2god@god-virtual-machine:~/桌面$ cat hello.txt
hello world我们也可以用下面的命令使用
cat
:1
2god@god-virtual-machine:~/桌面$ cat < hello.txt
hello world即将
hello.txt
的输入流重定向到cat
,由cat
将hello.txt
的内容打印到标准输出设备上。
如果我们想要在文件的内容后面追加内容而不是覆盖,那我们可以用
>>
来进行重定向:1
2
3
4god@god-virtual-machine:~/桌面$ echo hello >> hello.txt
god@god-virtual-machine:~/桌面$ cat hello.txt
hello world
hello>
:覆盖写入文件>>
:追加写入文件2>
:错误输出写入文件&>
:正确和错误输出统一写入到文件中
★使用管道( pipes ),我们能够更好的利用文件重定向。
|
操作符允许我们将一个程序的输出和另外一个程序的输入连接起来:管道是一种通信机制,通常用于进程间的通信(也可通过socket进行网络通信),它表现出来的形式将前面每一个进程的输出(stdout)直接作为下一个进程的输入(stdin)。1
2
3
4
5
6
7god@god-virtual-machine:~/桌面$ ls -l
总用量 12
-rw-rw-r-- 1 god god 18 1月 23 15:06 hello.txt
drwxrwxr-x 12 god god 4096 1月 22 20:24 HIT-Linux-0.11
drwxr-xr-x 9 root root 4096 3月 25 2020 vmware-tools-distrib
god@god-virtual-machine:~/桌面$ ls -l | tail -n1
drwxr-xr-x 9 root root 4096 3月 25 2020 vmware-tools-distrib在上面这个命令中,将
ls -l
的输出流作为tail
的输入流(用|
来连接),然后-n1
表示只显示ls -l
内容的最后一行。tail
:用于查看文件末尾的内容,通过加入选项-nx
来显示最后 x 行,例如-n1
表示只显示最后一行。1
2god@god-virtual-machine:~/桌面$ tail -n1 hello.txt
hello
★根用户
sudo、tee
对于大多数的类 Unix 系统,有一类用户是非常特殊的,那就是:根用户(root user)。 您应该已经注意到了,在上面的输出结果中,根用户几乎不受任何限制,他可以创建、读取、更新和删除系统中的任何文件。 通常在我们并不会以根用户的身份直接登录系统,因为这样可能会因为某些错误的操作而破坏系统。 取而代之的是我们会在需要的时候使用
sudo
命令。顾名思义,它的作用是让您可以以 su(super user 或 root 的简写)的身份执行一些操作。 当您遇到拒绝访问(permission denied)的错误时,通常是因为此时您必须是根用户才能操作。然而,请再次确认您是真的要执行此操作。sudo
:以系统管理者的身份执行指令,也就是说,经由 sudo 所执行的指令就好像是 root 亲自执行。(sudo -> super user do)
有一件事情是您必须作为根用户才能做的,那就是向系统的配置文件中写入内容。系统被挂载在
/sys
下,/sys
中的文件暴露了一些内核(kernel)参数。 因此,您不需要借助任何专用的工具,就可以轻松地在运行期间配置系统内核。注意:Windows 和 macOS 没有这个文件。
例如,您笔记本电脑的屏幕亮度写在
brightness
文件中,它位于/sys/class/backlight
。通过将数值写入该文件,我们可以改变屏幕的亮度。现在,蹦到您脑袋里的第一个想法可能是:1
2
3
4
5
6$ sudo find -L /sys/class/backlight -maxdepth 2 -name '*brightness*'
/sys/class/backlight/thinkpad_screen/brightness
$ cd /sys/class/backlight/thinkpad_screen
$ sudo echo 3 > brightness
An error occurred while redirecting file 'brightness'
open: Permission denied(虚拟机中没有
brightness
文件,所以用的是官网的示例)出乎意料的是,我们还是得到了一个错误信息。毕竟,我们已经使用了
sudo
命令!关于 shell,有件事我们必须要知道。|
、>
、和<
是通过 shell 执行的,而不是被各个程序单独执行。echo
等程序并不知道|
的存在,它们只知道从自己的输入输出流中进行读写。 对于上面这种情况, shell (权限为您的当前用户) 在设置sudo echo
前尝试打开 brightness 文件并写入,但是系统拒绝了 shell 的操作因为此时 shell 不是根用户。我们可以通过
sudo su
命令从普通用户切换到根用户:1
2
3god@god-virtual-machine:~/桌面$ sudo su
[sudo] god 的密码:
root@god-virtual-machine:/home/god/桌面#此时用户切换到根用户
root
,我们就可以对/sys
目录下的文件进行操作了,此时 shell 就是根用户。**注意:普通用户的提示符是$ —— root用户的提示符是#**。
如果想要切换回普通用户,可以用下面的命令:
1
2root@god-virtual-machine:/home/god/桌面# su god
god@god-virtual-machine:~/桌面$
另外我们也可以通过这样的命令修改
brightness
文件的内容:1
$ echo 3 | sudo tee brightness
因为打开
/sys
文件的是tee
这个程序,并且该程序以root
权限在运行,因此操作可以进行。tee
:用于读取标准输入的数据,并将其内容输出成文件。(tee -> T(T形水管接口))该命令的基本格式如下:
[root@localhost ~]#tee [OPTIONS] [文件...]
-a
:附加到既有文件的后面,而非覆盖它。-i
:忽略中断信号。
然后按回车,之后输入想要在这些文件中添加的内容,最后用
ctrl+d
结束。