find
一部のネストされたcsvファイルの結果に対して2つのパイプコマンドを実行したいが、悲惨に失敗します。
アイデアは次のとおりです。
$ 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を使用してください。
答え1
書くなら
find ... -exec foo | bar \;
パイプは呼び出し前にシェルによって解釈されますfind
。その結果、パイプの左側がありfind ... -exec foo
、パイプの右側が「 '-exec'の引数の欠落」エラーが発生しているようですbar
。
ハウジングの損傷から垂直柱を保護します。
find ... -exec foo \| bar \;
最初の次のトークンはコマンド-exec
として解釈され、終了者まで(含まれていない)までのすべての後続のトークンはそのコマンドの引数と見なされるため役に立ちません。find
;
+
バラより「find」の-execオプションについて徹底した説明のために。
パイプを使用するには-exec
シェルを呼び出す必要があります。たとえば、
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 ... {} +
最後に見つかった単一のファイルに対してシェルを呼び出さないより速いバリエーションを使用できます。たとえば、代わりに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 }' {} +
(awk
改行文字で終わらない誤った行も計算されますwc
。)
答え2
必要なものがそれぞれから1を引くことであれば、wc -l
これは非常に簡単です。
find [whatever you want] -exec wc -l {} + | perl -pe 's/(\d+)/$1-1/e'