我遇到一个问题,我想从命令中找到每条路径的长度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
允许换行的情况。