cat a.txt | xargs -I % echo %
上記の例では、xargsはコマンド引数としてecho %
を取ります。しかし、場合によっては、引数を1つではなく複数のコマンドで処理する必要があります。例えば:
cat a.txt | xargs -I % {command1; command2; ... }
しかしxargsはこの形式を受け入れません。私が知っている1つの解決策は、コマンドをラップする関数を定義できるということですが、それはパイプラインではないので、私はそれを好みません。別の解決策はありますか?
cat a.txt | xargs -I % sh -c 'command1; command2; ...'
これは 猫の無用な使い方 です。私はそれを書くと思います:
< a.txt xargs -I % sh -c 'command1; command2; ...'
(はい、リダイレクトはコマンドの先頭に置くことができます。)
おそらくcommand1
やcommand2
は一つ以上の%
文字を含みます。そうでなければ、xargs
への-I %
オプションへの意味があまりありません。
GNU Parallelを使えば、次のことができます。
cat a.txt | parallel 'command1 {}; command2 {}; ...; '
詳細については、紹介ビデオをご覧ください。 https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1
これはxargsやcatを使わない別のアプローチです。
while read stuff; do
command1 "$stuff"
command2 "$stuff"
...
done < a.txt
あなたが使用することができます
cat file.txt | xargs -i sh -c 'command {} | command2 {} && command3 {}'
{} =テキストファイルの各行の変数
私がすることの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よりも冗長度が低くなります。また、その動作が必要な場合は、読み取りからそれぞれのコマンドの任意の位置に値を挿入するように関数を変更することもできます。
(| sh
なしで)ドライランモードを許可するスタイルが好きです。
cat a.txt | xargs -I % echo "command1; command2; ... " | sh
パイプでも使えます:
cat a.txt | xargs -I % echo "echo % | cat " | sh
私のために働くもう一つの可能な解決策はのようなものです -
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
これは最も安全なバージョンのようです。
tr '[\n]' '[\0]' < a.txt | xargs -r0 /bin/bash -c 'command1 "$@"; command2 "$@";' ''
(-0
を削除してtr
name__をリダイレクトに置き換えることもできます(または、ファイルを代わりにヌルで区切られたファイルに置き換えることができます)。主にxargs
name__とfind
name__を-print0
の出力に組み合わせて使用します。) -0
拡張子のないxargs
name__バージョン
Argsは実行時にパラメータを配列としてシェルに渡すので安全です。 ["$@"][1]
を使用してすべてが取得されると、シェル(少なくともbash
name__)はそれらを変更されていない配列として他のプロセスに渡します。
...| 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 xargs
name__またはその他の-P
拡張子があり、32個のプロセスを並列に実行したい場合、各コマンドごとに10個以下のパラメーターを指定します。
tr '[\n]' '[\0]' < a.txt | xargs -r0 -n10 -P32 /bin/bash -c 'command1 "$@"; command2 "$@";' ''
これは入力中のどんな特殊文字に対してもロバストであるべきです。一部の行に改行が含まれていると、tr
name__バージョンで無効な入力が返されますが、改行で区切られたファイルでは避けられません。
bash -c
の空白の最初のパラメータは、これによるものです。( bash
name__の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.
これに対する私の現在の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 $>