简洁版本:
当在我的 Mac 上的 bash 块内打开扩展 glob 时if
,整个块会失败,并在该块中包含的任何扩展 glob 模式上出现语法错误,即使它是在激活之后也是如此。在我的其他机器上没有问题。为什么?
长版:
if
我在整个文件中设置了以下条件.bashrc
。在 shell 初始化期间运行的if
其他文件中也有类似的语句。.*
我这样做是为了防止将来出错时对该文件进行递归执行。
if [ -n "${_BASHRC_INIT}" ] ; then
export _BASHRC_INIT="True"
...
...
...
unset _BASHRC_INIT
fi
laa
我在 my 中定义了这个函数,.bashrc
它依赖于扩展的 glob。我在定义函数之前立即启用它。该函数显示目录中匹配.*
排除.
和 的所有文件..
。由于我已ls
别名为ls --color=auto
,它还显示文件类型颜色,如果我只是通过管道传输到 ,则不会保留该颜色grep
。它在不在if
块内时起作用。
shopt -s extglob
laa() {
if [[ $# -eq 1 ]]; then
exec 3>&1 # Another file descriptor for this STDOUT
_items=$(cd "${1}"; ls -d .!(|.)?* 1>&3) # This requires extended glob
# `shopt -s extglob` to enable
exec 3>&- # Close file descriptor
elif [ $# -gt 1 ]; then
exec 3>&1
for i in $@; do
_items=$(cd $i; echo "$i:" 1>&3; ls -d .!(|.)* 1>&3)
done
exec 3>&-
else
ls -d .!(|.)*
fi
}
当该块也包含此摘录时,该块中包含的所有命令if
都无法在我的 M1 Mac 上执行。我的 Windows PC 上的 WSL2 (Debian) 不会出现此问题。 Mac 运行bash 5.2.15
安装了 Homebrew 和 WSL 运行bash 5.1.4
。
具体来说,它在命令上失败ls
并出现以下错误。
bash: syntax error near unexpected token `('
bash: ` _items=$( cd "${1}"; ls -d .!(|.)?* 1>&3 ) # This requires extended glob'
如果我在输入之前执行,则整个if
块将按预期执行。shopt -t extglob
这是我目前的解决方案。然而,这打破了我试图在我的环境中设置的约定。我尝试.*
在我的计算机上使用相同的文件,这也有助于在新计算机上快速创建一致的环境。
是什么导致对块的内容进行额外的语法检查if
?
答案1
您可以在这里完全避免 KSH 风格的扩展 glob- 例如参见如何全局显示除当前目录和父目录之外的所有隐藏文件。
下compat51
于6.12 Shell 兼容模式
解析命令替换的行为就像启用了扩展 glob(请参阅 Shopt 内置)一样,因此解析包含 extglob 模式(例如,作为 shell 函数的一部分)的命令替换不会失败。这假设意图是在执行命令和执行字扩展之前启用 extglob。如果执行命令时 extglob 尚未启用,它将在字扩展时失败。
因此考虑例如
#!/bin/bash
echo "$BASH_VERSION"
laa() {
if [[ $# -eq 1 ]]; then
exec 3>&1 # Another file descriptor for this STDOUT
_items=$(cd "${1}"; ls -d .!(|.)?* 1>&3) # This requires extended glob
# `shopt -s extglob` to enable
exec 3>&- # Close file descriptor
elif [ $# -gt 1 ]; then
exec 3>&1
for i in $@; do
_items=$(cd $i; echo "$i:" 1>&3; ls -d .!(|.)* 1>&3)
done
exec 3>&-
fi
}
shopt -s extglob
laa .
whereextglob
是在函数定义之后但执行之前设置的(如果该函数源自您的 .bashrc 文件),那么在 bash 5.1 下它可以工作:
$ /bin/bash ./myscript
5.1.16(1)-release
.admins .cache .hardinfo .parallel .RData .ssh .vboxclient-display-svga-x11.pid .Xauthority .zcompdump
.bash_history .config .lesshst .password-store .Rhistory .sudo_as_admin_successful .vboxclient-draganddrop.pid .xinitrc .zsh_history
.bash_logout .dialogrc .local .profile .selected_editor .test_dot_folder .vboxclient-seamless.pid .xscreensaver .zshrc
.bashrc .gnupg .mozilla .python_history .spectrum3d .vboxclient-clipboard.pid .wget-hsts .xsession-errors
在 5.2 下,它不会:
$ ./src/bash-5.2.15/bash ./myscript
5.2.15(1)-release
./myscript: line 8: syntax error near unexpected token `('
./myscript: line 8: ` _items=$(cd "${1}"; ls -d .!(|.)?* 1>&3) # This requires extended glob'
然而, 行为外部命令替换的情况是相同的;所以一旦你添加了else
块:
#!/bin/bash
echo "$BASH_VERSION"
laa() {
if [[ $# -eq 1 ]]; then
exec 3>&1 # Another file descriptor for this STDOUT
_items=$(cd "${1}"; ls -d .!(|.)?* 1>&3) # This requires extended glob
# `shopt -s extglob` to enable
exec 3>&- # Close file descriptor
elif [ $# -gt 1 ]; then
exec 3>&1
for i in $@; do
_items=$(cd $i; echo "$i:" 1>&3; ls -d .!(|.)* 1>&3)
done
exec 3>&-
else
ls -d .!(|.)*
fi
}
shopt -s extglob
laa .
你的代码在这两种情况下都应该失败 - 只是在 5.2 下更快
$ ./src/bash-5.2.15/bash ./myscript
5.2.15(1)-release
./myscript: line 8: syntax error near unexpected token `('
./myscript: line 8: ` _items=$(cd "${1}"; ls -d .!(|.)?* 1>&3) # This requires extended glob'
$
$ /bin/bash ./myscript
5.1.16(1)-release
./myscript: line 18: syntax error near unexpected token `('
./myscript: line 18: ` ls -d .!(|.)*'
如果您必须依赖 bash 5.1 行为,那么您可以BASH_COMPAT=51
在 .bashrc 文件中的函数定义之前进行设置。