パイプ入力の行ごとに1回コマンドを実行しますか?


162

のすべての一致に対してjavaコマンドを1回実行したいls | grep pattern -。この場合、私はできると思いますfind pattern -exec java MyProg '{}' \;が、一般的なケースに興味があります-「標準入力のすべての行に対して一度コマンドを実行する」という簡単な方法はありますか?(魚またはバッシュで。)

回答:


92

それがそうxargsです。

... | xargs command

25
そうでもない。 printf "foo bar\nbaz bat" | xargs echo wheeになりwhee foo bar baz batます。-Lまたは-nオプションを追加しますか?
ジャンダー

3
@Jander質問はかなり一般的だったので、一般的なツールを提供しました。確かに、特定の状況に応じてオプションで動作を調整する必要があります。
キース

4
... | tr '\ n' '\ 0' | xargs -0
vrdhn

7
「質問に正しい答えを与える特定の状況」など。:)
mattdm

7
xargsでこれを行う適切な方法を確認したい場合は、以下の私の答えを参照してください。
マイケルゴールドシュテイン

167

受け入れ答えは正しい考えを持っていますが、キーは通過させることであることを意味する、スイッチを「一度出力の行ごとのコマンドを実行します。」xargs-n1

cat file... | xargs -n1 command

または、単一の入力ファイルの場合、パイプをcat完全に回避して次のように実行できます。

<file xargs -n1 command

1
また、空の場合は 実行xargsない機能も興味深いものです。標準入力に空白以外の文字が含まれていない場合は、コマンドを実行しないでください。通常、入力がない場合でもコマンドは1回実行されます。このオプションはGNU拡張機能です。stdin--no-run-if-empty -r
ローナンジュシェ

4
内部の行にどのようにアクセスしますcommandか?
BT

これは、xargsの正しい使用法です。-n1を指定しないと、パラメーターのリストをすべてではない複数の呼び出しとして扱うコマンドでのみ機能します。
masterxilo

3
printf "foo bar \ nbaz bat" | xargsの-n1エコーわーい行した単語としないことで、分割
Gismo Ranas

112

Bashまたはその他のBourneスタイルのシェル(ash、ksh、zsh、…):

while read -r line; do command "$line"; done

read -r標準入力から1行を読み取ります(バックスラッシュread-r解釈せず、必要ありません)。したがって、次のいずれかを実行できます。

$ command | while read -r line; do command "$line"; done  

$ while read -r line; do command "$line"; done <file

6
試したtail -f syslog | grep -e something -e somethingelse| while read line; do echo $line; doneところうまくいきませんでした。whileループにパイプされたファイルで動作し、tail -fでのみ動作しgrep、で動作しましたが、両方のパイプでは動作しませんでした。与えるオプションは、それが働かせたgrep--line-buffered

これは、各行をstdinに送信する必要がある場合にも機能しますcommand | while read -r line; do echo "$line" | command ; done
Den

21

Keithに同意します。xargsはこのジョブの最も一般的なツールです。

私は通常、3段階のアプローチを使用します。

  • やりたいことがあるまで基本的なことをしてください
  • awkで行を準備して、正しい構文を取得する
  • その後、おそらくbashを使用して、xargsに実行させます。

小さくて速い方法がありますが、この方法はほとんど常に機能します。

簡単な例:

ls | 
grep xls | 
awk '{print "MyJavaProg --arg1 42 --arg2 "$1"\0"}' | 
xargs -0 bash -c

最初の2行で作業するファイルを選択し、awkは実行するコマンドといくつかの引数を含む素敵な文字列を準備します。$ 1はパイプからの最初の列入力です。そして最後に、xargsがこの文字列をbashに送信して、実行するようにします。

それは少しやり過ぎですが、このレシピは非常に柔軟なので、多くの場所で私を助けました。


6
注意、xargs -0あなたのawkのprint文があるべきよう、レコードセパレータとしてnullバイトを使用していますprintf("MyJavaProg --args \"%s\"\0",$1)
グレン・ジャックマン

@glenn:null文字を逃した、答えを更新します
ヨハン

@Johanは大したことではありませんが、使用しawkている場合はパターンマッチを実行して、grep たとえばスキップすることができます。ls | awk '/xls/ {print...
Eric Renouf

15

GNU Parallelは、そのようなタスク用に作られています。最も簡単な使用法は次のとおりです。

cat stuff | grep pattern | parallel java MyProg

詳細については、紹介ビデオをご覧くださいhttp : //www.youtube.com/watch?v=OpaiGYxkSuQ


1
以下のための本当の必要はありませんcatので、ここでgrep直接ファイルを読むことができます
エリックRenouf


1
リンクのおかげで、私はそれが読むのが簡単であることに必ずしも同意しませんが、それが関係なく考慮されたことを知ってうれしいです。私は今、リンクが実際にここに適用されないことを少しだけ言します。なぜなら、代替は実際にはないが< stuff grep patterngrep pattern stuffリダイレクトや猫がまったく必要ないからです。それでも、それは重大あなたの引数を変更しないと、あなたはそれが常にで始まるパイプで物事を使用することが明確だと思うならばcat、あなたにパワー、その後、
エリックRenouf

8

また、while readフィッシュシェルでループします(タグを使用したことを考慮して、フィッシュシェルが必要であると想定しています)。

command | while read line
    command $line
end

注意すべき点はほとんどありません。

  • read-r最も一般的なユースケースを簡単にするために、引数を取らず、バックスラッシュを解釈しません。
  • $linebashとは異なり、fishは変数をスペースで区切らないため、引用符で囲む必要はありません。
  • commandそれ自体は構文エラーです(プレースホルダー引数のそのような使用をキャッチするため)。それを実際のコマンドに置き換えます。

の代わりに&whileとペアにする必要はありませんか?dodoneend
aff

@affこれは特に、異なる構文を持つ魚の殻に関するものです。
コンラッド

ああ、それ魚の意味です。
aff

6

入力引数をコマンドラインに正確に挿入する場所を制御する必要がある場合、またはそれを数回繰り返す必要がある場合は、を使用する必要がありますxargs -I{}

例#1

another_folder現在のディレクトリのサブフォルダーをミラー化する空のフォルダー構造を作成します。

    ls -1d ./*/ | xargs -I{} mkdir another_folder/{}
例2

stdinからのファイルリストに操作を適用します。この場合.html.bak拡張子を追加して各ファイルのコピーを作成します。

    find . -iname "*.html" | xargs -I{} cp {} {}.bak

xargsMacOS / BSDのmanページから:

 -I replstr
         Execute utility for each input line, replacing one or more occurrences of
         replstr in up to replacements (or 5 if no -R flag is specified) arguments
         to utility with the entire line of input.  The resulting arguments, after
         replacement is done, will not be allowed to grow beyond 255 bytes; this is
         implemented by concatenating as much of the argument containing replstr as
         possible, to the constructed arguments to utility, up to 255 bytes.  The
         255 byte limit does not apply to arguments to utility which do not contain
         replstr, and furthermore, no replacement will be done on utility itself.
         Implies -x.

Linux xargsmanページ

   -I replace-str
          Replace  occurrences of replace-str in the initial-
          arguments with names read from standard input.  Al
          so,  unquoted  blanks do not terminate input items;
          instead the separator  is  the  newline  character.
          Implies -x and -L 1.

1

潜在的にサニタイズされていない入力を処理する場合、実行する前に(特に人のメールボックスのクリーニングのような破壊的なものである場合)視覚的に検査するために、ジョブ全体を1行ずつ「スペルアウト」するのが好きです。

だから私がやっていることは、パラメータのリスト(つまり、ユーザー名)を生成し、次のように1行ごとに1つの方法でファイルにフィードすることです:

johndoe  
jamessmith  
janebrown  

次に、リストを開き、vim検索でマングルし、次のように、実行する必要がある完全なコマンドのリストを取得するまで式を置き換えます。

/bin/rm -fr /home/johndoe  
/bin/rm -fr /home/jamessmith 

このように正規表現が不完全な場合、どのコマンドに潜在的な問題があるかがわかります(例:)/bin/rm -fr johnnyo connor。この方法で正規表現を元に戻し、より信頼性の高いバージョンで再試行できます。名前のマングリングはこのことで有名です。なぜなら、ゴッホ、オコナーズ、セントクレア、スミスウェッソンなどのエッジケースをすべて処理するのは難しいからです。

set hlsearchこれを行うと便利です。vimすべての一致が強調表示されるため、一致しない場合や意図しない方法で一致する場合に簡単に見つけることができます。

正規表現が完璧になり、テスト/考えられるすべてのケースをキャッチしたら、通常はそれをsed式に変換して、別の実行のために完全に自動化できるようにします。

入力行数が原因で視覚的な検査を行えない場合は、コマンドを実行する前に画面(またはログ)にエコーすることを強くお勧めします。失敗する。その後、元の正規表現に戻ってもう一度調整できます。


0

プログラムがパイプを無視するが、引数としてファイルを受け入れる場合、特別なファイルを指すようにできます/dev/stdin

私はjavaに精通していませんが、bashでそれを行う方法の例を次に示します。

$ echo $'pwd \n cd / \n pwd' |bash /dev/stdin
/home/rolf
/

$は、bashが\n改行に変換するために必要です。理由はわかりません。



0

ここで、すぐに使用できるコピーペースト:

cat list.txt | xargs -I{} command parameter {} parameter

リストのアイテムは{}が置かれている場所に置かれ、コマンドとパラメーターの残りの部分はそのまま使用されます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.