常規做法
cat >test.sh<<EOF''
#!/bin/bash
exit_script(){
exit 1
}
echo "before exit"
exit_script
echo "after exit"
EOF
chmod a+x test.sh
./test.sh
echo $?
# 輸出
before exit
1
可以看到直接使用exit
可以退出腳本,並且可以將錯誤碼作為參數傳遞。下面我們將腳本做一點點改動。
存在的問題
cat >test.sh<<EOF''
#!/bin/bash
exit_script(){
exit 1
}
echo "before exit"
:|exit_script
echo "after exit"
EOF
chmod a+x test.sh
./test.sh
echo $?
# 輸出
before exit
after exit
0
在管道(|)
中執行exit_script
函數,不會退出整個腳本!原因在於,exit
只能退出它所在的Shell
,而放在管道
中執行的命令/函數都是在獨立的Shell(Sub-Shell)
中執行的,所以上面腳本的進程樹是這個樣子的:
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
17510 26959 26959 26959 pts/0 14049 Ss 0 0:00 \_ -bash
26959 13843 13843 26959 pts/0 14049 S 0 0:00 | \_ /bin/bash ./test.sh
13843 13844 13843 26959 pts/0 14049 S 0 0:00 | | \_ :
13843 13845 13843 26959 pts/0 14049 S 0 0:00 | | \_ /bin/bash ./test.sh
13845 13846 13843 26959 pts/0 14049 S 0 0:00 | | \_ exit 1
自上往下,各個PID的含義如下表:
PID | 說明 |
---|---|
26959 | ./test.sh所在的Shell |
13843 | 管道中:新開的Shell |
13844 | :命令 |
13845 | 管道中exit_shell新開的Shell |
13846 | exit命令 |
使用trap
和kill
退出整個腳本
cat >test.sh<<EOF''
#!/bin/bash
export TOP_PID=$$
trap 'exit 1' TERM
exit_script(){
kill -s TERM $TOP_PID
}
echo "before exit"
:|exit_script
echo "after exit"
EOF
chmod a+x test.sh
./test.sh
echo $?
# 輸出
before exit
1
這里首先在腳本的主進程中捕獲(trap)
TERM
信號: 當主進程接收到TERM
信號后,會執行exit 1
;再在Sub-Shell
中向腳本主進程發送TERM
信號,這樣就可以讓整個腳本退出了!