STDIN 内の行をループしてシェル コマンドを実行するにはどうすればよいでしょうか?

STDIN 内の行をループしてシェル コマンドを実行するにはどうすればよいでしょうか?

STDIN から取得した各行に対してシェル コマンドを実行したいと思います。

この場合、 を実行したいと思いますxargs mv。たとえば、次の 2 行があるとします。

mfoo foo
mbar bar

実行したい内容:

xargs mv mfoo foo
xargs mv mbar bar

ruby、、awkおよびで次の戦略を試しましたxargs。ただし、やり方が間違っています。

ただxargs

$ echo "mbar bar\nmbaz baz" | xargs mv
usage: mv [-f | -i | -n] [-v] source target
       mv [-f | -i | -n] [-v] source ... directory

を通してawk

$ echo "mbar bar\nmbaz baz" | awk '{ system("xargs $0") }'

を通してruby

$ echo "mbar bar\nmbaz baz" | ruby -ne '`xargs mv`'
$ ls
cat  foo  mbar mbaz

いくつか質問があります:

  • 私がやろうとしていることをどうやってやればいいのでしょうか?
  • 私のそれぞれの試みの何が間違っているのでしょうか?
  • 自分がやろうとしていることを「考える」ためのより良い方法はあるでしょうか?

xargs特に、次の方法が機能するため、私の試みが機能しないことに困惑しています。

$ echo "foo\nbar" | xargs touch
$ ls
bar foo

答え1

echo "mbar bar\nmbaz baz" | xargs mv

では、何が起こっているかを確認するには オプションをxargs使用する必要があります。上記のケースでを呼び出すと、何が表示されますか。-txargs-t

mv mbar bar mbaz baz

明らかに、これは正しくありません。xargs空腹のワニのように、パイプ経由で によって渡されたすべての引数を食べてしまったのですecho。したがって、ワニへの引数の解放を制限する方法が必要です。また、行ごとに要求したので、POSIX オプションの-lまたは が必要です。-L

echo "mbar bar\nmbaz baz" | xargs -l -t mv

POSIX 的な方法:

echo "mbar bar\nmbaz baz" | xargs -L 1 -t mv

mv mbar bar
mv mbaz baz

そしてこれがあなたが望んでいたものなのです。HTH

答え2

次の方法で行けます:

echo -e "foo bar\ndoo dar" | xargs -n 2 mv

ファイルから読み取るには:

cat lines.txt | xargs -n 2 mv

または

xargs -a lines.txt -n 2 mv

答え3

xargs期待通りに動作しない可能性があります。この質問に対する @MichaelHomer のコメントを参照してください:

xargs mv mfoo foo入力を待ち、入力されたmfoo foo $x_1 $x_2 $x_3...行ごとにmv を実行します。@MichaelHomer$x_n

これは、xargsマニュアルページ:

説明
xargs ユーティリティは、標準入力からスペース、タブ、改行、およびファイル終了で区切られた文字列を読み取り、その文字列を引数として utility を実行します。

実際には、xargs最初の引数として指定された引数をコマンドラインユーティリティに渡すときに、改行は無視されるようです。たとえば、改行に何が起こるかに注目してください。

$ echo "foo bar\ncat dog"
foo bar
cat dog
$ echo "foo bar\ncat dog" | xargs echo
foo bar cat dog

この動作は、-nフラグ:

-n 番号


ユーティリティの各呼び出しで標準入力から取得される引数の最大数を設定します。

前の例に戻って、xargs引数を2つだけ取って終了したい場合は、次のようにします。@ddnomad のアプローチ:

$ echo "foo bar\ncat dog" | xargs -n 2 echo
foo bar
cat dog

また、Rakesh Sharma の回答のコメントに示されているように、BSD ではxargsフラグを使用して読み取る行数を指定できます-L。man ページから:

-L番号

読み取られた行数ごとにユーティリティを呼び出します。EOF に到達し、
読み取られた行数が数値より少ない場合は、利用可能な行数でユーティリティが呼び出されます。

テストには-tスイッチも同様に役立ちます:

例をもう一度見てみると、次のどちらも機能しますが、この場合、意図していたのは 2 番目の例であるため、2 番目の例の方が適しています。

$ echo "foo bar\ncat dog" | xargs -n 2 echo
foo bar
cat dog
$ echo "foo bar\ncat dog" | xargs -L 1 echo
foo bar
cat dog

man ページからの注記:

-L オプションと -n オプションは相互に排他的であり、最後に指定されたオプションが使用されます。

答え4

xargs を使用しない非常に簡単な解決策があります。
次の関数を定義します。

$ mv2() { while (($# >1 )); do echo mv "$1" "$2"; shift 2; done; }

次に、必要なファイル名のリストを指定して呼び出します (改行は不要)。

$ mv2 mbar bar mbaz baz
mv mbar bar
mv mbaz baz

関数を実際に動作させるには、エコーを削除します。

関連情報