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 循环中使用 "$@"
吗?好吧,敲入这几个字符实在是太麻烦了,对于一些只为求快的脚本来说,如果你知道文件名中没有空格,沿用老派的 $*
语法基本没什么大碍。对于那些更稳健的脚本而言,安全起见,建议使用 "$@"
。
酷客网相关文章:
评论前必须登录!
注册