晴耕雨読

working in the fields on fine days and reading books on rainy days

find -exec でパイプを介した複数コマンドの実行

find -exec の中でパイプを使って複数コマンドを実行する方法について説明します。

ここでは、カレントディレクトリ以下にあるファイルの中身を全て小文字にするコマンドを書くことを例に説明していきます。 一つのファイルだけであれば、次のコマンドでできます。

cat input.txt | tr 'A-Z' 'a-z' > input.txt.lower

これをそのまま、find -exec で実行しようとしても上手く動きません。

# 動かない例
find . -type f -name '*.txt' -exec cat {} | tr 'A-Z' 'a-z' > {}.lower \;

原因は -exec の後ろには1つのコマンドしか取れないからです。 代わりに、sh -c "実行したいコマンド" と書くことで複数のコマンドを一つのコマンドにまとめることで動くようになります。

find . -type f -name '*.txt' -exec sh -c "cat {} | tr 'A-Z' 'a-z' > {}.lower" \;

ワンライナー以外の回答

もしシェルスクリプトを書いているなら、あえてワンライナーにする必要はないと思います。 複数のコマンドを一つのコマンドにまとめるという点で関数を使う方法があります。

tolower() {
    cat "$1" | tr 'A-Z' 'a-z' > "$1".lower
}
find . -type f -name '*.txt' -exec tolower {} \;

その他に挙げるとすれば、-exec を使わないで for-in を使う方法もあります。 個人的には、この書き方が一番可読性が高いと思います。

for file in $(find . -type f -name '*.txt'); do
    cat "$file" | tr 'A-Z' 'a-z' > "$file".lower
done

以上です。