web-dev-qa-db-ja.com

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

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

176
Xodarap

それがxargsが行うことです。

... | xargs command
98
Keith

受け入れられた答え は正しい考えを持っていますが、鍵はxargsを渡すことです-n1スイッチ。これは、「出力行ごとにコマンドを1回実行する:」という意味です。

cat file... | xargs -n1 command

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

<file xargs -n1 command
193

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

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

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

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

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

私はキースに同意します。xargsはこの仕事のための最も一般的なツールです。

私は通常、3ステップのアプローチを使用します。

  • 作業したいものがなくなるまで、基本的なことを行います
  • awkで行を準備して、正しい構文を取得する
  • 次に、xargsにbashを使用して実行させます。

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

簡単な例:

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

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

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

22
Johan

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

cat stuff | grep pattern | parallel Java MyProg

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

15
Ole Tange

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

command | while read line
    command $line
end

注意すべきいくつかのポイント。

  • readはかかりません-r引数。最も一般的な使用例を簡単にするために、バックスラッシュを解釈しません。
  • 引用する必要はありません$line、bashとは異なり、fishは変数をスペースで区切りません。
  • command自体は構文エラーです(このようなプレースホルダー引数の使用をキャッチするため)。実際のコマンドに置き換えてください。
10
Konrad Borowski

潜在的にサニタイズされていない入力を処理する場合、実行する前に、ジョブ全体を1行ずつ「スペルアウト」して視覚的に確認したいと思います(特に、メールボックスのクリーニングなどの破壊的な場合)。

したがって、私が行うことは、パラメーター(つまり、ユーザー名)のリストを生成し、次のように、1行に1レコードの方法でファイルにフィードします。

johndoe  
jamessmith  
janebrown  

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

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

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

set hlsearchvimでこれを行うのに役立ちます。これは、すべての一致を強調表示するため、一致しない場合、または意図しない方法で一致する場合に簡単に見つけることができます。

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

入力の行数が原因で目視による検査ができない場合は、実行する前にコマンドを画面にエコーすることを強くお勧めします(さらに良いのはログ)。エラーが発生した場合は、どのコマンドが原因であるかを正確に把握できます。失敗する。その後、元の正規表現に戻ってもう一度調整できます。

1
Marcin

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

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

リストの項目は{}がある場所に配置され、残りのコマンドとパラメーターはそのまま使用されます。

1
DustWolf

他の答えが示すように、これはxargsで可能です。質問の「1行に1回」の部分で2つの詳細を区別する必要があります。

  1. 1回:-n 1を使用すると、各引数に対してコマンドが1回だけ呼び出されるようになります。ただし、デフォルトでは、xargsは引数がスペースで区切られていると想定します。これは、ファイルにスペースが含まれると中断します。
  2. 行ごと:-d '\n'を使用するか、tr '\n' '\0'で入力を前処理して-0を使用します。これにより、コマンドは入力のスペースに対して堅牢になります。

最終的なコマンドラインは次のようになります。

.... | xargs -n 1 -d '\n' <command>

または(trを使用)

.... | tr '\n' '\0' | xargs -n 1 -0 <command>

コマンドが一度に複数の引数を処理できる場合(grepまたはsedなど)、-n 1を省略して、多くの場合、操作を高速化できます。

0
krlmlr

プログラムがパイプを無視するが、ファイルを引数として受け入れる場合は、それを特殊ファイル/dev/stdinに指定するだけです。

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

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

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

0
Rolf

私はこれを好みます-複数行のコマンドと明確なコードを許可します

find -type f -name filenam-pattern* | while read -r F
do
  echo $F
  cat $F | grep 'some text'
done

ref https://stackoverflow.com/a/3891678/248616

0
Nam G VU