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.
我有以下脚本:
#!/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.