2013-07-19 60 views
1

當我的bash腳本開始變得複雜時,我通常會將它們分解爲函數。這尤其適用於複雜的管道,因爲一系列複雜的管道命令(例如包含while循環)可能很快變得難以閱讀。當需要並行化時更是如此,其中xargs非常有用。Bash:在xarg中使用的導出函數

我知道我可以功能導出到一個子shell與export -f,從而在一個簡單的情況下,我可以做

export -f myfunction 
some-command | xargs -Iline bash -c "myfunction 'line'" 

但如果myfunction取決於其他功能這變得難以維持 - 每次函數的變化使得子shell執行myfunction所需的函數改變,導出語句將不得不改變 - 這似乎很容易出錯。

是否有一些通用的方法來導出subhells使用的函數?我在想沿着「導出所有定義的函數」命令行,然後將允許代碼結構像

main() { ... } 
func1() { ... } 
func2() { ... } 
<export all functions> 
main "[email protected]" 

回答

2

你的問題問大約只有導出功能。這在bash中很容易,見下文。

你的問題的標題/主題暗示在xargs中使用函數,就好像它們是一個腳本; 我不知道xargs可以直接「調用」bash函數,但是您當然也可以在xargs所調用的腳本中使用導出的函數 ,如下所示。

首先,列出函數的函數。在默認情況下並-v用戶功能列出所有功能:

lsfns() { 
    case "$1" in 
     -v | v*) 
     # verbose: 
     set | grep '()' --color=always 
     ;; 
     *)⋅ 
     declare -F | cut -d" " -f3 | egrep -v "^_" 
     ;; 
    esac 
} 

下一頁的功能導出所有用戶的功能:

exportfns() { export -f $(lsfns); } 

或只是把export -f $(lsfns).bashrc

例腳本doit.sh:(chmod a+rx doit.sh後)

#!/bin/bash 
lsfns "[email protected]" # make use of function exported by parent shell :) 

實施例的命令行:

echo -v | xargs doit.sh 

echo "" | xargs doit.sh 

EDIT 1比較:進一步響應於kdb的評論/答案be低(「遇到導出函數根本不起作用的情況):

shell函數的導出不是Posix兼容的 - 即它只適用於Bash,可能還有其他的shell,比如Zsh,Ksh等。

也就是說,在Dash和「標準」Posix shell中沒有提供「export -f」,我們不能導出函數,並且如果我們在Bash中導出一個函數,然後運行一個以sh-bang開頭的腳本例如「#!/ bin/dash」,該腳本將無法使用父shell中的「導出」函數,因爲由Bash導出到「進程環境」的函數不會被Dash識別。


編輯2:進一步響應OP評論「但如果myfunction取決於其他功能這變得難以維持」:

這可能是一種情況,人們可以好好利用殼source的命令(別名「。」),例如:

. ~/etc/my-functions.sh; myMain ... 

而且類似地,如果你「活」在功能而不是腳本文件中,比如當你需要的時候通過調用myMain,那麼這個函數的第一行就可以來源你的函數庫;

,因爲這將是在「定期運行一個腳本」的情況下多餘的開銷,myMain成爲命令行存根功能,(重新)加載你的函數庫,並調用actuallyDoit功能(這也可以從被稱爲在你的腳本里面,如果你有一個腳本文件)。

享受
Zenaan

+0

其實我的問題包含一個使用xargs的例子,所以我沒有看到第二段的含義。我讚賞其餘的答案('exportfns'會有幫助)。 – kdb

+0

我正在爲未來的讀者澄清,以防有人試圖直接使用xargs「調用」一個函數,將該函數視爲腳本來處理,例如'find dir/| xargs aBashFn'不能工作 - 你必須將這個函數包裝在腳本中,或者像你的第一個例子那樣,產生一個bash子shell - 但是fork bash調用一個函數的方式非常繁重,除非你只需要它一次。我希望這解釋了爲什麼我寫這個... – zenaan

1

這似乎是工作打印所有的函數名的東西。它感覺脆弱,所以對其進行測試

declare -f | grep -oP '^\S+(?=\s*\(\))' 
0
export -f $(compgen -A function) 
+0

雖然我第一次認爲這是一個很好的答案,因爲它的簡單性,我不幸地發現它打破了xargs。我得到「xargs:環境對於exec來說太大了」。這可能是cygwin特定的,但Windows似乎限制了環境變量的長度。 – kdb

0

自從收到了答案,我發現很多情況下,最好校對不同的技術:使腳本調用本身。一個簡單的例子是

# Print hash and disk usage for each argument 
if [[ $1 == run ]]; then 
    shift 1 
    printf "%s\0" "[email protected]" | xargs -0 -n 1 -P "$NUMBER_OF_PROCESSORS" -- "$0" printpar 
elif [[ $1 == printpar ]]; then 
    echo ":: $(cat "$2" | sha1sum) $(du -sh "$2")" 
else 
    echo "Invalid first parameter '$1'" 
    exit 1 
fi 

在現實世界的例子,我想要麼使有關的參數(例如使用__SUCH__爲自呼關鍵字的形狀)的假設或隱藏遞歸調用後面的無證--command-line-switch

導出函數通常更優雅,但對於大量的函數,它會變得非常慢,我記得遇到問題,它完全失敗。