假设我有一个这样的脚本:
#!/bin/bash
printf '%q\n' "b c"
执行脚本打印:
b\ c
在命令行上。
现在,在包含名为的文件的目录中,b c
我想将脚本的输出传递给如下命令ls
:
$ ls $(./myscript)
这里的问题是b c
被分割为b\
和c
,即两个参数,ls
当然找不到它们。有什么办法可以规避这个问题吗?我认为转义输出中的空间就足够了。
答案1
使用引号:
$ ls "$(./myscript)"
答案2
命令替换
如果需要将命令的输出作为参数传递给另一个命令,请使用命令替换。与变量替换发生的情况类似,命令替换的结果会经历单词拆分($IFS
默认情况下在 中的每个字符处拆分为空格)和通配符扩展(文件名的通配符)。所以 ,始终在命令替换周围使用双引号除非你希望发生分裂和通配符。
ls -- "$(./myscript)"
--
告诉ls
停止处理选项,如果脚本打印的文件名以-
.
您必须打印文字文件名。不要在脚本中添加额外的引用。
实际上,有一种极端情况会导致参数混乱:命令替换会从命令输出中删除任何最后的换行符。因此,无论输出是foo↲bar
还是(我用它foo↲bar↲
来表示换行符),命令的参数都将只是。这在实践中很少发生,因为尽管文件名中允许使用换行符,但没有人使用它们。尽管如此,这可能是一个安全隐患,因为试图攻击您的系统的人可能会在文件名中放入换行符。foo↲bar↲↲↲
↲
ls
foo↲bar
要处理最后的换行符,请安排命令输出额外的非换行符,并将其从命令替换的结果中删除。
output=$(./myscript; echo a)
output="${output%a}"
ls -- "$output"
将多个文件名传递给命令
不能出现在文件名或命令行参数中的一个字符是空字节。输出多个文件名或其他要传递给命令的参数的安全方法是用空字节分隔它们并调用xargs -0
.它适用于 Linux、*BSD 和 OSX,但它是其他 UNIX 变体可能缺乏的 POSIX 扩展。如果生成的命令行太长,xargs 会将其拆分并多次调用该命令。
… | xargs -0 ls
如果没有-0
,xargs
则需要其他常用命令无法生成的输入格式。如果输入不包含空格或任何\"'
.
引用输入的一种方法xargs
是在至少以下字符之前放置反斜杠:换行符、制表符、空格和\"'
。
for x in …; do
printf '%s\n' "$x" | sed -e 's/[ '\''\"]/\\&/g' -e 's/$/\\$/' -e '$ s/\\$//'
done | xargs ls
在 sed 命令中放置一个空格和一个制表符,而不是括号内的两个空格字符。
关于不受保护的替换和引用的进一步说明
在不受保护的变量或命令替换中,对于发生通配符的每个单词,四个字符\[*?
都会扩展。然而,只有人物[*?
扳机通配符:没有其他通配符的反斜杠保持不变。因此,如果当前目录包含三个名为foo
、goo
和的文件bar
,则:
ls $(echo '?oo bar')
ls
使用参数进行调用foo
,goo
并且bar
ls $(echo '?\oo b\ar')
ls
使用参数进行调用foo
,goo
并且b\ar