我想從Bash函數返回一個字符串。 shell
我將用Java編寫示例以顯示我想作的事情: 安全
public String getSomeString() { return "tadaa"; } String variable = getSomeString();
下面的示例在bash中有效,可是有更好的方法嗎? bash
function getSomeString { echo "tadaa" } VARIABLE=$(getSomeString)
考慮如下代碼,向Vicky Ronnen擡起頭來: ide
function use_global { eval "$1='changed using a global var'" } function capture_output { echo "always changed" } function test_inside_a_func { local _myvar='local starting value' echo "3. $_myvar" use_global '_myvar' echo "4. $_myvar" _myvar=$( capture_output ) echo "5. $_myvar" } function only_difference { local _myvar='local starting value' echo "7. $_myvar" local use_global '_myvar' echo "8. $_myvar" local _myvar=$( capture_output ) echo "9. $_myvar" } declare myvar='global starting value' echo "0. $myvar" use_global 'myvar' echo "1. $myvar" myvar=$( capture_output ) echo "2. $myvar" test_inside_a_func echo "6. $_myvar" # this was local inside the above function only_difference
會給 函數
0. global starting value 1. changed using a global var 2. always changed 3. local starting value 4. changed using a global var 5. always changed 6. 7. local starting value 8. local starting value 9. always changed
也許正常狀況是使用test_inside_a_func
函數中使用的語法,所以在大多數狀況下均可以使用這兩種方法,儘管捕獲輸出是在任何狀況下始終可使用的更安全的方法,模仿了您所返回的函數的返回值能夠用其餘語言找到,正如Vicky Ronnen
正確指出的那樣。 性能
agt@agtsoft:~/temp$ cat ./fc #!/bin/sh fcall='function fcall { local res p=$1; shift; fname $*; eval "$p=$res"; }; fcall' function f1 { res=$[($1+$2)*2]; } function f2 { local a; eval ${fcall//fname/f1} a 2 3; echo f2:$a; } a=3; f2; echo after:a=$a, res=$res agt@agtsoft:~/temp$ ./fc f2:10 after:a=3, res=
像上面的bstpierre同樣,我使用並建議使用顯式命名輸出變量: this
function some_func() # OUTVAR ARG1 { local _outvar=$1 local _result # Use some naming convention to avoid OUTVARs to clash ... some processing .... eval $_outvar=\$_result # Instead of just =$_result }
請注意使用引號$。 這樣能夠避免將$result
內容解釋爲shell特殊字符。 我發現這比捕獲回聲的result=$(some_func "arg1")
慣用語快一個數量級 。 在MSYS上使用bash時,速度差別彷佛更爲明顯,由於從函數調用捕獲stdout幾乎是災難性的。 spa
能夠發送局部變量,由於局部變量在bash中動態做用域: code
function another_func() # ARG { local result some_func result "$1" echo result is $result }
正如其餘人所寫,最直接,最可靠的解決方案是使用命令替換: 作用域
assign() { local x x="Test" echo "$x" } x=$(assign) # This assigns string "Test" to x
缺點是性能,由於這須要單獨的過程。
本主題中建議的另外一種技術,即傳遞要分配給變量的變量名稱做爲參數,會產生反作用,我不建議以其基本形式使用它。 問題是您可能須要在函數中使用一些變量來計算返回值,而且可能會發生的是,用於存儲返回值的變量名稱會干擾其中之一:
assign() { local x x="Test" eval "$1=\$x" } assign y # This assigns string "Test" to y, as expected assign x # This will NOT assign anything to x in this scope # because the name "x" is declared as local inside the function
固然,您可能不將函數的內部變量聲明爲局部變量,但實際上應始終這樣作,不然,若是存在一個同名變量,則您可能會意外覆蓋父做用域中不相關的變量。
一種可能的解決方法是將傳遞的變量顯式聲明爲global:
assign() { local x eval declare -g $1 x="Test" eval "$1=\$x" }
若是將名稱「 x」做爲參數傳遞,則函數主體的第二行將覆蓋先前的本地聲明。 可是名稱自己可能仍然會干擾,所以,若是您打算使用先前存儲在傳遞的變量中的值,而後再在其中寫入返回值,請注意,您必須從一開始就將其複製到另外一個局部變量中。 不然結果將不可預測! 此外,這僅適用於最新的BASH版本4.2。 更多可移植的代碼可能會使用具備相同效果的顯式條件構造:
assign() { if [[ $1 != x ]]; then local x fi x="Test" eval "$1=\$x" }
也許最優雅的解決方案只是爲函數返回值保留一個全局名稱,並在您編寫的每一個函數中一致地使用它。
如前所述,從函數返回字符串的「正確」方法是使用命令替換。 若是該函數也須要輸出到控制檯(如@Mani所述),請在該函數的開頭建立一個臨時fd並重定向到控制檯。 返回字符串以前,請關閉臨時fd。
#!/bin/bash # file: func_return_test.sh returnString() { exec 3>&1 >/dev/tty local s=$1 s=${s:="some default string"} echo "writing directly to console" exec 3>&- echo "$s" } my_string=$(returnString "$*") echo "my_string: [$my_string]"
執行沒有參數的腳本會產生...
# ./func_return_test.sh writing directly to console my_string: [some default string]
但願這能夠幫助人們
-安迪