如果使用聲明,bash 變數不會在數組內擴展
最近我決定閱讀更多關於 bash 內置函式
declare
、local
和readonly
的內容,這導致我從:local variable_name variable_name='value' readonly variable_name
至:
variable_name='value' declare -r variable_name
此更改減少了編寫的行數並允許我設置一些屬性,例如告訴 bash 變數的值是整數,這很好。但是,在創建將用作 cURL 別名的函式時,我注意到如果我使用數組中的變數永遠不會擴展,而使用and
declare
擴展就好了。local``readonly
這是一個例子:
#!/usr/bin/env bash set -o errexit -o errtrace -o pipefail -o nounset IFS=$'\n\t' curl() { curl_version="$(command curl --version | awk 'NR==1 {print $2}')" declare -r curl_version curl_args=( --user-agent "curl/${curl_version}" --silent --fail ) command curl "${curl_args[@]}" \ "${@}" } curl --url 'https://httpbin.org/get'
因為無論出於何種原因變數都不會擴展,
--user-agent
所以數組的一部分使腳本退出並出現錯誤,因為據 bash 所知,這是一個未綁定的變數,並且由於set -o nounset
.幾天來我一直試圖讓它工作,所以我想是時候扔毛巾尋求幫助了。誰能指出我正確的方向以了解我做錯了什麼?
編輯:
忘了提,但是如果我在同一行中聲明它,變數確實會擴展,比如
declare -r variable_name
. 問題是,如果我這樣做,我會從 ShellCheck 中點擊 SC2155,因此我試圖在設置值後聲明。
和:
curl_version="$(command curl --version | awk 'NR==1 {print $2}')" declare -r curl_version
在函式中,您將
$curl_version
全域變數設置為某個值,然後創建一個單獨的本地和只讀變數,該變數最初未設置。看起來你想要:
# instantiate a new local variable (but in bash it inherits the "export" # attribute if any of the variable with same name in the parent scope) local curl_version # unset to remove that export attribute if any. Though you could # also change the above to local +x curl_version unset -v curl_version # give a value: curl_version="$(command curl --version | awk 'NR==1 {print $2}')" # make that local variable read only local -r curl_version
(這裡使用
local
而不是declare
更清楚地表明您想要使變數成為本地變數¹)。或者同時使用:
local +x -r curl_version="$(command curl --version | awk '{print $2; exit}')"
(儘管如 shellcheck 所述,您隨後會失去管道的退出狀態²)。
無論如何,我不會像你在 C 中使用的那樣在 shell 中使用
readonly
/ ,尤其是在. Shell(ksh93 除外)沒有像 C 中那樣的靜態作用域。並且(與例如相反),如果在全域作用域中將其設為只讀,則不能創建函式的局部變數。typeset -r``const``bash``bash``zsh
例如:
count() { local n for (( n = 0; n < $1; n++ )) { echo "$n"; } } readonly n=5 count "$n"
可以在 zsh 中工作,但不能在 bash 中工作。
local -r
如果你只使用並且從不使用它可能沒問題readonly
。¹ 在任何情況下
typeset
//在declare
中local
都是一樣的bash
,唯一的區別是如果你嘗試在local
函式之外使用,它會報告錯誤。and之間的區別(與typeset -r
andreadonly
相同)是後者在函式內呼叫時不會實例化新變數。typeset -x``export
² 看看
exit
在awk
那個版本中如何awk
在第一行之後停止處理輸入,curl
可能會被 SIGPIPE 殺死(實際上不太可能curl
一次性發送它的輸出並且它會適合管道)並且因為pipefail
,管道最終可能以 141 退出狀態失敗,但local
只要它可以為變數賦值,它本身仍然會成功。