2011-04-15 208 views
49

我需要傳遞一個函數作爲Bash中的參數。例如,下面的代碼:Bash:傳遞函數作爲參數

function x() { 
    echo "Hello world" 
} 

function around() { 
    echo "before" 
    eval $1 
    echo "after" 
} 

around x 

應該輸出:

before 
Hello world 
after 

我知道eval是不正確的在這方面不過這只是一個例子:)

任何想法?

回答

67

如果你不需要任何幻想像延遲功能名稱或參數的評估,你不需要eval

function x()  { echo "Hello world";   } 
function around() { echo before; $1; echo after; } 

around x 

做你想做的。你甚至可以通過功能和它的參數是這樣的:

function x()  { echo "x(): Passed $1 and $2"; } 
function around() { echo before; "[email protected]"; echo after; } 

around x 1st 2nd 

打印

before 
x(): Passed 1st and 2nd 
after 
0

eval可能是實現它的唯一方法。唯一真正的缺點是它的安全性方面,因爲你需要確保沒有惡意傳入,只有你想調用的函數會被調用(並檢查它沒有像';'這樣的討厭字符)也在其中)。

所以,如果你是調用代碼的人,那麼評估可能是唯一的方法。請注意,還有其他形式的eval可能會涉及子命令($()和``),但它們並不安全並且更昂貴。

+0

eval是唯一的方法。 – Wes 2011-04-15 04:18:05

+1

你可以很容易地檢查'eval $ 1'是否會使用'if declare -F「$ 1」>/dev/null來調用一個函數;然後評估$ 1; fi' – user123444555621 2011-04-15 04:26:14

+2

...或甚至更好:'eval $(declare -F「$ 1」)' – user123444555621 2011-04-15 04:58:03

1

你應該沿着線的東西:

function around() 
{ 
    echo 'before'; 
    echo `$1`; 
    echo 'after'; 
} 

你可以調用around x

1

一個更好的方法是在你的函數中使用局部變量。問題就變成了如何讓調用者獲得結果。一種機制是使用命令替換:

function myfunc() 
{ 
    local myresult='some value' 
    echo "$myresult" 
} 

result=$(myfunc) # or result=`myfunc` 
echo $result 

在這裏,結果被輸出到標準輸出和所述呼叫者使用命令替換捕獲在一個變量的值。然後可以根據需要使用該變量。

13

沒有必要使用eval

function x() { 
    echo "Hello world" 
} 

function around() { 
    echo "before" 
    var=$($1) 
    echo "after $var" 
} 

around x 
5

你不可錯過什麼不是字符串的函數。流程替換可以將其僞造。 Bash傾向於保持開放FIFO直到它擴展完成的命令。

這裏有一個快速癡心一片

foldl() { 
    echo $(($(</dev/stdin)$2)) 
} < <(tr '\n' "$1" <$3) 

# Sum 20 random ints from 0-999 
foldl + 0 <(while ((n=RANDOM%999,x++<20)); do echo $n; done) 

功能可以出口,但這不是一樣有趣它第一次出現。我發現它主要用於使腳本或運行腳本的其他程序可以訪問調試功能。

(
    id() { 
     "[email protected]" 
    } 

    export -f id 
    exec bash -c 'echowrap() { echo "$1"; }; id echowrap hi' 
) 

id仍然只得到恰好是一個函數(從一個序列化環境中自動導入)及其參數的個數名稱的字符串。

Pumbaa80對另一個答案的評論也很好(eval $(declare -F "$1")),但它主要用於數組,而不是函數,因爲它們總是全局的。如果你要在一個函數中運行它,它所要做的就是重新定義它,所以沒有任何效果。它不能用於根據當前範圍內發生的任何事情來創建閉包或部分函數或「函數實例」。充其量,這可以用來將函數定義存儲到其他地方重新定義的字符串中 - 但這些函數也只能使用硬編碼,除非使用eval

基本上Bash不能像這樣使用。

9

我不認爲有人會回答這個問題。他沒有問他是否可以按順序迴音。問題的作者想知道他是否可以模擬函數指針行爲。

有幾個答案很像我會做的,我想用另一個例子來擴展它。

從筆者:

function x() { 
    echo "Hello world" 
} 

function around() { 
    echo "before" 
    ($1)     <------ Only change 
    echo "after" 
} 

around x 

爲了擴大這一點,我們將有函數x回聲「世界,你好:$ 1」,顯示當函數執行真正發生。我們將通過一個字符串,它的功能是「X」的名稱:)傳遞給函數左右(其中回聲報「之前」

function x() { 
    echo "Hello world:$1" 
} 

function around() { 
    echo "before" 
    ($1 HERE)     <------ Only change 
    echo "after" 
} 

around x 

爲了說明這一點,字符串「X」,調用函數x (通過變量$ 1,第一個傳遞給參數的參數)傳遞參數「HERE」,最後回聲。

另一方面,這是使用變量作爲函數名稱的方法。變量實際上包含作爲函數名稱的字符串,並且($ variable arg1 arg2 ...)調用傳遞參數的函數。見下文:

function x(){ 
    echo $3 $1 $2  <== just rearrange the order of passed params 
} 

Z="x"  # or just Z=x 

($Z 10 20 30) 

給出:30 10 20,在那裏我們執行命名的函數「X」存儲在變量Z和通過分配的變量名傳遞的參數10 20和30

以上在這裏我們參考函數所以我們可以使用變量來代替實際知道函數名稱的地方(這類似於你在c中的一個非常經典的函數指針情況下可以做的事情,用於推廣程序流但是預先選擇你將要進行的函數調用基於命令行參數)。

在bash中,這些不是函數指針,而是指向稍後使用的函數名稱的變量。

+0

這個答案很棒。我做了所有例子的bash腳本並且運行它們。我也非常喜歡你如何製造「只有改變」的製造商,這對我們有很大的幫助。 您的倒數第二段有錯誤拼寫:「Aabove」 – 2017-09-12 21:28:39

+0

錯字固定。謝謝@J MADISON – uDude 2017-09-14 20:18:10

+0

爲什麼你用'()'包裝它不會啓動一個子shell? – horseyguy 2018-01-12 02:28:39