由于 MacOS 中扩展 glob 模式的语法错误,bash `if` 块内容无法执行

由于 MacOS 中扩展 glob 模式的语法错误,bash `if` 块内容无法执行

简洁版本:

当在我的 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- 例如参见如何全局显示除当前目录和父目录之外的所有隐藏文件


compat516.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 文件中的函数定义之前进行设置。

相关内容