bash脚本什么时候执行语法校验?

When is syntaxic verification executed in a bash script?

我有以下脚本:

#!/bin/bash

# initialisation of the script
mkdir -p test_dir
touch test_dir/test{1..15}
touch test_dir/test{a..e}

# enabling etended glob
shopt -s extglob

# we count the number of files which name is touchNUMBER
for f in test_dir/test+([0-9]); do ((count++)); done; echo $count

它工作正常并打印 15

但是,当我尝试将此脚本连接到单行代码时,returns 出现错误:

#!/bin/bash

mkdir -p test_dir
touch test_dir/test{1..15}
touch test_dir/test{a..e}

shopt -s extglob; for f in test_dir/test+([0-9]); do ((count++)); done; echo $count

输出:

./test.sh: line 7: syntax error near unexpected token `('

似乎 bash 在确定该行语法的正确性之前没有评估 shopt -s extglob

编辑:

有趣的是,将有罪的行替换为:

shopt -s extglob; sleep 10;for f in test_dir/test+([0-9]); do ((count++)); done; echo $count

立即显示相同的错误消息,从而确认错误消息是在执行该行之前引发的。

这是为什么?有办法解决吗?

bash 逐行处理脚本。在第一种情况下,shopt -s extglob 已在解析 for 循环时执行。在错误情况下,您有一行在解析后将被识别为由 ; 分隔的两个命令。但是,这意味着当bash需要识别扩展模式+([0-9])时,shopt -x extglob还没有被执行。

没有理由在脚本中将其设为单行。一行代码旨在减少频繁执行的 interactive 命令的输入;在脚本中不需要这样做,应优先考虑可读性。

Bash 逐行读取脚本或输入。然后它解析整行,使用元字符 (| & ; ( ) < > space tab newline) 它也能识别引号和扩展。
并且,只有整条线被表征了,每个部分才开始执行。

在简单的情况下,每个命令都应该放在单独的行中。
这有效:

$ shopt -u extglob
$ shopt -s extglob
$ echo "test_dir/test"+([0-9])
test_dir/test01 test_dir/test11 test_dir/test22

虽然这不起作用:

$ shopt -u extglob
$ shopt -s extglob ; echo "test_dir/test"+([0-9])
bash: syntax error near unexpected token `('

但这还不是全部历史。如果我们设法用引号或扩展来延迟评估,该行将起作用:

$ shopt -u extglob
$ shopt -s extglob ; echo $(echo "test_dir/test"+([0-9]))
test_dir/test01 test_dir/test11 test_dir/test22

或:

$ shopt -u extglob
$ shopt -s extglob ; a=$(echo test_dir/test+([0-9])); echo "$a"
test_dir/test01 test_dir/test11 test_dir/test22

原因是子shell $(…) 在求值时继承了父shell 的条件。在子 shell 实际启动之前,父 shell 无法扩展子 shell 中的内容。

但是,正如 @chepner 所说的那样:

There is no reason to make this a one-liner in a script. One-liners are meant to reduce typing for frequently executed interactive commands; there is no need to do so in a script, where readability should be prioritized.