我有一個函數(=被呼叫者),它應該在其呼叫者中有效地聲明和分配幾個變數。它還應該能夠知道呼叫者的名字是什麼。
eval
現在,我透過向傳遞的變數傳回由呼叫者編輯的字串來實現前者。
write_local_var_assignments variable_name; eval "$variable_name"
"$FUNCNAME"
我想我可以透過讓呼叫者傳遞或讓被呼叫者呼叫caller
內建函數並解析其輸出來實現後者。
所有這些解決方案看起來都很笨拙,所以我有兩個問題:
- 被呼叫者是否可以在沒有呼叫者合作的情況下將局部變數分配到呼叫者的上下文?
即,我可以壓縮:
write_local_var_assignments variable_name; eval "$variable_name"
進入剛剛
run_local_var_assignments
?
- 有沒有更好的方法來取得函數呼叫者的名稱?無需解析或命令替換即可直接獲取結果會很好。
答案1
在bash
(and ksh88
, mksh
, yash
, dash
, zsh
) 中,局部變數作用域是動態的。
這段程式碼:
f() { a=2; echo "f: $a"; }
g() { local a=1; f; echo "g: $a"; }
a=0
g
echo "global: $a"
產生這個輸出:
f: 2
g: 2
global: 0
這是f
updateg
的$a
變量,因為它是從 調用的g
。
這與使用 in 聲明的變量形成對比,使用語法 ( ) intypeset
聲明的函數或使用in聲明的變量,您將得到:ksh
function f { ...; }
ksh93
private
zsh
f: 2
g: 1
global: 2
所以在這種情況下,您不需要執行任何操作。
要知道呼叫你的函數的名稱,在 中bash
,你可以使用${FUNCNAME[1]}
。
等價zsh
的是$funcstack[2]
:
$ zsh -c 'f() { echo $funcstack[2]; }; g() { f; }; g'
g
$ bash -c 'f() { echo "${FUNCNAME[1]}"; }; g() { f; }; g'
g
答案2
如果您不介意使用命令替換,有一個函數可以讓您將自我評估的字串傳回給呼叫者,從而允許您run_local_var_assignments
這樣調用您創建的函數:
$(run_local_var_assignments)
函數如下所示:
emit () {
local IFS=$'\n'
printf 'eval eval %q' "$*"
}
它發出一個 eval 語句,就像eval "$variable_name"
上面的一樣。由於它是結果行中的第一件事,因此 bash 將發出的 eval 作為命令運行。
該函數完成的雙重 eval 和 %q 轉義只是在那裡,因為它們是正確地將換行符和其他特殊字元傳遞給呼叫者所必需的。
然後你的函數可以寫成:
run_local_var_assignments () {
local assignments=()
assignments+=( 'local myvar1="my value1"' )
assignments+=( 'local myvar2="my value2"' )
emit "${assignments[@]}"
}
賦值的編寫方式就好像它們是原始碼一樣,因此值中的空格之類的內容需要如上所述的引號。
在命令替換中呼叫此函數(如本答案的頂部)將使用這些值在呼叫者的作用域中建立這些局部變數。
您也可以使用常規字串或定界符來代替語句數組,但我經常發現數組方法對於動態構建語句塊很有用,所以我想我會展示這一點,儘管它稍微複雜一些。