Bash

為什麼 man 抱怨 .profile 中定義的函式?

  • July 22, 2021

我使用 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

問題:

  1. 為什麼man必須.profile在開始之前進行採購?

  2. 儘管進行了兩次檢查,為什麼上述程式碼會被非 Bash shell 解析?

  3. 當它不是 Bash 時簽入.profile不來源.bashrc

  4. 簽入.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( bashin shmode) 啟動並導入所有導出的函式時,它無法解析它,因為extglob它不是預設啟用的選項(無論是否在sh模式下)。

IOW,導出函式意味著該函式將在所有bash呼叫中可用,並且shsh使用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相反zshksh沒有選項的本地範圍(除了set最近版本中設置的選項)),並使用eval延遲解析直到呼叫函式並extglob設置選項。

在這裡,您還可以這樣做:

f() (
 shopt -s extglob
 pattern='*.?(ba)sh'
 case ... in
   ($pattern)...
 esac
)

不過,您也可以這樣做:

f() {
 case ... in
   (*.sh | *.bash) ...
 esac
}

sh這是不需要的標準語法extglob

引用自:https://unix.stackexchange.com/questions/659253