当管道查找输出时如何处理奇怪的路径字符?

当管道查找输出时如何处理奇怪的路径字符?

我遇到一个问题,我想从命令中找到每条路径的长度find。我的第一次尝试是运行这样的东西:

find . -exec sh -c "echo {} | wc -c" \;

我从这个答案中得到了这个想法。 (上面的命令是不是我的问题,我只是用它作为例子,它完全是人为的。另外,有时我可能需要多个管道。)

但是当我运行它时,输出中出现错误,可能是由于输出路径中的特殊字符造成的。不幸的是,我不知道如何解决哪些路径导致了问题,并且错误消息没有提供任何信息。不管...

我后来偶然发现了这个答案

命令find直接执行命令。该命令(包括文件名参数)不会被 shell 或任何其他可能修改文件名的东西处理。这是非常安全的。

这看起来很方便。事实上,如此方便以至于-exec sh -c ...“治愈”似乎比疾病本身更糟糕。

所以我的问题是,当我需要管道命令find和我的路径可能有特殊字符?这个问题有通用的解决方案吗我不用考虑一堆注意事项?我正在使用 bash。


注意:这是一个类似的问题:如何最好地将 find + exec 命令的输出发送到管道?区别在于,我不一定要尝试将输出传输到-exec.也就是说,如果find ... -exec ... foo {} | bar \;这是可行的方法,那对我来说完全没问题。我只是在寻找阻力最小的通用路径,命令的结构对我来说并不重要。

答案1

将文件名作为参数传递给 shell 脚本:

find . -exec sh -c 'printf "%s\n" "$1" | wc -c' sh {} \;

或者对于每个 shell 调用多个文件:

find . -exec sh -c 'for x in "$@"; do printf "%s\n" "$x" | wc -c; done' sh {} +

你的命令

find . -exec sh -c "echo {} | wc -c" \;

将在 shell 命令行上按原样插入文件名。它仅适用于不包含空格或 shell 特有字符的文件名。例如,像Don't stop me now.mp3,之类的东西this&that.txt会引起问题。 (第一个将产生一个未终止的带引号的字符串,第二个将echo在后台启动,然后尝试运行名为 的命令that.txt。)

另一方面,sh -c ... sh {} \;(或者... {} +已将find文件名作为不同的参数传递给 shell,然后它们将在位置参数中可用,并且可以在不与 shell 语法混合的情况下使用它们。("$1"对于第一个,"$@"对于整个列表。)

对于检查文件名长度的情况,您也可以"${#var}"在 shell 中获取它,只不过它给出的长度为人物根据当前区域设置,同时wc -c计数字节

答案2

尽管-exec echo {}回避处理,多个版本回声mangle 参数包含反斜杠或前导连字符。 (当然,它不会wc按照您想要的方式进行传输。)

我不会对每个路径名执行,而是wc使用一个旨在处理多个输入行(又名记录)的程序:

find . | awk '{print length}'   # basecase

# if in a multibyte locale and you want bytes not chars
# prefix the awk with LANG=C (or any other singlebyte)

# if pathname (ever) contains newline, and you have GNU find (and awk?)
find . -print0 | awk -vRS='\0' '{print length}' 

或者perl -nle 'print length'默认为字节,但我没有找到一种方法来处理-print0允许换行的情况。

相关内容