s.k*_*s.k 3 ubuntu pipe find exec
我想find对一些嵌套的 csv 文件的结果运行两个管道命令,但我很惨地失败了。
这是一个想法:
$ find ./tmp/*/ -name '*.csv' -exec tail -n +2 {} | wc -l \;
Run Code Online (Sandbox Code Playgroud)
为了不计算每个 CSV 文件的标题行。
该命令失败:
wc: ';': No such file or directory
find: missing argument to `-exec'
Run Code Online (Sandbox Code Playgroud)
for在这种情况下我真的需要做一个循环吗?
例如:
wc: ';': No such file or directory
find: missing argument to `-exec'
Run Code Online (Sandbox Code Playgroud)
但是这样我就失去了很好的输出,find其中确实包括计数中的文件名。
使用此解决方案时,我也丢失了文件名:find -exec 中的管道命令?
$ for f in ./tmp/*/*.csv; do tail -n +2 ${f} | wc -l; done
Run Code Online (Sandbox Code Playgroud)
一点精度;当我谈到打印的文件名时,这是因为我在单个文件上调用命令时习惯了以下结果:
$ tail -n +2 | wc -l ./tmp/myfile.csv
2434 ./tmp/myfile.csv
Run Code Online (Sandbox Code Playgroud)
我使用 Ubuntu 18.04。
如果你写
find ... -exec foo | bar \;
Run Code Online (Sandbox Code Playgroud)
垂直条在find调用之前由您的 shell 解释。结果管道的左侧是find ... -exec foo,这显然给出了“缺少`-exec'的参数”错误;管道的右手边是bar。
保护垂直条不受壳的影响,如
find ... -exec foo \| bar \;
Run Code Online (Sandbox Code Playgroud)
没有帮助,因为后面的第一个标记-exec被解释find为命令,并且所有以下标记,直到(但不包括);或+终止符,都被视为该命令的参数。
有关详细说明,请参阅了解 `find` 的 -exec 选项。
要使用管道,-exec您需要调用一个 shell。例如:
find ./tmp/*/ -name '*.csv' -exec sh -c '
printf "%s %s\n" "$(tail -n +2 "$1" | wc -l)" "$1"' mysh {} \;
Run Code Online (Sandbox Code Playgroud)
然后,为了避免冒“参数列表太长”错误的风险,./tmp/*/可以重写为
find ./tmp -path './tmp/*/*' ...
Run Code Online (Sandbox Code Playgroud)
或者,更准确地说,还要排除tmp的隐藏子目录(./tmp/*/默认情况下可能会这样做),如
find ./tmp -path './tmp/.*' -prune -o -path './tmp/*/*' ...
Run Code Online (Sandbox Code Playgroud)
最后,您可以使用更快的-exec ... {} +变体,它避免为任何单个找到的文件调用 shell。例如,用awk代替tail和wc:
find ./tmp -path './tmp/.*' -prune -o -path './tmp/*/*' \
-name '*.csv' -exec awk '
BEGIN { skip = 1 }
FNR > skip { lc[FILENAME] = (FNR - skip) }
END { for (f in lc) print lc[f],f }' {} +
Run Code Online (Sandbox Code Playgroud)
(请注意,awk还计算那些不以换行符结尾的格式错误的行,而wc不会)。