Bash脚本引用问题

时间:2014-01-28 02:39:13

标签: bash shell escaping double-quotes

我有以下Bash脚本:

DIR="~/Folder/With\ Spaces"
CMD="find $DIR -type f"
# echo showing hidden characters
echo $CMD | cat -v
while read line
do
    echo $line
done < <($CMD)

输出:

find ~/Folder/With\ Spaces -type f
find: ~/Folder/With\: No such file or directory
find: Spaces: No such file or directory

我已经通过我能想到的每一种方式,单引号和双引号,反斜杠和没有反斜杠,在其他行中的变量引用,没有骰子。

如果我理解正确,CMD应该如下:

find ~/Folder/With\ Spaces -type f

这应该可以正常工作,并且由于find不能在其路径周围使用引号,因此这是正确的方法。回声显示它与此匹配。在命令行中键入此字符串可以正常工作。同样,echo命令打印出来。但是脚本的输出表明还有其他事情发生,可能是在执行命令的done行。

如何让Bash将文件名解释为此上下文中的一个路径?为什么?反斜杠(以阻止它将它解释为由空格分隔的两个部分)被视为字符串的一部分,那么它在哪里拆分,为什么?

1 个答案:

答案 0 :(得分:5)

Bash永远不会将数据作为代码进行评估,但它会做一些可以让你认为它做的事情(即分词和通配)。由于\是shell语法的一部分,因此在展开变量时不会将其重新解释为转义序列。

这是你应该怎么做的:

DIR=~/"Folder/With Spaces"  # ~ doesn't expand in quotes, and there's no "\". 
CMD=(find "$DIR" -type f)   # use an array to keep track of words

# Print the words escaped so you know what will actually be executed
printf "%q " "${CMD[@]}" | cat -v
echo

while IFS= read -r line  # don't split or interpret words 
do
    echo "$line"         # use quotes to prevent globbing and word splitting
done < <("${CMD[@]}")    # run the command in the array without spacing issues