為什麼 man 抱怨 .profile 中定義的函式?
我使用 Bash 5.1.8。執行
man
顯示手冊頁,但出現以下錯誤man ps sh: bat: line 10: syntax error near unexpected token `(' sh: bat: line 10: ` *.?(ba)sh)' sh: error importing function definition for `bat'
我認為某些 shell (
sh
) 發現 Bashisms 令人討厭。如果我從以下內容中刪除 Bash-isms,這些錯誤就會消失~/.bashrc
:function bat { # lines snipped for brevity case "$f" in *.rs ) opt_syntax="--syntax=rust";; *.?(ba)sh ) opt_syntax="--syntax=shellscript";; *.?(m)m ) opt_syntax="--syntax=objc";; esac # lines snipped for brevity } export -f bat
我確信
.bashrc
它本身沒有問題,因為當 Bash 啟動時我看不到任何錯誤或警告。進一步調試我注意到.profile
採購.bashrc
# source Bash customizations [ -n "${BASH_VERSION}" ] && [ -r "${HOME}/.bashrc" ] && . "${HOME}/.bashrc"
這是我的
~/.bashrc
開始# If not running interactively, don't do anything [[ "$-" != *i* ]] && return
問題:
為什麼
man
必須.profile
在開始之前進行採購?儘管進行了兩次檢查,為什麼上述程式碼會被非 Bash shell 解析?
當它不是 Bash 時簽入
.profile
不來源.bashrc
簽入
.bashrc
以在非互動式時停止進一步處理從@muru 的評論中,我意識到我不應該在導出的函式中使用 Bashism,因為它存在被非 Bash shell 導入的風險。一個仍然存在的問題:為什麼
man
打電話sh
?.
man
沒有讀你的~/.profile
,但它會執行sh
解釋一些命令行(或解釋nroff
至少在我的系統上是一個sh
腳本包裝器groff
),並且在你的系統上sh
恰好是bash
,它導入bash
由’s導出的函式(在export -f
變數命名為BASH_FUNC_funcname%%
shellshock),即使以sh
.在這裡,您要導出一個語法取決於
extglob
選項的函式。因此,當sh
(bash
insh
mode) 啟動並導入所有導出的函式時,它無法解析它,因為extglob
它不是預設啟用的選項(無論是否在sh
模式下)。IOW,導出函式意味著該函式將在所有
bash
呼叫中可用,並且sh
在sh
使用bash
. 因此,您需要注意這些函式中的語法與它們的預設設置兼容,bash
或者完全避免導出函式,因為即使函式永遠不會被呼叫,這些函式中的程式碼也會被解析。sh``bash
看:
$ env 'BASH_FUNC_f%%=() { case x in ?(x)) echo x; esac; }' bash -O extglob -c f x $ env 'BASH_FUNC_f%%=() { case x in ?(x)) echo x; esac; }' bash -c f bash: f: line 0: syntax error near unexpected token `(' bash: f: line 0: `f () { case x in ?(x)) echo x; esac; }' bash: error importing function definition for `f' bash: line 1: f: command not found
如果您的函式需要一個影響語法解析的非預設選項,例如
extglob
,您可以將其定義為:f() ( shopt -s extglob eval ' function body here ' ) export -f f
這裡在子shell中執行程式碼,僅在函式執行期間設置選項(與選項
bash
相反zsh
或ksh
沒有選項的本地範圍(除了set
最近版本中設置的選項)),並使用eval
延遲解析直到呼叫函式並extglob
設置選項。在這裡,您還可以這樣做:
f() ( shopt -s extglob pattern='*.?(ba)sh' case ... in ($pattern)... esac )
不過,您也可以這樣做:
f() { case ... in (*.sh | *.bash) ... esac }
sh
這是不需要的標準語法extglob
。