由空格分隔,由control operators(换行符, ‘||’, ‘&&’, ‘&’, ‘;’, ‘;;’, ‘;&’, ‘;;&’, ‘|’, ‘|&’, ‘(’, or ‘)’)终止的单词序列,构成了shell的简单命令(Simple Command)。
通常,第一个单词指定要执行的命令,其余单词是该命令的参数。
control operators将命令进行组合,产生pipeline和list
首先,一个独立的简单命令,都是pipeline
command # 这也是pipeline
由 | 或 |& 分隔的命令序列,也是pipeline
command1 [ | or |& command2 ] ... # 这还是是pipeline
pipeline中每个命令的输出通过管道连接到下一个命令的输入。 也就是说,每个命令都会读取前一个命令的输出。
$ ls -al /proc/self/fd|cat
total 0
dr-x------ 2 felix felix 0 May 31 07:01 .
dr-xr-xr-x 7 felix felix 0 May 31 07:01 ..
lrwx------ 1 felix felix 0 May 31 07:01 0 -> /dev/tty1
l-wx------ 1 felix felix 0 May 31 07:01 1 -> pipe:[289] # 标准输出fd 1连接到了管道
lrwx------ 1 felix felix 0 May 31 07:01 2 -> /dev/tty1
lr-x------ 1 felix felix 0 May 31 07:01 3 -> /proc/239/fd
$ : fakecomand|ls -al /proc/self/fd
total 0
dr-x------ 2 felix felix 0 May 31 07:03 .
dr-xr-xr-x 7 felix felix 0 May 31 07:03 ..
lr-x------ 1 felix felix 0 May 31 07:03 0 -> 'pipe:[295]' # 标准输入fd 0连接到了管道
lrwx------ 1 felix felix 0 May 31 07:03 1 -> /dev/tty1
lrwx------ 1 felix felix 0 May 31 07:03 2 -> /dev/tty1
lr-x------ 1 felix felix 0 May 31 07:03 3 -> /proc/242/fd
$ ls -al /proc/self/fd|&cat
total 0
dr-x------ 2 felix felix 0 May 31 07:05 .
dr-xr-xr-x 7 felix felix 0 May 31 07:05 ..
lrwx------ 1 felix felix 0 May 31 07:05 0 -> /dev/tty1
l-wx------ 1 felix felix 0 May 31 07:05 1 -> pipe:[302]
l-wx------ 1 felix felix 0 May 31 07:05 2 -> pipe:[302]
lr-x------ 1 felix felix 0 May 31 07:05 3 -> /proc/243/fd
$ : fakecomand|&ls -al /proc/self/fd
total 0
dr-x------ 2 felix felix 0 May 31 07:05 .
dr-xr-xr-x 7 felix felix 0 May 31 07:05 ..
lr-x------ 1 felix felix 0 May 31 07:05 0 -> 'pipe:[308]'
lrwx------ 1 felix felix 0 May 31 07:05 1 -> /dev/tty1
lrwx------ 1 felix felix 0 May 31 07:05 2 -> /dev/tty1
lr-x------ 1 felix felix 0 May 31 07:05 3 -> /proc/246/fd
如果使用|&,则command1的standard output和standard error,都将通过管道连接到command2的standard input。 它是2>&1 |的简写.
如果pipeline不是异步执行,Shell将等待pipeline中的所有命令完成。
pipeline中的每个命令都在其自己的subshell中执行,该subshell是一个单独的进程。
$ ps -ef|grep 'bash'
felix 6 5 0 21:25 tty1 00:00:00 -bash
felix 77 6 0 21:35 tty1 00:00:00 grep --color=auto bash
$ ps -ef|grep 'ps'
felix 78 6 0 21:35 tty1 00:00:00 ps -ef
felix 79 6 0 21:35 tty1 00:00:00 grep --color=auto ps
$ ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 21:25 ? 00:00:00 /init
root 5 1 0 21:25 tty1 00:00:00 /init
felix 6 5 0 21:25 tty1 00:00:00 -bash
felix 80 6 0 21:36 tty1 00:00:00 ps -ef
pipeline的exit status是最后一条命令的exit status. shell等待pipeline中的所有命令终止后才返回exist status.
其实,完整的pipeline格式可以是:
[time [-p]] [!] command1 [ | or |& command2 ] …
time在这里是保留字(reserved word),在pipeline完成后打印时间统计信息包括消耗的时间、命令执行所消耗的用户和系统时间。-p选项将输出格式更改为POSIX指定的格式。Bash变量TIMEFORMAT可以为时间设置格式字符串。如果没有设置该变量,Bash默认使用如下格式:
$'\nreal\t%3lR\nuser\t%3lU\nsys\t%3lS'
$ time ps -ef | grep 'ef'
felix 90 6 0 21:43 tty1 00:00:00 ps -ef
felix 91 6 0 21:43 tty1 00:00:00 grep --color=auto ef
real 0m0.037s
user 0m0.000s
sys 0m0.047s
保留字!将pipeline的退出状态逻辑取反
pipeline中每个命令的输出通过管道连接到下一个命令的输入,这种连接,要早于redirection. 所以redirection会覆盖管道的连接
例如
$ : fakecomand|&ls -al /proc/self/fd <foo
total 0
dr-x------ 2 felix felix 0 May 31 07:07 .
dr-xr-xr-x 7 felix felix 0 May 31 07:07 ..
lr-x------ 1 felix felix 0 May 31 07:07 0 -> /home/felix/foo # fd0 被redirection覆盖
lrwx------ 1 felix felix 0 May 31 07:07 1 -> /dev/tty1
lrwx------ 1 felix felix 0 May 31 07:07 2 -> /dev/tty1
lr-x------ 1 felix felix 0 May 31 07:07 3 -> /proc/248/fd
在上面的例子里,先建立了pipe,但第二个命令的ls的fd0被redirection修改为文件。此时,建立的pipe其实是broken的。
command1|command2
command1和command2是同时执行的,虽然command1的PID通常比command2的要小,但是两个命令分别在自己的process里运行。两个平行的process通过pipe来同步,即,command2如果要从pipe里读,那么必须要等command1往pipe里写。如果command2的执行根本就不依赖于从pipe里读,那么command1和command2的执行进度就没有关系,也就无法确定谁先执行结束。这与顺序执行命令里介绍的“串行执行”结构不同:
command1;command2 # Bash一定会等待command1执行完成后,再执行command2