shell, subshell, pipes and exit

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




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值