Linux命令行学习笔记:壳层shell
Efficient Linux at the Command Line by Daniel J. Barrett (O’Reilly). Copyright 2022 Daniel Barret, 978-1-098-11340-7.
我们可以在命令提示符处输入命令进行操作,那么什么是命令提示符,我们的命令又是如何运行的呢?
命令提示符由计算机壳层 (Shell)
产生,它是存在于我们和Linux系统之间的用户界面。Linux提供多种Shell,最常见的就是bash
bash等Shell可不仅仅是运行命令,例如我们可以使用包含通配符
(*) 的命令一次性指代多个文件:
1 | ls *.py |
1 | data.py main.py user_interface.py |
通配符是由shell处理的,而非ls程序。
shell也用于处理之前的管道函数,它将stdin与stdout转换,而我们调用的程序并不知道它们是否在互相交流。
文件名的模式匹配 (Pattern Matching or Globbing)
通配符星号 (*) 匹配文件或目录路径中的任何零个或多个字符序列(但不包括开头的点):
1 | grep Linux chapter* |
在上面的情景中,shell(注意不是grep)将chapter*扩展为一百个匹配的文件名列表,随后shell运行grep。
通配符问号 (?)
用于匹配单个字符(但不包括开头的点),例如,要仅匹配chapter1
~ chapter9,可以使用j:
1 | grep Linux chapter? |
或者要匹配chapter10 ~
chapter99,可以使用两个问号匹配两位数字:
1 | grep Linux chapter?? |
通配符方括号 ([])
用于使shell在一个集合中筛选一个字符,例如,要匹配chapter1 ~
chapter5:
1 | grep Linux chapter[12345] |
同理,也可以使用连字符 (-) 用于提供字符的范围:
1 | grep Linux chapter[1-5] |
结合星号和方括号,我们可以匹配偶数章节:
1 | grep Linux chapter*[02468] |
当然,并不只有数字可用于方括号中的匹配,例如:
1 | ls [A-Z]*_*@ |
如果一个模式匹配没有任何文件符合,那么其就会原样返回作为命令参数,例如:
1 | ls *.doc |
1 | ls: 无法访问 '*.doc': 没有那个文件或目录 |
变量评估
运行的shell可以定义并储存变量,例如HOME变量储存量Linux
home文件夹的路径,USER变量储存Linux用户名,例如
使用printenv来打印HOME变量和USER变量的值:
1 | printenv HOME |
1 | /home/developer |
1 | printenv USER |
1 | developer |
在变量名前放置美元符号 ($) ,可以输出变量的值(即evaluating variables):
1 | echo My name is $USER and my files are in $HOME |
1 | My name is developer and my files are in /home/developer |
变量的定义
USER和HOME等变量都是shell预先定义好的,这些变量在我们登陆时就已经定义好了,按照惯例这些变量是全部大写的。
使用
1 | name=value |
的语法,我们可以在任何时候定义或者修改一个变量的值。
例如我们可以定义
1 | work=$HOME/Projects |
注意,定义变量时,等号两边不允许任何空格。
常见误区
当我们使用echo打印变量值时:
1 | echo $HOME |
我们或许会认为是echo命令将HOME的值输出。但实际上,echo仅仅打印我们赋予的任何参数,是shell完成了变量的评估,再把HOME的值赋给echo。
模式匹配 vs 变量
假设我们当前文件夹中有两个子文件夹:mammals和reptiles,而mammals文件夹中含有lizard.txt和snake.txt,我们需要把lizard.txt和snake.txt移动到reptiles文件夹中。
我们有如下两种可能的方法:
方法1:
1 | mv mammals/*.txt reptiles |
方法2:
1 | FILES="lizard.txt snake.txt" |
显然,方法1是奏效的。
1 | echo mammals/*.txt |
1 | mammals/lizard.txt mammals/snake.txt |
因此,方法1相当于输入
1 | mv mammals/lizard.txt mammals/snake.txt reptiles |
对于我们定义的变量FILES:
1 | echo $FILES |
1 | lizard.txt snake.txt |
因此,方法1相当于输入
1 | mv mammals/lizard.txt snake.txt reptiles |
该命令会在当前目录下查找snake.txt并报错:
1 | mv mammals/$FILES reptiles |
1 | mv: 对 'snake.txt' 调用 stat 失败: 没有那个文件或目录 |
为了使用FILES变量,我们应该使用for循环:
1 | FILES="lizard.txt snake.txt" |
使用别名缩短命令
变量代表一个值,shell也用名称代表命令,称为alias,例如:
1 | alias g=grep |
我们可以使用已有函数的名称作为别名,这样就在我们的shell中替换了这个函数。
使用不带参数的alias命令可以展示shell中的别名和它们的值:
1 | alias |
1 | alias g='grep' |
使用unalias可以从shell中删除别名:
1 | unalias g |
输入/输出重定向
shell可以控制其上命令的输入与输出,例如管道 ( | ) ,可以将一个命令的stdout重定向至另一个命令的stdin。
另外一个shell特性是可以将stdout输出至一个文件,例如,将animals.txt中含Perl的行输出至文件,只需在其后添加>,再写上要输出的文件名:
1 | grep Perl animals.txt > outfile |
如果outfile不存在,将新建一个,若存在,将对其覆写。如果要追加,则应使用>>。
与输出重定向相对的是输入重定向,使从文件中获取输入而非stdin,使用<。
很多Linux命令不仅接受文件名作为参数,也接受直接的stdin,例如wc命令:
从命名的文件读取:
1 | wc animals.txt |
1 | 7 51 325 animals.txt |
从重定向的输入读取:
1 | wc < animals.txt |
1 | 7 51 325 |
注意:
一些错误信息 (stderr) ,不能作为stdout使用
>重定向。若要将stderr和stdout都重定向,应使用
&<。
可以将不同形式的重定向结合起来,例如:
1 | grep Perl < animals.txt | wc > count |
1 | 1 6 47 |
使用引号和转义字符禁用求值
通常,shell将空格作为词语间的分隔符,但如果我们需要shell”认真“对待空格,例如,我们有一文件,名为Efficient Linux Tips.txt,我们就必须使用单引号、双引号或反斜线:
1 | cat 'Efficient Linux Tips.txt' |
又如,使用
1 | echo '$HOME' |
即可输出
1 | $HOME |
在双引号间,反斜线可作为转移字符,但在单引号间则不能。句末的反斜线使原有的换行符失效,从而使命令可以在不同的行间。
在命令别名前的反斜线使shell寻找同名的命令,忽略覆盖:
1 | alias less="less -c" |
要运行的标准的less命令,可以:
1 | \less myfile |
定位要运行的程序
我们常见的ls命令其实是硬盘上的一个可执行文件,在/bin中。
1 | ls -l /bin/ls |
1 | -rwxr-xr-x 1 root root 138216 2月 8 2024 /bin/ls |
那么shell是如何确定ls在/bin中的呢?事实上,shell在一系列预先确定的目录(称为搜索路径,search
path)中查找。该列表储存在变量PATH中:
1 | echo $PATH |
1 | /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin |
搜索路径中目录以:分隔,使用tr命令,将:转变为换行符以获得更清晰的视图:
1 | echo $PATH | tr : "\n" |
1 | /usr/local/sbin |
要在搜索路径中定位一个程序,可以使用which命令:
1 | which cp |
1 | /usr/bin/cp |
1 | which which |
1 | /usr/bin/which |
搜索路径中可能存在相同名称的命令,shell运行的是在搜索路径中考前的程序。
注意:
shell会先确定命令名称是否已经被设置为别名,再在搜索路径中搜索。
环境和初始化文件
正在运行的shell在变量中保存着大量重要信息:搜索路径、当前目录、偏好的文本编辑器、自定义的shell提示符等。正在运行的shell的变量合称为shell的环境。当shell退出时,其环境会被销毁。
我们可以在初始化文件$HOME/.bashrc中预先定义环境,例如:
1 | # Set the search path |
\(HOME/.bashrc*中的改变将不影响正在运行的shell,只作用于今后运行的shell。但我们可以强制使正在运行的shell重新读取*\)HOME/.bashrc:
1 | source $HOME/.bashrc |
或
1 | . $HOME/.bashrc |
