web-dev-qa-db-ja.com

xargsを通して複数のコマンドを呼び出す

cat a.txt | xargs -I % echo %

上記の例では、xargsはコマンド引数としてecho %を取ります。しかし、場合によっては、引数を1つではなく複数のコマンドで処理する必要があります。例えば:

cat a.txt | xargs -I % {command1; command2; ... }

しかしxargsはこの形式を受け入れません。私が知っている1つの解決策は、コマンドをラップする関数を定義できるということですが、それはパイプラインではないので、私はそれを好みません。別の解決策はありますか?

265
Dagang
cat a.txt | xargs -I % sh -c 'command1; command2; ...'

これは 猫の無用な使い方 です。私はそれを書くと思います:

< a.txt xargs -I % sh -c 'command1; command2; ...'

(はい、リダイレクトはコマンドの先頭に置くことができます。)

おそらくcommand1command2は一つ以上の%文字を含みます。そうでなければ、xargsへの-I %オプションへの意味があまりありません。

382
Keith Thompson

GNU Parallelを使えば、次のことができます。

cat a.txt | parallel 'command1 {}; command2 {}; ...; '

詳細については、紹介ビデオをご覧ください。 https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1

31
Ole Tange

これはxargsやcatを使わない別のアプローチです。

while read stuff; do
  command1 "$stuff"
  command2 "$stuff"
  ...
done < a.txt
23
hmontoliu

あなたが使用することができます

cat file.txt | xargs -i  sh -c 'command {} | command2 {} && command3 {}'

{} =テキストファイルの各行の変数

17
Ossama

私がすることの1つは、この関数を.bashrc/.profileに追加することです。

function each() {
    while read line; do
        for f in "$@"; do
            $f $line
        done
    done
}

それからあなたはのようなことをすることができます

... | each command1 command2 "command3 has spaces"

xargsや-execよりも冗長度が低くなります。また、その動作が必要な場合は、読み取りからそれぞれのコマンドの任意の位置に値を挿入するように関数を変更することもできます。

14
mwm

| shなしで)ドライランモードを許可するスタイルが好きです。

cat a.txt | xargs -I % echo "command1; command2; ... " | sh

パイプでも使えます:

cat a.txt | xargs -I % echo "echo % | cat " | sh
9
brablc

私のために働くもう一つの可能​​な解決策はのようなものです -

cat a.txt | xargs bash -c 'command1 $@; command2 $@' bash

最後の 'bash'に注意してください - 私はそれがargv [0]としてbashに渡されると思います。この構文にそれがないと、各コマンドの最初のパラメータは失われます。どんなWordでも構いません。

例:

cat a.txt | xargs -n 5 bash -c 'echo -n `date +%Y%m%d-%H%M%S:` ; echo " data: " $@; echo "data again: " $@' bash
7
tavvit

これは最も安全なバージョンのようです。

tr '[\n]' '[\0]' < a.txt | xargs -r0 /bin/bash -c 'command1 "$@"; command2 "$@";' ''

-0を削除してtrname__をリ​​ダイレクトに置き換えることもできます(または、ファイルを代わりにヌルで区切られたファイルに置き換えることができます)。主にxargsname__とfindname__を-print0の出力に組み合わせて使用​​します。) -0拡張子のないxargsname__バージョン

Argsは実行時にパラメータを配列としてシェルに渡すので安全です。 ["$@"][1]を使用してすべてが取得されると、シェル(少なくともbashname__)はそれらを変更されていない配列として他のプロセスに渡します。

...| xargs -r0 -I{} bash -c 'f="{}"; command "$f";' ''を使用する場合、文字列に二重引用符が含まれていると割り当ては失敗します。これは、-iまたは-Iを使用するすべてのバリアントに当てはまります。 (文字列に置き換えられるため、入力データに予期しない文字(引用符、バッククォート、ドル記号など)を挿入してコマンドを挿入することができます。

コマンドが一度に1つのパラメータしか取ることができない場合:

tr '[\n]' '[\0]' < a.txt | xargs -r0 -n1 /bin/bash -c 'command1 "$@"; command2 "$@";' ''

あるいは、やや少ないプロセスで:

tr '[\n]' '[\0]' < a.txt | xargs -r0 /bin/bash -c 'for f in "$@"; do command1 "$f"; command2 "$f"; done;' ''

GNU xargsname__またはその他の-P拡張子があり、32個のプロセスを並列に実行したい場合、各コマンドごとに10個以下のパラメーターを指定します。

tr '[\n]' '[\0]' < a.txt | xargs -r0 -n10 -P32 /bin/bash -c 'command1 "$@"; command2 "$@";' ''

これは入力中のどんな特殊文字に対してもロバストであるべきです。一部の行に改行が含まれていると、trname__バージョンで無効な入力が返されますが、改行で区切られたファイルでは避けられません。

bash -cの空白の最初のパラメータは、これによるものです。( bashname__のmanページ から)(Thanks @clacke)

-c   If the -c option is present, then  commands  are  read  from  the  first  non-option  argument  com‐
     mand_string.   If there are arguments after the command_string, the first argument is assigned to $0
     and any remaining arguments are assigned to the positional parameters.  The assignment  to  $0  sets
     the name of the Shell, which is used in warning and error messages.
3

これに対する私の現在のBKMは

... | xargs -n1 -I % Perl -e 'system("echo 1 %"); system("echo 2 %");'

これがPerlを使っているのは残念ですが、Perlはbashよりもインストールされる可能性が低いです。しかし、それは受け入れられた答えより多くの入力を処理します。 (私はPerlに頼らないユビキタス版を歓迎します。)

@KeithThompsonの提案

 ... | xargs -I % sh -c 'command1; command2; ...'

入力にシェルのコメント文字#が含まれていない限り、最初のコマンドの一部と2番目のコマンドの一部は切り捨てられます。

入力がlsやfindなどのファイルシステムリストから派生していて、エディタが#を名前に持つ一時ファイルを作成する場合、ハッシュ#は非常に一般的です。

問題の例:

$ bash 1366 $>  /bin/ls | cat
#Makefile#
#README#
Makefile
README

おっと、これが問題です。

$ bash 1367 $>  ls | xargs -n1 -I % sh -i -c 'echo 1 %; echo 2 %'
1
1
1
1 Makefile
2 Makefile
1 README
2 README

ああ、それはそれだ:

$ bash 1368 $>  ls | xargs -n1 -I % Perl -e 'system("echo 1 %"); system("echo 2 %");'
1 #Makefile#
2 #Makefile#
1 #README#
2 #README#
1 Makefile
2 Makefile
1 README
2 README
$ bash 1369 $>  
1
Krazy Glew