shell set命令使用詳解
文章目錄
1. set
會顯示所有的環境變數和 Shell 函式
$ cat script.sh
set
a=1
b=2
c=3
echo $a
echo $b
echo $c
echo $d
$ script.sh
bash script.sh
BASH=/bin/bash
BASHOPTS=cmdhist:complete_fullquote:extquote:force_fignore:hostcomplete:interactive_comments:progcomp:promptvars:sourcepath
BASH_ALIASES= ()
BASH_ARGC=()
BASH_ARGV=()
BASH_CMDS=()
BASH_LINENO=([0]="0")
BASH_SOURCE=([0]="script.sh")
BASH_VERSINFO=([0]="4" [1]="3" [2]="48" [3]="1" [4]="release" [5]="x86_64-pc-linux-gnu")
BASH_VERSION='4.3.48(1)-release'
2. set -u
遇到不存在的變數就會報錯,並停止執行,或者-o nounset
$ cat script.sh
set -u #-o nounset
a=1
b=2
c=3
echo $a
echo $b
echo $c
echo $d
$ bash script.sh
1
2
3
script.sh: line 9: d: unbound variable
3. set -x
用來在執行結果之前,先輸出執行的那一行命令,或者set -o xtrace
$ cat script.sh
set -x #set -o xtrace
a=1
b=2
c=3
echo $a
echo $b
echo $c
echo $d
執行結果:
$ bash script.sh
+ a=1
+ b=2
+ c=3
+ echo 1
1
+ echo 2
2
+ echo 3
3
+ echo
4. bash 的錯誤處理
#!/usr/bin/env bash
foo
echo bar
上面指令碼中,foo是一個不存在的命令,執行時會報錯。但是,Bash 會忽略這個錯誤,繼續往下執行。
$ bash script.sh
script.sh:行3: foo: 未找到命令
bar
可以看到,Bash 只是顯示有錯誤,並沒有終止執行。
這種行為很不利於指令碼安全和除錯。實際開發中,如果某個命令失敗,往往需要指令碼停止執行,防止錯誤累積。這時,一般採用下面的寫法。
command || exit 1
上面的寫法表示只要command有非零返回值,指令碼就會停止執行。
如果停止執行之前需要完成多個操作,就要採用下面三種寫法。
# 寫法一
command || { echo "command failed"; exit 1; }
# 寫法二
if ! command; then echo "command failed"; exit 1; fi
# 寫法三
command
if [ "$?" -ne 0 ]; then echo "command failed"; exit 1; fi
另外,除了停止執行,還有一種情況。如果兩個命令有繼承關係,只有第一個命令成功了,才能繼續執行第二個命令,那麼就要採用下面的寫法。
command1 && command2
5. set -e
set -e從根本上解決了這個問題,它使得指令碼只要發生錯誤,就終止執行。或者set -o errexit
#!/usr/bin/env bash
set -e
foo
echo bar
執行結果如下。
$ bash script.sh
script.sh:行4: foo: 未找到命令
可以看到,第4行執行失敗以後,指令碼就終止執行了。
set -e根據返回值來判斷,一個命令是否執行失敗。但是,某些命令的非零返回值可能不表示失敗,或者開發者希望在命令失敗的情況下,指令碼繼續執行下去。這時可以暫時關閉set -e,該命令執行結束後,再重新開啟set -e。
set +e
command1
command2
set -e
上面程式碼中,set +e表示關閉-e選項,set -e表示重新開啟-e選項。
還有一種方法是使用command || true,使得該命令即使執行失敗,指令碼也不會終止執行。
#!/bin/bash
set -e
foo || true
echo bar
6. set -o pipefail
set -e有一個例外情況,就是不適用於管道命令。
所謂管道命令,就是多個子命令通過管道運算子(|)組合成為一個大的命令。Bash 會把最後一個子命令的返回值,作為整個命令的返回值。也就是說,只要最後一個子命令不失敗,管道命令總是會執行成功,因此它後面命令依然會執行,set -e就失效了。
請看下面這個例子。
#!/usr/bin/env bash
set -e
foo | echo a
echo bar
執行結果如下。
$ bash script.sh
a
script.sh:行4: foo: 未找到命令
bar
上面程式碼中,foo是一個不存在的命令,但是foo | echo a這個管道命令會執行成功,導致後面的echo bar會繼續執行。
set -o pipefail用來解決這種情況,只要一個子命令失敗,整個管道命令就失敗,指令碼就會終止執行。
#!/usr/bin/env bash
set -eo pipefail
foo | echo a
echo bar
執行後,結果如下。
$ bash script.sh
a
script.sh:行4: foo: 未找到命令