Linuxでコマンド出力行を別のコマンドとして実行する

Linuxでコマンド出力行を別のコマンドとして実行する

私は次のコマンドを実行しています:

find ./ -type f -printf "wc -l \"%p\"\n"

これにより、次の出力が提供されます。

wc -l "ANLS 457567.pl"
wc -l "ANLS 457567.pl"

それでは、行全体を別のコマンドで実行したいと思います。

この目標をどのように達成できますか?

私はそれを使用してxargs出力をパラメータとして他のコマンドに渡すことができることを知っていますが、好奇心で(コマンド出力行を別のコマンドとして実行)ことが可能です。

答え1

このようなコマンドをシェルにパイプできますが、すべてのグローブ、変数、その他の項目が拡張されるため、非常に問題が発生する可能性があります。

たとえば、アスタリスク(*、glob)またはドル記号($、変数、およびコマンド拡張)を含むファイル名は問題を引き起こします。すべての内容が正しく引用されていることを確認する必要があり、通常はファイル名自体に引用符が含まれる可能性があるため、簡単ではありません。


findただし、外部コマンド自体を実行できる場合は、次のようになります。

find ./  -type f -exec wc -l {} \;

theは{}現在のファイル名に置き換えられ、末尾(引用符)セミコロンが必要です。

少なくとも一部のバージョンは、find複数のファイル名付きコマンドを一度に実行するために使用できる代替形式をサポートして、多くの実行時間を節約します。

find ./  -type f -exec wc -l {} \+

答え2

ただシェルのSTDINに渡してください:

find ./  -type f -printf "wc -l \"%p\"\n" | sh 

または、シェルがプロセス置換をサポートしている場合は、次のようにします<()

sh <(find ./  -type f -printf "wc -l \"%p\"\n")

ただfind:

find ./  -type f -exec wc -l {} +

答え3

この場合の実際の回避策については@ilkkachuコメントを参照してください。しかしそれ以上に、シェルは各行を実行する前に変数を拡張するので、

find ./  -type f -printf "wc -l %p\n" | while read line; do
    $line
done

(たとえば、スペースなどのファイル名に特殊文字が含まれていない限り)うまく機能します。

周囲に引用符がなく、$line引用符もありません%p。それは私たちのせいです。考えるシェルは実行前に行を単語に分割します。$lineユーザーが自分で書いたように、単語をコマンドと引数として扱います。そのため、スペースは機能しません。

これは組み込みシェルを使用して防ぐことができますeval本物引用符を含むシェルにコマンドを入力したかのように機能します(今は再び表示されます)。

find ./  -type f -printf "wc -l \"%p\"\n" | while read line; do
    eval $line
done

最後の方法はすべての場合で動作しますが、まだ良い方法ではありません。eval最後の手段でなければなりません。私はこれらのオプションが機能していることをお知らせするためにこれらのオプションを含めましたが、一般的-execfind

関連情報