Source From Here
一、簡介
set 命令是 Bash 腳本的重要環節,卻常常被忽視,導致腳本的安全性和可維護性出問題。本文介紹它的基本用法,讓你可以更安心地使用 Bash 腳本。我們知道,Bash 執行腳本的時候,會創建一個新的 Shell:
上面代碼中,script.sh 是在一個新的 Shell 裡面執行。這個 Shell 就是腳本的執行環境,Bash 默認給定了這個環境的各種參數。set 命令用來修改 Shell 環境的運行參數,也就是可以定制環境。一共有十幾個參數可以定制,官方手冊有完整清單,本文介紹其中最常用的四個。
二、set -u
執行腳本的時候,如果遇到不存在的變量,Bash 默認忽略它:
上面代碼中,$a
是一個不存在的變量。執行結果如下:
可以看到,echo $a 輸出了一個空行,Bash 忽略了不存在的 $a,然後繼續執行 echo bar。大多數情況下,這不是開發者想要的行為,遇到變量不存在,腳本應該報錯,而不是一聲不響地往下執行。 set -u 就用來改變這種行為。腳本在頭部加上它,遇到不存在的變量就會報錯,並停止執行。
運行結果如下:
可以看到,腳本報錯了,並且不再執行後面的語句。-u 還有另一種寫法 -o nounset,兩者是等價的:
三、set -x
默認情況下,腳本執行後,屏幕只顯示運行結果,沒有其他內容。如果多個命令連續執行,它們的運行結果就會連續輸出。有時會分不清,某一段內容是什麼命令產生的。set -x 用來在運行結果之前,先輸出執行的那一行命令:
執行上面的腳本,結果如下:
可以看到,執行 echo bar 之前,該命令會先打印出來,行首以 + 表示。這對於調試複雜的腳本是很有用的。-x 還有另一種寫法 -o xtrace:
四、Bash 的錯誤處理
如果腳本里面有運行失敗的命令(返回值非 0),Bash 默認會繼續執行後面的命令:
面腳本中,foo
是一個不存在的命令,執行時會報錯。但是,Bash 會忽略這個錯誤,繼續往下執行:
可以看到,Bash 只是顯示有錯誤,並沒有終止執行。這種行為很不利於腳本安全和除錯。實際開發中,如果某個命令失敗,往往需要腳本停止執行,防止錯誤累積。這時,一般採用下面的寫法:
上面的寫法表示只要
command 有非零返回值,腳本就會停止執行。如果停止執行之前需要完成多個操作,就要採用下面三種寫法:
另外,除了停止執行,還有一種情況。如果兩個命令有繼承關係,只有第一個命令成功了,才能繼續執行第二個命令,那麼就要採用下面的寫法:
五、 set -e
上面這些寫法多少有些麻煩,容易疏忽。set -e 從根本上解決了這個問題,它使得腳本只要發生錯誤,就終止執行:
執行結果如下。
可以看到,第4行執行失敗以後,腳本就終止執行了。
set -e 根據返回值來判斷,一個命令是否運行失敗。但是,某些命令的非零返回值可能不表示失敗,或者開發者希望在命令失敗的情況下,腳本繼續執行下去。這時可以暫時關閉 set -e,該命令執行結束後,再重新打開 set -e:
上面代碼中,
set +e 表示關閉 -e 選項,set -e 表示重新打開 -e 選項。還有一種方法是使用 command || true,使得該命令即使執行失敗,腳本也不會終止執行:
上面代碼中,true 使得這一行語句總是會執行成功,後面的
echo bar 會執行。-e 還有另一種寫法 -o errexit。
六、set -o pipefail
set -e 有一個例外情況,就是不適用於管道命令。所謂管道命令,就是多個子命令通過管道運算符(|)組合成為一個大的命令。Bash 會把最後一個子命令的返回值,作為整個命令的返回值。也就是說,只要最後一個子命令不失敗,管道命令總是會執行成功,因此它後面命令依然會執行,set -e 就失效了。
請看下面這個例子:
執行結果如下:
上面代碼中,foo 是一個不存在的命令,但是 foo | echo a 這個管道命令會執行成功,導致後面的echo bar會繼續執行。set -o pipefail 用來解決這種情況,只要一個子命令失敗,整個管道命令就失敗,腳本就會終止執行:
運行後,結果如下。
可以看到,echo bar 沒有執行。
七、總結
set 命令的上面這四個參數,一般都放在一起使用。
這兩種寫法建議放在所有Bash 腳本的頭部。另一種辦法是在執行Bash 腳本的時候,從命令行傳入這些參數:
一、簡介
set 命令是 Bash 腳本的重要環節,卻常常被忽視,導致腳本的安全性和可維護性出問題。本文介紹它的基本用法,讓你可以更安心地使用 Bash 腳本。我們知道,Bash 執行腳本的時候,會創建一個新的 Shell:
上面代碼中,script.sh 是在一個新的 Shell 裡面執行。這個 Shell 就是腳本的執行環境,Bash 默認給定了這個環境的各種參數。set 命令用來修改 Shell 環境的運行參數,也就是可以定制環境。一共有十幾個參數可以定制,官方手冊有完整清單,本文介紹其中最常用的四個。
二、set -u
執行腳本的時候,如果遇到不存在的變量,Bash 默認忽略它:
- #!/usr/bin/env bash
- echo $a
- echo bar
可以看到,echo $a 輸出了一個空行,Bash 忽略了不存在的 $a,然後繼續執行 echo bar。大多數情況下,這不是開發者想要的行為,遇到變量不存在,腳本應該報錯,而不是一聲不響地往下執行。 set -u 就用來改變這種行為。腳本在頭部加上它,遇到不存在的變量就會報錯,並停止執行。
- #!/usr/bin/env bash
- set -u
- echo $a
- echo bar
可以看到,腳本報錯了,並且不再執行後面的語句。-u 還有另一種寫法 -o nounset,兩者是等價的:
- set -o nounset
默認情況下,腳本執行後,屏幕只顯示運行結果,沒有其他內容。如果多個命令連續執行,它們的運行結果就會連續輸出。有時會分不清,某一段內容是什麼命令產生的。set -x 用來在運行結果之前,先輸出執行的那一行命令:
- #!/usr/bin/env bash
- set -x
- echo bar
可以看到,執行 echo bar 之前,該命令會先打印出來,行首以 + 表示。這對於調試複雜的腳本是很有用的。-x 還有另一種寫法 -o xtrace:
- set -o xtrace
如果腳本里面有運行失敗的命令(返回值非 0),Bash 默認會繼續執行後面的命令:
- #!/usr/bin/env bash
- foo
- echo bar
可以看到,Bash 只是顯示有錯誤,並沒有終止執行。這種行為很不利於腳本安全和除錯。實際開發中,如果某個命令失敗,往往需要腳本停止執行,防止錯誤累積。這時,一般採用下面的寫法:
- command || exit 1
- # 写法一
- 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
上面這些寫法多少有些麻煩,容易疏忽。set -e 從根本上解決了這個問題,它使得腳本只要發生錯誤,就終止執行:
- #!/usr/bin/env bash
- set -e
- foo
- echo bar
可以看到,第4行執行失敗以後,腳本就終止執行了。
set -e 根據返回值來判斷,一個命令是否運行失敗。但是,某些命令的非零返回值可能不表示失敗,或者開發者希望在命令失敗的情況下,腳本繼續執行下去。這時可以暫時關閉 set -e,該命令執行結束後,再重新打開 set -e:
- set +e
- command1
- command2
- set -e
- #!/bin/bash
- set -e
- foo || true
- echo bar
- set -o errexit
set -e 有一個例外情況,就是不適用於管道命令。所謂管道命令,就是多個子命令通過管道運算符(|)組合成為一個大的命令。Bash 會把最後一個子命令的返回值,作為整個命令的返回值。也就是說,只要最後一個子命令不失敗,管道命令總是會執行成功,因此它後面命令依然會執行,set -e 就失效了。
請看下面這個例子:
- #!/usr/bin/env bash
- set -e
- foo | echo a
- echo bar
上面代碼中,foo 是一個不存在的命令,但是 foo | echo a 這個管道命令會執行成功,導致後面的echo bar會繼續執行。set -o pipefail 用來解決這種情況,只要一個子命令失敗,整個管道命令就失敗,腳本就會終止執行:
- #!/usr/bin/env bash
- set -eo pipefail
- foo | echo a
- echo bar
可以看到,echo bar 沒有執行。
七、總結
set 命令的上面這四個參數,一般都放在一起使用。
- # 写法一
- set -euxo pipefail
- # 写法二
- set -eux
- set -o pipefail
沒有留言:
張貼留言