Shell 元字符¶
在 Shell 中,许多非字母、数字字符都拥有特殊的含义,这些字符被称为元字符。其中有几个元字符用于文件名扩展,也称为通配符。先来了解一下 Shell 中的元字符都有那些。
字符 | 含义 |
---|---|
~ | 通配符:home 目录 |
? | 通配符:匹配任意一个字符 |
* | 通配符:匹配 0 个或多个字符 |
[] | 通配符:匹配一组字符中的任意一个字符 |
{} | 通配符:匹配一组字符中的任意一个字符串或识别变量的边界 |
; | 在同一行中分隔多条命令 |
# | 注释 |
& | 在后台运行命令 |
$ | 引用变量 |
\ | 转义符 |
' | 取消所有替换(强引用) |
" | 取消部分替换(弱引用) |
` | 命令替换 |
! | 历史列表:事件标记 |
< | 重定向输入 |
> | 重定向输出 |
| | 管道 |
() | 在子 Shell 中运行命令 |
通配符¶
通配符实际上是一种 Shell 实现的路径扩展功能。当命令中包含通配符时,Shell 会先解释通配符的意义,然后在将命令重组,最后运行该命令。
字符 | 含义 | 实例 |
---|---|---|
* | 匹配 0 或多个字符 | a*b 匹配 aabcb、axyzb、a012b、ab ... |
? | 匹配任意一个字符 | a?b 匹配 aab、abb、acb、a0b ... |
[list] | 匹配 list 中的任意单一字符 | a[xyz]b 匹配 axb、ayb、azb |
[!list] | 匹配除 list 中的任意单一字符 | a[!0-9]b 匹配 axb、aab、a-b ... |
[a-f] | 匹配 a~f 中的任意单一字符 | a[a-f]b 匹配 aab、abb、acb ... |
{abc, ...} | 匹配大括号内的任意一个字符串 | a{abc,xyz,123}b 匹配 aabcb、axyzb、a123b |
提示
通配符看起来有点像正则表达式语句,但是它与正则表达式不同,不能相互混淆。把通配符理解为
Shell 特殊字符就行。而且涉及的只有 * ? [] {}
这几种。
在使用 []
和 {}
时,字符中间不能有空格。
通配符可以用在所有的 Shell 命令中,组合起来可以起到意想不到的作用。
# 一次性创建多个文件
[Linux]$ touch number{1,2,3,4}.txt
[Linux]$ ls
number1.txt number2.txt number3.txt number4.txt
[Linux]$ touch number{1..9}.txt
[Linux]$ ls
number1.txt number3.txt number5.txt number7.txt number9.txt
number2.txt number4.txt number6.txt number8.txt
# 在使用命令前可以先使用 echo 命令验证结果
[Linux]$ echo {10..23}
10 11 12 13 14 15 16 17 18 19 20 21 22 23
[Linux]$ echo {0..3}{Z..X}
0Z 0Y 0X 1Z 1Y 1X 2Z 2Y 2X 3Z 3Y 3X
[Linux]$ echo a{A{1,2},B{3,4}}b
aA1b aA2b aB3b aB4b
命令分隔符¶
在大多数情况下,一条命令行只需输入一条命令。可以用 ;
将两条命令连接起来,从而在一条命令行中输入多个命令。在输入时 ;
两边可以不加空格也可以加空格,为了方便阅读可以在 ;
后边加入一个空格。
[Linux]$ date; cal
Wed 19 Aug 2020 09:44:55 PM CST
August 2020
Su Mo Tu We Th Fr Sa
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31
[Linux]$ whoami; pwd; date
glenn
/home/glenn/Public
Wed 19 Aug 2020 09:48:32 PM CST
提示
还有两个特殊的命令分隔符,即 &&
和 ||
,也叫做条件执行。 &&
前的命令执行成功后会继续执行后面的命令,俗称和命令。 ||
前的命令执行失败后才会执行后面的命令,俗称或命令。
如果想在命令没有运行成功时,追加一条警告信息,可以使用以下命令:
[Linux]# update || echo 'The update program failed.'
注释¶
注释在命令行中基本不会用到,主要应用在脚本中。注释可以出现在脚本的任意位置,在每一行中 #
字符之后的内容都会被注释掉(在脚本执行时,会忽略所有的注释)。
# 以下两种注释相等,一般注释会单独占用一行
# 显示时间及内核
[Linux]$ date; uname
Wed 19 Aug 2020 09:58:54 PM CST
Linux
[Linux]$ date; uname # 显示时间及内核
Wed 19 Aug 2020 09:58:54 PM CST
Linux
后台运行程序¶
在单独的 Shell 程序中,也有前后台之分。前台即实时显示的内容,后台类似于图形界面中最小化的程序。将程序(或脚本)放入后台有两种方法:
元字符 & 将程序放入后台执行¶
在输入命令时在后边加入 &
符号会把命令程序直接放到后台执行,此时可以用 jobs 命令 查看后台执行程序的列表。
# 将 tar 命令放入后台执行
[Linux]$ tar -cvf web.tar /var/www/html/ &
注意
在后台运行命令时,有输出的命令(如:ping)一样会将结果输出到屏幕,所以最好将输出重定向到某个文件(如: ping www.baidu.com >out.file 2>&1
)。
使用 <Ctrl+Z> 快捷键暂停程序运行¶
使用 <Ctrl+Z> 快捷键放入后台的命令处于暂停状态,是不会运行的。
[Linux]$ top
# 使用 <Ctrl+Z> 快捷键可以将 top 放入后台
[Linux]$ jobs
[1]- Stopped vi
[2]+ Stopped top
提示
bg 命令和 fg 命令
在后台暂停的命令,可以使用 bg 命令让程序在后台继续执行。格式如下:
[Linux]$ bg %1
而 fg 命令用于把后台工作恢复到前台执行,格式如下:
[Linux]$ fg %1
在使用 bg 和 fg 命令时, %1
为后台的编号( %
可以省略)。当命令不带参数执行时会对应带有 +
号的后台程序。
引用变量¶
使用一个定义过的变量,只要在变量名前面加 $
符号即可,如:
[Linux]$ your_name=glenn
[Linux]$ echo $your_name
glenn
[Linux]$ echo ${your_name}
glenn
变量名外面的花括号是可选的,加不加都行。加花括号是为了帮助解释器识别变量的边界,比如下面这种情况:
[Linux]$ name=Java
[Linux]$ echo ${name}Script
如果不给 name 变量加花括号,写成 echo $nameScript ,解释器就会把 $nameScript 当成一个变量(其值为空),代码执行结果就不是我们期望的样子了。
推荐给所有变量加上花括号,这是个好的编程习惯。
已定义的变量,可以被重新赋值,如:
[Linux]$ your_name=glenn
[Linux]$ echo $your_name
glenn
[Linux]$ your_name=rose
[Linux]$ echo $your_name
rose
注意
给变量赋值时不能写成 $your_name=rose
,只有使用变量的时候才加 $
符。
转义符¶
有时候,可能希望按字面上的含义使用元字符,而不使用其特殊含义。例如,将分号作为分号使用,而不是一个命令分隔符。在这种情况下就需要用转义符去转义元字符。 Shell 中有三种转义符。
字符 | 说明 |
---|---|
\(反斜杠) | 转义符,去除紧跟其后的元字符的特殊意义 |
'(单引号) | 强引用,所有的元字符都使用其字面含义。强引用中不允许再次出现单引号 |
"(双引号) | 弱引用,只保留 $ 、 ` 和 \ 三种元字符的特殊含义 |
# 转义符转义
[Linux]$ echo It is warm and sunny\; come over and visit
It is warm and sunny; come over and visit
# 中间有空格的文件名使用引用
[Linux]$ cd 'your name'
[Linux]$ cd "your name"
提示
- 使用转义符转义单个字符
- 使用强引用引用字符串
- 使用弱引用引用字符串,保留
$
、`
和\
三种元字符的特殊含义。
转义字符有强弱之分, \
大于 '
大于 "
。
命令替换¶
命令替换允许在一条命令中嵌入另一条命令,Shell 首先执行嵌入的命令,并且用输出替换该命令,然后在执行整条命令。通过将一条命令封装在 `
中,可以将它嵌入另一条命令。
[Linux]$ echo "The time and date are `date`"
The time and date are Fri 21 Aug 2020 09:29:52 PM CST
提示
命令替换还有另外一种格式,即将命令放入 $()
中, $(command)
等同于 `command`
。注意区分 ${}
是引用变量。
历史列表¶
Shell 通过使用 !
字符,为历史列表提供了一个特殊的扩展功能。例如用 !24
重新执行历史列表中事件编号为 24 的命令。
Shell 中还有几个好用的历史列表命令:
字符 | 说明 |
---|---|
!! | 执行上一条命令(等同于 <Up> + <Enter> 键) |
!number | 执行历史列表中第 number 行的命令 |
!string | 执行最近的以 string 字符串开头的命令 |
!?string | 执行最近的包含这个字符串的命令 |
!* | 使用上一条命令的选项和参数 |
!$ | 使用上一条命令的最后一个参数 |
提示
应该谨慎地使用 !string
和 !?string
格式,除非你完全确信历史列表条目的内容。
[Linux]$ ls -l Music/
total 0
[Linux]$ !!
ls -l Music/
total 0
[Linux]$ ls !*
ls -l Music/
total 0
[Linux]$ ls !$
ls Music/
重定向¶
在 Shell 中,标准输入/标准输出的概念很好理解。默认情况下,大多数程序从键盘读取输入,并将输出写入到屏幕。标准输入(stdin)默认为键盘输入;标准输出(stdout)默认为屏幕输出;标准错误输出(stderr)默认也是输出到屏幕。
在 Linux 进程中,每个输入源和每个输出目标都会有一个唯一的数字标识,这个数字称为文件描述符。例如,一个进程可以从文件 #8 中读取数据,并将数据写入到文件 #6 中。默认情况下,Linux 为每个进程提供 3 个预定义的文件描述符,即 0
代表标准输入, 1
代表标准输出, 2
代表标准错误。
类型 | 描述符 | 默认值 | 系统文件 |
---|---|---|---|
标准输入(standard input) | 0 | 从键盘获取 | /proc/self/fd/0 |
标准输出(standard output) | 1 | 输出到屏幕 | /proc/self/fd/1 |
错误输出(error output) | 2 | 输出到屏幕 | /proc/self/fd/2 |
在 Shell 中也可以改变默认的标准输入、标准输出或错误输出,来实现输入输出的重定向。比如将标准输出指向文件时,那么标准的输出就会保存到文件中。
# 重定向输出,用标准输出替换文件中的内容
[Linux]$ date > date.txt
[Linux]$ cat date.txt
Sat 22 Aug 2020 09:29:27 PM CST
# 重定向输出,将标准输出的内容追加到文件中
[Linux]$ cal >> date.txt
[Linux]$ cat date.txt
Sat 22 Aug 2020 09:29:27 PM CST
August 2020
Su Mo Tu We Th Fr Sa
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31
提示
在重定向输出时(也包括下边的重定向标准错误),如果指定的文件不存在则会新建文件。一定要小心区分替换和追加的区别,不要丢失了重要的数据。
>
替换文件中的数据>>
将输出追加到文件的末尾
Shell 中定义了两种不同的输出目标:标准输出和错误输出,标准输出用于正常输出,错误输出用于错误消息输出。重定向标准错误时,需要加入错误输出文件描述符(即数字 2
),当重定向错误输出时,不会影响标准输入和标准输出。
[Linux]$ ls a.txt b
ls: cannot access 'b': No such file or directory
a.txt
[Linux]$ ls a.txt b 2> error
a.txt
[Linux]$ cat error
ls: cannot access 'b': No such file or directory
# 分开定义标准输出和错误输出
[Linux]$ ls a.txt b > out 2> error
# 将错误输出追加到文件中
[Linux]$ ls a.txt b 2>> error
如果想将标准输出和错误输出重定向到同一个位置,可以先重定向标准输出,然后在将错误输出指定到标准输出中。
[Linux]$ ls a.txt b > output 2>&1
除了重定向输出,还可以用 <
重定向输入。甚至同时指定重定向输入和输出。
[Linux]$ cat a.txt
a
c
d
b
# 重定向输入
[Linux]$ sort < a.txt
a
b
c
d
# 同时指定重定向输入和输出
[Linux]$ sort < a.txt > b.txt
[Linux]$ cat b.txt
a
b
c
d
提示
在 Linux 中,有一个特殊的设备文件,即 /dev/null
,它会丢弃一切写入其中的数据(但会反馈写入操作成功)。
在程序员行话中, 将 /dev/null
称为位桶(bit bucket)或黑洞(black hole),经常被用于丢弃不需要的输出流,或作为输入流的空文件。
如果不希望看到命令的错误信息,可以将错误输出重定向到 /dev/null
中。同时可以使用 cat /dev/null > a.txt
清空一个文件中的内容。
管道¶
在 Linux 设计原则里,每个命令都是一个小工具,每个工具只出色的完成一件事情。当靠一个工具无法解决问题时,能够使用一组命令来完成任务。Shell 允许创建一序列命令,将一个命令的标准输出发送到下一个命令的标准输入,两个命令之间需要用 |
符号连接,这时,一序列命令称为管道线(pipeline), |
符号称为管道(pipe)。
# 将文件 a 和文件 b 的内容发送到 less 命令中读取
[Linux]$ cat a.txt b.txt | less
上边的命令中,less 只处理 cat 的正确输出结果,如果文件 b.txt 不存在,则只会显示 a.txt 文件的内容。可以将 cat 命令的标准输出和错误输出一起发送给 less 命令,命令为 cat a.txt b.txt 2>&1 | less
。
提示
可以将管道想象成真实的水管,在污水处理中,污水(输出)从一端进入,经过一层过滤(命令)之后流向另一层再去过滤,直到污水被净化干净。
有时候,可能希望将程序的输出同时发送到两个地方。例如,希望将一个输出即保存在文件中,同时还发送到另一个程序。这时可以使用 tee
命令,tee
命令会从标准输入读取数据,将其内容输出到标准输出,同时保存成文件。命令语法为:
command 1 | tee file | command2
推荐阅读: tee 输出分流
危险
乍看起来,管道也有重定向的作用,它也改变了数据输入输出的方向,那么,管道和重定向之间到底有什么不同呢?
简单地说,重定向将命令与文件连接起来,用文件来接收命令的输出;而管道将命令与命令连接起来,用第二个命令来接收第一个命令的输出。来看一个特殊的例子:
# 注意这里是 root 用户
[Linux]# cd /usr/bin
[Linux]# ls > less
第一条命令将当前目录切换到了大多数程序所存放的目录,第二条命令是告诉 Shell 用 ls 命令的输出覆盖文件 less 中的内容。因为 /usr/bin 目录已经包含了 less(程序)文件,所以重定向会破坏西系统中的 less 程序。
这是使用重定向错误重写文件的一个教训,所以在使用时要谨慎。
在子 Shell 运行命令¶
当在 Shell 中执行命令时,首先 Shell 会判断这条命令是内部命令(Shell 中内置的命令)还是外部命令(单独的程序)。如果是内部命令就直接解释命令,如果是外部命令会创建一个 Shell 副本进程(即子 Shell)运行这个程序。当程序终止时,会重新将控制权交还给原 Shell (即父 Shell),并等待输入另一条命令。
将命令用小括号括起来执行,括号中的命令将会新开一个子 Shell 顺序执行,可以在括号中用 ;
组合多条命令,圆括号中的命令被称为一个编组。
[Linux]$ (cd ./file; ./a.py)
提示
当用 ssh 远程登陆主机执行,因为所有的命令都是 ssh 进程的子进程,所以当 ssh 断开连接时,所有的命令都会被杀死。如果想在断开连接时,命令依然在后台执行,可以将命令放入 ()
中执行。
[Linux]$ (ping www.baidu.com > /dev/null &)
[Linux]$ ps -ef | grep ping
glenn 22202 1 0 21:22 pts/0 00:00:00 ping www.baidu.com
glenn 22204 21736 0 21:22 pts/0 00:00:00 grep ping
可以看到进程的父 ID 是 init 而不是当前终端的进程 ID,因而关闭 ssh 连接后无任何影响。