Shell 管道及执行顺序分析

2019-09-23 09:50:52刘景俊

### 建立FD4,到时用作保存ls的返回值(echo $?),你可以将FD4看作你考试时用于存放计算“echo $?”的草稿纸;

(ls you no 2>&1 1>&3 3>&-;echo $? >&4)
### 大家还记得前面说的子shell和管道吧。这条命令首先会继承FD0、FD1、FD2、FD3、FD4,它位于管道前,所以在运行命令前会先把子shell自己的FD1和管道“|”相连。
但是我们的条件是stderr通过管道送往egrep,stdout仍然输出到monitor。
于是通过2>&1,先把 子shell的FD1 的管道“送给”FD2,于是子shell中的stderr送往管道“|”;
再通过 1>&3,把以前的“硬盘备份”恢复给子shell的FD1,于是子shell中的FD1变成送到monitor了。
再通过3>&- ,将3关闭;
接着运行echo $? ,本来其输出值应该送往管道的,通过 >&4 ,将 输出 送往 “草稿纸”FD4,留以备用。

((ls you no 2>&1 1>&3 3>&-;echo $? >&4)|egrep * >file)
于是,stderr 通过管道送给 egrep ,stdout 送给monitor,但是,还有 FD4,它送到哪去了?
$(((ls you no 2>&1 1>&3 3>&-;echo $? >&4)|egrep * >file) 4>&1)
最后的 4>&1 ,就是把FD4 重定向到 FD1。但由于其输出在 $( )中,其值就赋给变量ss了。

最后一行关闭 FD3、FD4。

6、高阶例子
lightspeed 版主大大的:Shell 经典问题之 [ I/O 重定向] (http://bbs.chinaunix.net/forum/viewtopic.php?t=452079&show_type=new)
[Q] 对于命令 cmd1, cmd2, cmd3, cmd4. 如何利用单向管道完成下列功能:
1. 所有命令并行执行
2. cmd1 和 cmd2 不需要 stdin
3. cmd1 和 cmd2 的 stdout 定向到 cmd3 的 stdin
4. cmd1 和 cmd2 的 stderr 定向到 cmd4 的 stdin
5. cmd3 的 stdout 定向到文件 a, stderr 定向到屏幕
6. cmd4 的 stdout 定向到文件 b, stderr 定向到屏幕
7. cmd1 的返回码赋给变量 s
8. 不能利用临时文件

解决方法:
exec 3>&1; exec 4>&1
my_value=$(((((cmd1 1>&3 ; echo $? >&4 )| cmd2 ) 3>&1 | cmd3 >a 2>&3 ) 2>&1 | cmd4 >b ) 4>&1)
exec 3>&-; exec 4>&-

解释:
exec 3>&1; exec 4>&1
建立FD3 ,给cmd1恢复其FD1用和给cmd3 恢复其FD2用;
建立FD4,保存“echo $?”输出值的“草稿纸”

第一对括号到第一个管道:(cmd1 1>&3 ; echo $? >&4 )|
cmd1本身没有stdin,其stdout原要送往第一个管道,由于1>&3的作用,其stdout被送往FD3;而 >&4 的作用实际是将 cmd1 运行后的返回码送往 FD4。cmd1的stderr默认等待下一步处理。最后,没有往管道送任何东西;

第二对括号到第二个管道:((cmd1 1>&3 ; echo $? >&4 )| cmd2) 3>&1|
由于第一个括号中cmd1的 stdout 被送往 FD3,导致管道左端没有任何输入,cmd2 从而就没有stdin。cmd2 的stdout则为默认的;
将第二对括号看出一个命令,其所有的stdout送往第二个管道“|”;同时由于3>&1的作用,原先cmd1的stdout在送往FD3 又与cmd2的stdout并到一起,所以cmd1 和 cmd2 的 stdout 都送往第二个管道“|”。而cmd1、cmd2的stderr仍然默认等待下一步处理;