【问题标题】:Bash script quote issuesBash 脚本引用问题
【发布时间】:2024-07-13 14:15:02
【问题描述】:

我有以下 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 将文件名解释为一个路径?为什么?反斜杠(以防止将其解释为由空格分隔的两部分)被视为字符串的一部分,那么它在哪里拆分它,为什么?

【问题讨论】:

    标签: bash shell escaping double-quotes


    【解决方案1】:

    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
    

    【讨论】:

    • 三件事。 1:行得通。 2:你是个天才。 3:“正确”的解决方案说服了我改用 Python。 :D
    • 我对 bash 的研究越深入,我就越喜欢有人说 Perl 看起来像线路噪音。