常規做法
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信號,這樣就可以讓整個腳本退出了!
