我使用 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'
Run Code Online (Sandbox Code Playgroud)
我认为某些 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
Run Code Online (Sandbox Code Playgroud)
我确信.bashrc
它本身没有问题,因为在 Bash 启动时我没有看到任何错误或警告。进一步调试我注意到.profile
采购.bashrc
# source Bash customizations
[ -n "${BASH_VERSION}" ] && [ -r "${HOME}/.bashrc" ] && . "${HOME}/.bashrc"
Run Code Online (Sandbox Code Playgroud)
这是我的~/.bashrc
开始
# If not running interactively, don't do anything
[[ "$-" != *i* ]] && return
Run Code Online (Sandbox Code Playgroud)
问题:
man
必须.profile
在开始之前找到源?.profile
不要源.bashrc
时,它不是猛砸.bashrc
非交互式时签入以停止进一步处理从@muru 的评论中,我意识到我不应该在导出的函数中使用 Bashism,因为它存在被非 Bash shell 导入的风险。一个仍然存在的问题:为什么man
调用sh
?.
Sté*_*las 17
man
不读书的~/.profile
,但它将运行sh
解释一些命令行(或解释nroff
,至少在我的系统是一个sh
脚本包装到groff
),并在系统中sh
恰好是bash
,其进口功能通过出口bash
的export -f
(在命名变量BASH_FUNC_funcname%%
,因为shellshock),即使作为sh
.
在这里,您要导出一个语法取决于extglob
选项的函数。因此,当sh
( bash
in sh
mode) 启动并导入所有导出的函数时,它无法解析它,因为extglob
它不是默认启用的选项(无论是否处于sh
模式下)。
IOW,导出功能是指功能将在所有可用的bash
调用和所有sh
地方的系统调用sh
与实现bash
。因此,您需要注意这些函数中的语法与它们的默认设置bash
和sh
-as-都兼容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
Run Code Online (Sandbox Code Playgroud)
如果您的函数需要影响语法解析的非默认选项,例如extglob
,您可以将其定义为:
f() (
shopt -s extglob
eval '
function body here
'
)
export -f f
Run Code Online (Sandbox Code Playgroud)
这里在子shell中运行代码,以便仅在函数执行期间设置选项(bash
,与选项的本地范围相反zsh
或ksh
没有本地范围(除了set
最近版本中设置的选项)),并使用eval
延迟解析直到调用函数并设置extglob
选项。
在这里,您还可以执行以下操作:
f() (
shopt -s extglob
pattern='*.?(ba)sh'
case ... in
($pattern)...
esac
)
Run Code Online (Sandbox Code Playgroud)
不过,你也可以这样做:
f() {
case ... in
(*.sh | *.bash) ...
esac
}
Run Code Online (Sandbox Code Playgroud)
这是sh
不需要extglob
.
Gil*_*il' 12
不要导出函数,尤其是需要非默认 bash 配置的函数。
导出的函数是 bash 功能,因此它们不会影响其他 shell。但它们会影响 bash 的所有调用。您的函数需要extglob
启用该选项,否则不仅运行函数失败,甚至解析函数定义也会失败。当函数被导出时,bash 会在开始执行任何操作之前解析函数定义。
所述sh
外壳是用于UNIX系统中的粘合代码。它不仅用于交互使用,在幕后也大量使用。程序sh
出于多种目的而调用。man
调用几个伴随程序,它们中的任何一个都可能调用一个 shell。例如,nroff
Linux 上的(这是将手册页的源代码转换为终端格式的文本的主要命令)的通常实现是一个 shell 脚本,它调用groff
( 的 GNU 实现nroff
)和一些选项以使其与一个传统的nroff
。这会调用sh
,这取决于发行版可能是 dash、bash(或者,在 Linux 上很少见,其他一些实现)。
对于交互使用,bash的负荷.bashrc
,所以有在其环境中的功能是没有意义的:把自己的定义.bashrc
,或者从源文件.bashrc
。对于您自己的脚本,将函数定义放在您选择的文件中(不是.profile
或.bashrc
它们自己,因为它们包含不应为每个脚本执行的内容)并在脚本中放置一个source
or.
命令。导出函数定义几乎没有什么好处:因为您可以控制使用它们的脚本,所以您可以使它们在定义所在的位置作为源。如果 bash 在其默认设置中无法解析该函数,则它不可能工作。