shell 腳本之set 命令(轉)

服務器的開發和管理離不開 Bash 腳本,掌握它須要學習大量的細節。html

set命令是 Bash 腳本的重要環節,卻經常被忽視,致使腳本的安全性和可維護性出問題。本文介紹它的基本用法,讓你能夠更安心地使用 Bash 腳本。node

1、簡介

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

$ bash script.sh 

上面代碼中,script.sh是在一個新的 Shell 裏面執行。這個 Shell 就是腳本的執行環境,Bash 默認給定了這個環境的各類參數。bash

set命令用來修改 Shell 環境的運行參數,也就是能夠定製環境。一共有十幾個參數能夠定製,官方手冊有完整清單,本文介紹其中最經常使用的四個。服務器

順便提一下,若是命令行下不帶任何參數,直接運行set,會顯示全部的環境變量和 Shell 函數。函數

$ set 

2、set -u

執行腳本的時候,若是遇到不存在的變量,Bash 默認忽略它。學習

#!/usr/bin/env bash echo $a echo bar 

上面代碼中,$a是一個不存在的變量。執行結果以下。ui

$ bash script.sh bar 

能夠看到,echo $a輸出了一個空行,Bash 忽略了不存在的$a,而後繼續執行echo bar。大多數狀況下,這不是開發者想要的行爲,遇到變量不存在,腳本應該報錯,而不是一言不發地往下執行。spa

set -u就用來改變這種行爲。腳本在頭部加上它,遇到不存在的變量就會報錯,並中止執行。命令行

#!/usr/bin/env bash set -u echo $a echo bar 

運行結果以下。

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

能夠看到,腳本報錯了,而且再也不執行後面的語句。

-u還有另外一種寫法-o nounset,二者是等價的。

set -o nounset 

3、set -x

默認狀況下,腳本執行後,屏幕只顯示運行結果,沒有其餘內容。若是多個命令連續執行,它們的運行結果就會連續輸出。有時會分不清,某一段內容是什麼命令產生的。

set -x用來在運行結果以前,先輸出執行的那一行命令。

#!/usr/bin/env bash set -x echo bar 

執行上面的腳本,結果以下。

$ bash script.sh + echo bar bar 

能夠看到,執行echo bar以前,該命令會先打印出來,行首以+表示。這對於調試複雜的腳本是頗有用的。

-x還有另外一種寫法-o xtrace

set -o xtrace 

4、Bash 的錯誤處理

若是腳本里面有運行失敗的命令(返回值非0),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從根本上解決了這個問題,它使得腳本只要發生錯誤,就終止執行。

#!/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 

上面代碼中,true使得這一行語句老是會執行成功,後面的echo bar會執行。

-e還有另外一種寫法-o errexit

set -o errexit 

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: 未找到命令 

能夠看到,echo bar沒有執行。

7、總結

set命令的上面這四個參數,通常都放在一塊兒使用。

# 寫法一 set -euxo pipefail # 寫法二 set -eux set -o pipefail 

這兩種寫法建議放在全部 Bash 腳本的頭部。

另外一種辦法是在執行 Bash 腳本的時候,從命令行傳入這些參數。

$ bash -euxo pipefail script.sh
相關文章
相關標籤/搜索