天天看點

shell之set 指令

我們知道,Bash 執行腳本的時候,會建立一個新的 Shell。

$ bash script.sh

上面代碼中,script.sh是在一個新的 Shell 裡面執行。這個 Shell 就是腳本的執行環境,Bash 預設給定了這個環境的各種參數。

set指令用來修改 Shell 環境的運作參數,也就是可以定制環境。一共有十幾個參數可以定制,​​官方手冊​​有完整清單,本文介紹其中最常用的四個。

1)set指令行下不帶任何參數,直接運作set:

會顯示所有的環境變量和 Shell 函數

$ set
BASH=/bin/bash
BASHOPTS=checkwinsize:cmdhist:expand_aliases:extquote:force_fignore:histappend:hostcomplete:interactive_comments:login_shell:progcomp:promptvars:sourcepath
BASH_ALIASES=()
BASH_ARGC=()
BASH_ARGV=()
BASH_CMDS=()
BASH_LINENO=()
BASH_SOURCE=()
BASH_VERSINFO=([0]="4" [1]="2" [2]="46" [3]="1" [4]="release" [5]="x86_64-redhat-linux-gnu")
BASH_VERSION='4.2.46(1)-release'
COLUMNS=210
DIRSTACK=()
DT=20191025_164714
...      

2)set -u:

執行腳本的時候,如果遇到不存在的變量,Bash 預設忽略它。例如:

#腳本script.sh:
#!/usr/bin/env bash
echo $a
echo bar

#執行:
$ sh script.sh
bar      

可以看到$a雖然不存在,但沒有報錯。大多數情況下,這不是開發者想要的行為,遇到變量不存在,腳本應該報錯,而不是一聲不響地往下執行。set -u就用來改變這種行為。腳本在頭部加上它,遇到不存在的變量就會報錯,并停止執行。

#!/usr/bin/env bash
set -u

echo $a
echo bar

$ sh script.sh
bash: script.sh:行4: a: 未綁定的變量      

3)set -x:

預設情況下,腳本執行後,螢幕隻顯示運作結果,沒有其他内容。如果多個指令連續執行,它們的運作結果就會連續輸出。有時會分不清,某一段内容是什麼指令産生的。set -x用來在運作結果之前,先輸出執行的那一行指令。

#腳本 script.sh
#!/usr/bin/env bash
set -x

echo bar

#執行
$ sh script.sh
+ echo bar
bar      

可以看到,執行​

​echo bar​

​​之前,該指令會先列印出來,行首以​

​+​

​表示。這對于調試複雜的腳本是很有用的。

4)set -e:

如果腳本裡面有運作失敗的指令(傳回值非0),Bash 預設會繼續執行後面的指令。例如:

#腳本 script.sh
#!/usr/bin/env bash

foo
echo bar

#執行
$ sh script.sh
script.sh:行3: foo: 未找到指令
bar      

可以看到,Bash 隻是顯示有錯誤,并沒有終止執行,這種行為很不利于腳本安全和除錯。set -e從根本上解決了這個問題,它使得腳本隻要發生錯誤,就終止執行。例如:

#腳本 script.sh
#!/usr/bin/env bash
set -e

foo
echo bar

#執行
$ sh script.sh
script.sh:行4: foo: 未找到指令      

可以看到,第4行執行失敗以後,腳本就終止執行了。

set -e根據傳回值來判斷,一個指令是否運作失敗。但是,某些指令的非零傳回值可能不表示失敗,或者開發者希望在指令失敗的情況下,腳本繼續執行下去。這時可以暫時關閉set -e,該指令執行結束後,再重新打開set -e。

set +e
command1
command2
set -e      

5)set -o pipefail:

set -e有一個例外情況,就是不适用于管道指令。所謂管道指令,就是多個子指令通過管道運算符(|)組合成為一個大的指令。Bash 會把最後一個子指令的傳回值,作為整個指令的傳回值。也就是說,隻要最後一個子指令不失敗,管道指令總是會執行成功,是以它後面指令依然會執行,set -e就失效了。例如:

#腳本 script.sh
#!/usr/bin/env bash
set -e

foo | echo a
echo bar

#執行
$ sh script.sh
$ bash script.sh
a
script.sh:行4: foo: 未找到指令
bar      

上面代碼中,​

​foo​

​​是一個不存在的指令,但是​

​foo | echo a​

​​這個管道指令會執行成功,導緻後面的​

​echo bar​

​會繼續執行。

#腳本 script.sh
#!/usr/bin/env bash
set -eo pipefail

foo | echo a
echo bar

#執行
$ sh script.sh
$ bash script.sh
a
script.sh:行4: foo: 未找到指令      

繼續閱讀