Bash script quote issues

2019-06-05 18:44发布

问题:

I have the following Bash script:

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)

The output:

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

I've gone through this every way I can think of, with single and double quotes, backslash and no backslash, quotes around the variable in the other lines, no dice.

If I understand this correctly, CMD should be the following:

find ~/Folder/With\ Spaces -type f

This should work fine, and since find can't use quotes around its path, this is the correct way to do it. The echo shows it matches this. Typing this string into the command line works fine. Again, the echo command prints this. But the output of the script says there's something else going on, probably on the done line where the command is being executed.

How would I get Bash to interpret the filename as one path in this context? And why? The backslash (to stop it from interpreting it as two parts separated by spaces) is being treated as part of the string, so where is it splitting it, and why?

回答1:

Bash never evaluates data as code, but it does some things that could make you think it does (namely word splitting and globbing). Since \ is part of shell syntax, it's not reinterpretted as an escape sequence when you expand the variable.

Here's how you should do it:

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