Ubuntu
使用 find exec 計算嵌套在子目錄中的 csv 文件中的行數
我想
find
對一些嵌套的 csv 文件的結果執行兩個管道命令,但我失敗了。這是想法:
$ find ./tmp/*/ -name '*.csv' -exec tail -n +2 {} | wc -l \;
為了不計算每個 CSV 文件的標題行。
該命令失敗:
wc: ';': No such file or directory find: missing argument to `-exec'
在那種情況下我真的需要做一個
for
循環嗎?例如:
$ for f in ./tmp/*/*.csv; do tail -n +2 ${f} | wc -l; done
但是這樣一來,我就失去了很好的輸出,
find
其中確實包括計數中的文件名。使用此解決方案時,我也失去了文件名:find -exec 中的管道命令?
$ find ./tmp/*/ -type f -name "*.csv" -print0 | while IFS= read -d '' f; do tail -n +2 "${f}" | wc -l; done
有點精確;當我談到列印的文件名時,這是因為我在單個文件上呼叫命令時習慣了以下結果:
$ tail -n +2 | wc -l ./tmp/myfile.csv 2434 ./tmp/myfile.csv
我使用 Ubuntu 18.04。
如果你寫
find ... -exec foo | bar \;
find
在呼叫之前,豎線由您的 shell 解釋。結果管道的左手是find ... -exec foo
,這顯然給出了“缺少’-exec’的參數”錯誤;管道的右手是bar
.保護豎線不受外殼影響,如
find ... -exec foo \| bar \;
沒有幫助,因為之後的第一個標記
-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 {} \;
然後,為了避免冒“參數列表太長”錯誤的風險,
./tmp/*/
可以重寫為find ./tmp -path './tmp/*/*' ...
或者,更準確地說,也排除
tmp
的隱藏子目錄(./tmp/*/
預設情況下可能會這樣做),如find ./tmp -path './tmp/.*' -prune -o -path './tmp/*/*' ...
最後,您可以使用更快的
-exec ... {} +
變體,它避免為任何單個找到的文件呼叫 shell。例如,用andawk
代替: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 }' {} +
(請注意,這
awk
也計算那些不以換行符結尾的格式錯誤的行,而wc
沒有)。