我试图回显所有文件,/foo
同时保留列出的目录中的空格。目前,标题为 的目录"bar1 bar2"
将回显为/path/to/foo/bar1/
然后/bar2
然后file.txt
全部在新行上。我需要它回显/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
还尝试过:
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
答案1
问题不在于$file
,而在于您对 的使用$(find…)
。
常规变量和$(…)
进程替换都遵循完全相同的分词规则。如果你使用$(find…)
双引号,则会得到单身的单词全部输出。如果不这样做,它将在任何空格处拆分 –不是只是换行符或者其他一些你可能预料到的神奇边界。
上述内容的一个重要部分是反斜杠转义符不是扩展变量时进行处理。它只是在空格处进行拆分,仅此而已。
(换句话说,引用$file
没有帮助,因为它从一开始就没有正确的值!)
如果您想递归处理所有文件,您的选择是:
find
以另一种方式读取输出:find … -print | while read -r file; do echo "Got $file" done
(附注:文件名可能技术上也包括换行符,因此您可能需要防范这种情况,尽管这种情况很少见:
find … -print0 | while read -r -d "" file; do echo "Got $file" done
对于少量文件,使用 bash 的扩展通配符:
shopt -s globstar for file in /path/to/foo/**; do echo "Got $file" done
对于大型目录, 的优势find | while read
在于它是流式传输的 – 您的脚本将在结果出现时进行处理。同时,$(find)
和 通配符都必须收集一切存入内存,然后才返回(可能大量的)结果。
另一方面,使用管道会影响全部的 while
循环(而不仅仅是命令read
),所以如果你想运行任何事物想要读取键盘输入,您必须手动为其提供原始标准输入,例如vim "$file" < /dev/tty
。