使用 find exec 计算嵌套在 subdir 中的 csv 文件中的行数

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。

fra*_*san 5

如果你写

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代替tailwc

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不会)。