我怀疑以下问题已经得到解答,但我不知道该问题的术语,我已经足够好找到现有的答案。
我正在编写一个命令来遍历文件列表并在每行输出文件名,后跟以 P 开头的行数。到目前为止我已经得到了:
find -type f | xargs -I % sh -c '{ echo %; grep -P "^P \d+" % | wc -l; } | tr "\n" ","; echo ""; '
(实际的 find 命令有点复杂,但简单来说,它在我运行此命令的目录树中找到大约 11k 个感兴趣的文件)
这个命令大约 98% 可以满足我的目的,但我发现有一小部分文件的名称中带有括号,我无法忽略它们或用其他内容永久替换括号。
结果我收到了一些这样的案例:
sh: -c: line 0: syntax error near unexpected token `('
我知道括号是 shell 特殊字符,因此例如,如果我直接在名称中带有括号的单个文件上运行 grep,则必须将文件名括在单引号中或转义括号。我尝试交换命令中的引号类型(最外层为双引号,内层为单引号),这样我就可以将 grep 调用中的“%”放在单引号中,但这没有帮助。
有没有办法处理find -> xargs -> sh
链中的括号,以便它们在 sh 调用中得到正确处理?
答案1
最好不要将数据(文件名)直接嵌入代码(shell scriptlet)中。而是将文件名作为参数传递给您xargs
运行的 shell:
find -type f | xargs -I % \
sh -c '{ echo "$1"; grep -c -P "^P \d+" "$1"; } | tr "\n" ","; echo' sh %
此外,您应该能够使用grep -c
代替grep | wc -l
,它至少使命令更短一些。
答案2
由于您省略了.
in find . -type f
,我想您find
是 GNU find
,那么您可以执行以下操作:
find . -type f -printf %p, -exec grep -cP '^P \d' {} ';'
如果文件路径不包含:
字符,您也可以这样做(使用 GNU grep
):
grep -rcP '^P \d' . | tr : ,
如果它们可能包含:
字符但不包含换行符,则可以通过仅替换:
行中的最后一个来解决此问题,
:
grep -rcP '^P \d' . | LC_ALL=C sed 's/\(.*\):/\1,/'
该方法还可以用于:
find ... -type f -exec grep -cHP '^P \d' {} + | ...
如果您仍然需要使用find
,例如因为您有更多选择标准。
答案3
ilkkachu 的回答看起来是一个很好的改进,可能就是你应该做的。
出于信息目的,添加更轻的触摸修复以显示问题所在:
find -type f | xargs -I % sh -c '{ echo "%"; grep -P "^P \d+" "%" | wc -l; } | tr "\n" ","; echo ""; '
基本上 - 引用%
将被替换的内容。