Shell中的管道以及exit,众所周知,shell中的exit是退出当前shell的意思,但是某日工作中遇到了下面这种情况,在管道中使用exit。
GNU bash,版本 4.3.11
user@PCxulu:~/debug/test$ cat test.sh
#!/bin/bash
cat test.txt | while read ol
do
echo $ol
exit
done
echo "TTTT"
user@PCxulu:~/debug/test$ cat test.txt
aaaa
bbbb
cccc
user@PCxulu:~/debug/test$ ./test.sh
aaaa
TTTT
根据以上的输出发现,在shell的管道中exit后,并没有退出整个shell脚本,后续的TTTT依旧打印出来了。
原因: pipes会创建subshell,exit在管道中执行,所以只是退出了subshell,并没有退出当前的shell,因此输出了TTTT
我当前的Bash版本为
user@PCxulu:~/debug/test$ bash --version
GNU bash,版本 4.3.11(1)-release (x86_64-pc-linux-gnu)
现在确认一下原因,修改一下test.sh,并测试,结果如下:
user@PCxulu:~/debug/test$ cat test.sh
#!/bin/bash
echo "before PPID -- $PPID"
echo "before BASHPID -- $BASHPID"
echo "before \$\$ -- $$"
cat test.txt | while read ol
do
echo "subshell PPID -- $PPID"
echo "subshell BASHPID -- $BASHPID"
echo "subshell \$\$ -- $$"
echo $ol
exit
done
echo "after PPID -- $PPID"
echo "after BASHPID -- $BASHPID"
echo "after \$\$ -- $$"
echo "TTTT"
user@PCxulu:~/debug/test$ ./test.sh
before PPID -- 13910
before BASHPID -- 16130
before $$ -- 16130
subshell PPID -- 13910
subshell BASHPID -- 16132
subshell $$ -- 16130
aaaa
after PPID -- 13910
after BASHPID -- 16130
after $$ -- 16130
TTTT
查看BASHPID的值,subshell是不一样的。man bash中PPID、BASHPID以及$的解释如下:
BASHPID
Expands to the process ID of the current bash process. This differs from $$ under
certain circumstances, such as subshells that do not require bash to be re-initial‐
ized.
PPID The process ID of the shell's parent. This variable is readonly.
$ Expands to the process ID of the shell. In a () subshell, it expands to the
process ID of the current shell, not the subshell.
OK,原因找到了,如何解决呢,有好几种方法:
1. 针对subshell的返回值
user@PCxulu:~/debug/test$ cat test.sh
#!/bin/bash
cat test.txt | while read ol
do
echo $ol
exit 100
done
if [ $? -eq 100 ]
then
exit
fi
echo "TTTT"
user@PCxulu:~/debug/test$ ./test.sh
aaaa
2. 不使用pipes, 直接将文件test.txt重定向给while,如果想使用命令,可参考下面注释的一行,这就涉及到了Process Substitution的概念,有兴趣可了解下。
user@PCxulu:~/debug/test$ cat ./test.sh
#!/bin/bash
while read ol
do
echo $ol
exit
done < test.txt
#done < <(cat test.txt)
echo "TTTT"
user@PCxulu:~/debug/test$ ./test.sh
aaaa
当然还有其他的方法,这里就不深入下去了。
参考:
http://stackoverflow.com/questions/20725925/get-pid-of-current-subshell
http://stackoverflow.com/questions/20558295/quit-from-pipe-in-bash
http://unix.stackexchange.com/questions/14270/get-exit-status-of-process-thats-piped-to-another
http://tldp.org/LDP/abs/html/process-sub.html
http://stackoverflow.com/questions/18359777/bash-exit-doesnt-exit