入力リストが指定された各入力に対してコマンドを実行する方法

入力リストが指定された各入力に対してコマンドを実行する方法

複数のフォルダがあり、各フォルダにCDを挿入してコマンドを実行したいと思います。私はこれが次のようになるべきだと思います。

(次は私がやりたいことを人為的に単純化した例です。)

ls | xargs echo 'Looking at {}' && cd {} && /bin/do_stuff

しかし、これはうまくいきません。どうすればいいですか?

答え1

xargsコマンドが直接実行されるため動作しません。これは、1行のシェルコードも許可せず、実行のためにコマンドをシェルに渡します。実際、これらすべて&&はシェルによって解釈されます今後 ls走ってもxargs。また、{}はわかりやすい構造findですxargs

(必要に-I応じてwithを使用してxargsこの機能をまねることができますfind。)

最も簡単な方法xargsは、コマンドをシェルスクリプトに入れることです。

#!/bin/sh
echo "Looking at $1"
cd $1
/bin/do_stuff

(他のサブディレクトリのみを含むディレクトリで実行している限り、失敗しないように見え、失敗しないため、これを実際に置き換えました。 "and"動作が必要な場合は、スクリプトに必要な修正は明らか&&です。);echocd

次にシェルスクリプトを実行しますxargs。あなたがそれを呼び出す場合dostuff.sh

$ ls | xargs ./dostuff.sh

dostuff.sh同様に実行可能としてマークする必要がありますchmod +x dostuff.sh

おそらく、これらすべてを一行で書く方法がありますsh -c。しかし、このようなことをする魔法を覚えていなければ、それはただ好奇心に過ぎませんか?

答え2

xargs正しいツールですが、使いにくいです。

xargsあなたが指示しない限り、シェルを実行しないでください。オプションではなく、最初の引数で指定されたコマンドを実行し、コマンドラインに追加の引数を渡してから、標準入力から読み取られた引数を渡します。例えば

echo hello world | xargs echo 'Looking at'

走るecho 'Looking at' hello world。入力行ごとにコマンドを1回ずつ実行するには、次のようにしますxargs -L1

(echo hello; echo world) | xargs -L1 echo 'Looking at'

シェルコマンドを実行するには、明示的xargsにシェルを呼び出す必要があります。

(echo hello; echo world) | xargs sh -c 'echo "Looking at $0" && cd "$0" && /bin/do_stuff'

(あるいはtcsh -c本当にしたいのですが、今は21世紀です。)

コマンド内で補間を使用できますが、-I {}これは入力にシェル特殊文字が含まれていない場合にのみ機能することに注意してください。{}入力をコマンド($0上記)に引数として渡すことの利点は、入力について仮定が行われないことです(構文に従って行で区切られ、末尾のスペースを含まないことを除いてxargs -L)。

Bourne / POSIXスタイルのシェルでより多くの行を使用しながらよりきれいにするもう1つの方法は、シェル組み込み関数の周りにループを使用することですread

{ echo hello; echo world; } |
while IFS= read -r line; do
  echo "Looking at $line"
  (cd "$line" && /bin/do_stuff)
done

cdループ全体が同じシェルで実行されるため、サブシェルで実行しており、そのシェルの作業ディレクトリを変更したくありません。)

関連情報