当文件名中有空格时,如何解析 find 命令的输出?

当文件名中有空格时,如何解析 find 命令的输出?

使用如下循环

for i in `find . -name \*.txt` 

如果某些文件名中有空格,则会中断。

我可以使用什么技术来避免这个问题?

答案1

理想情况下,你根本不会这样做,因为在 shell 脚本中正确解析文件名总是很困难(修复空格问题,你仍然会遇到其他嵌入字符的问题,特别是换行符)。这甚至被列为第一次进入在 BashPitfalls 页面中。

也就是说,有一种方法几乎可以做到你想做的事:

oIFS=$IFS
IFS=$'\n'

find . -name '*.txt' | while read -r i; do
  # use "$i" with whatever you're doing
done

IFS=$oIFS

记住$i在使用它时也要引用,以避免其他东西稍后解释空格。还请记住$IFS在使用它之后将其设置为回退,因为不这样做会导致稍后出现令人困惑的错误。

这确实有一个附加的警告:while循环内部发生的事情可能会在子 shell 中发生,具体取决于您使用的确切 shell,因此变量设置可能不会持久。for循环版本避免了这种情况,但代价是,即使您应用解决方案来避免空格问题,如果返回的文件太多,$IFS您也会遇到麻烦。find

在某些时候,解决所有这些问题的正确方法是使用 Perl 或 Python 等语言而不是 shell 来完成。

答案2

使用find -print0并将其传送到xargs -0,或者编写您自己的小型 C 程序并将其传送到您的小型 C 程序。这就是-print0-0被发明的目的。

Shell 脚本不是处理带有空格的文件名的最佳方式:您可以这样做,但它会变得笨重。

答案3

您可以将“内部字段分隔符”(IFS)设置为除空格之外的其他内容,以进行循环参数拆分,例如

ORIGIFS=${IFS}
NL='
'
IFS=${NL}
for i in $(find . -name '*.txt'); do
    IFS=${ORIGIFS}
    #do stuff
done
IFS=${ORIGIFS}

IFS在 find 中使用后重置了它,我想主要是因为它看起来不错。我没发现将它设置为换行符有什么问题,但我认为这更“干净”。

另一种方法取决于您要对 的输出执行什么操作find,要么直接使用-exec命令find,要么使用-print0并将其导入xargs -0。在第一种情况下,find负责文件名转义。在这种-print0情况下,find使用空分隔符打印其输出,然后xargs在此进行拆分。由于没有文件名可以包含该字符(据我所知),所以这也始终是安全的。这在简单情况下最有用;通常不是完整for循环的好替代品。

答案4

我不同意bash批评者的观点,因为bash,以及 *nix 工具集,非常擅长处理文件(包括名称中嵌入空格的文件)。

实际上,find它让你可以细粒度地控制选择要处理的文件...在 bash 方面,你只需要意识到你必须将字符串变成bash words;通常使用“双引号”,或其他一些机制,如使用 IFS 或 find 的{}

请注意,在大多数/许多情况下,您不需要设置和重置 IFS;只需在本地使用 IFS,如以下示例所示。这三个都可以很好地处理空格。此外,您不需要“标准”循环结构,因为找到的 \; 实际上是循环;只需将您的循环逻辑放入 bash 函数中(如果您没有调用标准工具)。

IFS=$'\n' find ~/ -name '*.txt' -exec  function-or-util {} \;  

还有两个例子

IFS=$'\n' find ~/ -name '*.txt' -exec  printf 'Hello %s\n' {} \;  
IFS=$'\n' find ~/ -name '*.txt' -exec  echo {} \+ |sed 's/home//'  

'查找also allows you to pass multiple filenames as args to you script ..(if it suits your need: use+ instead\;')

相关内容