Bash Shell处理包含空格的参数列表

Bash Shell处理包含空格的参数列表,按照处理包含空格的参数中的建议,你给变量加上引号,但是仍然出现了错误。脚本和 遍历传入脚本的参数 节中的类似,如果文件名中带有空格,就会报错。

for FN in $*
do
    chmod 0750 "$FN"
done

解决方案

报错的原因与 for 循环中使用的 $* 有关。在这个示例中,我们需要用到另一个不同但相关的 shell 变量 $@。如果该变量出现在引号中,则会得到一个命令行参数列表,其中每个参数都会被单独引用起来。修改后的 shell 脚本如例所示。

#!/usr/bin/env bash
# 实例文件:chmod_all.2
#
# 在文件名包含空格时选择更好的引号添加方式,批量修改文件权限
#
for FN in "$@"
do
    chmod 0750 "$FN"
done

讨论

变量 $* 会扩展成 shell 脚本的参数列表。如果按照以下方式调用脚本:

myscript these are args

那么 $* 引用的就是这 3 个参数:these are args。当其用于 for 循环中时:

for FN in $*

第一次循环将第一个单词(these)赋给 $FN,第二次循环将第二个单词(are)赋给 $FN,以此类推。

如果参数是文件名,而且是以模式匹配的方式出现在命令行上,按以下方式调用脚本时:

myscript *.mp3

shell 会匹配到当前目录下以 .mp3 结尾的所有文件并将其传给脚本。思考以下示例,其中有 3 个 MP3 文件,名称分别为:

vocals.mp3
cool music.mp3
tophit.mp3

第二首 MP3 的文件名中的 cool 和 music 之间有一个空格。按照以下方式调用脚本时:

myscript *.mp3

实际上得到的是:

myscript vocals.mp3 cool music.mp3 tophit.mp3

如果脚本中包含以下这行:

for FN in $*

该行会扩展为:

for FN in vocals.mp3 cool music.mp3 tophit.mp3

列表中共有 4 个单词。第二首 MP3 的文件名中的第 5 个字符是空格(cool music.mp3),这导致 shell 将此文件名视为两个单词(cool 和 music.mp3),因此,在 for 循环的第二次迭代中,$FN 的值就是 cool。第三次迭代中的 $FN 的值是 music.mp3,但因为这两个文件名都不存在,所以出现了文件无法找到的错误信息。

$* 引用起来似乎行得通,但是:

for FN in "$*"

会扩展成:

for FN in "vocals.mp3 cool music.mp3 tophit.mp3"

结果就是 $FN 只得到了一个值(整个参数列表)。你会看到如下错误信息。

chmod: cannot access 'vocals.mp3 cool music.mp3 tophit.mp3': No such file or directory

因此,你得改用 shell 变量$@ 并将其放入引号。如果不加引号,$*$& 没什么两样。但当两者出现在引号中时,bash 就会区别对待了。就像先前看到的那样,"$*" 得到的是整个参数列表。而 "$@" 得到的可不是一个字符串,而是与各个参数对应的带有引号的字符串列表。
在使用 MP3 文件名的示例中:

for FN in "$@"

会扩展为:

for FN in "vocals.mp3" "cool music.mp3" "tophit.mp3"

你可以看到,现在第二首 MP3 的文件名加上了引号,其中的空格也因此成了文件名的一部分,不再被视为单词分隔符。

在第二次循环中,$FN 的值是包含空格的 cool music.mp3。因此,使用 $FN 时要小心。可能你也想将其放入引号,这样一来,其中的空格就不再作为分隔符,而是整个字符串的一部分。也就是说,应该使用 "$FN"

chmod 0750 "$FN"

难道不应该始终在 for 循环中使用 "$@" 吗?好吧,敲入这几个字符实在是太麻烦了,对于一些只为求快的脚本来说,如果你知道文件名中没有空格,沿用老派的 $* 语法基本没什么大碍。对于那些更稳健的脚本而言,安全起见,建议使用 "$@"

酷客网相关文章:

赞(0)

评论 抢沙发

评论前必须登录!