带空格的 Bash 变量(目录)

sup*_*ted 6 linux bash shell ubuntu bash-scripting

我试图在/foo保留列出目录中的空格的同时回显所有文件。目前,名为目录"bar1 bar2"将回声出作为/path/to/foo/bar1/然后/bar2file.txt全部上新的生产线。我需要它来 echo/path/to/foo/bar1 bar2/file.txt/path/to/foo/bar1\ bar2/file.txt.

for file in $(find /path/to/foo/ -type f); do
  if [[ ... ]]; then
    echo "${file}"
  fi
done
Run Code Online (Sandbox Code Playgroud)

还试过:

for file in $(find /path/to/foo/ -type f | sed 's/ /\ /g'); do
  if [[ ... ]]; then
    echo "${file}"
    echo "$file" # variations i've tried
    echo $file   # variations i've tried
    echo ${file}
    otherCommand "${file}"
  fi
done
Run Code Online (Sandbox Code Playgroud)

use*_*686 16

问题不在于 ,而在于$file您对$(find…).

常规变量和$(…)过程替换都受到完全相同的分词。如果你把$(find…)在双引号,你会得到一个单一的与所有输出字。-如果你不这样做,它会在任何空白被分割只是换行或其他一些神奇的边界,你可能期望。

上面的一个重要部分是在扩展变量时处理反斜杠转义。它只是在空格处分开,仅此而已。

(换句话说,引用$file没有帮助,因为它从来没有正确的值!)

如果要递归处理所有文件,您的选择是:

  1. find以另一种方式读取输出:

    find … -print | while read -r file; do
        echo "Got $file"
    done
    
    Run Code Online (Sandbox Code Playgroud)

    (旁注:文件名在技术上也可能包含换行符,因此您可能需要防范这种情况,尽管这种情况很少见:

    find … -print0 | while read -r -d "" file; do
        echo "Got $file"
    done
    
    Run Code Online (Sandbox Code Playgroud)
  2. 对于少量文件,使用 bash 的扩展通配符:

    shopt -s globstar
    for file in /path/to/foo/**; do
        echo "Got $file"
    done
    
    Run Code Online (Sandbox Code Playgroud)

对于大型目录,优点find | while read是它是流式传输的——您的脚本将在结果出现时对其进行处理。同时,$(find)通配符和通配符都必须首先将所有内容收集到内存中,然后才返回(可能是大量的)结果。

另一方面,使用管道会影响整个 while循环(不仅仅是read命令),所以如果你想运行任何想要读取键盘输入的东西,你必须手动为它提供原始标准输入,例如vim "$file" < /dev/tty.

  • 最好使用`... | IFS=读...`; 否则,`read` 将删除前导和尾随空格(这很少见,但在文件名中完全合法)。另请注意,管道使循环在 s 子shell 中运行,因此在其中设置的任何变量在循环外都将不可用。有关更多提示和技巧,请参阅 [BashFAQ #20:“如何找到并安全地处理包含换行符、空格或两者的文件名?”](http://mywiki.wooledge.org/BashFAQ/020)。 (3认同)