コマンド出力の各行にシェルコマンドを適用する方法は?


192

コマンド(などls -1)からの出力があるとします。

a
b
c
d
e
...

echoそれぞれにコマンド(たとえば)を順番に適用したいと思います。例えば

echo a
echo b
echo c
echo d
echo e
...

それをbashで行う最も簡単な方法は何ですか?


3
ls -1ここに例があるかもしれませんが、の出力を解析することは良くないことを覚えておくことは重要ですls。参照:mywiki.wooledge.org/ParsingLs
codeforester

回答:


217

おそらく最も簡単に使用できますxargs。あなたの場合:

ls -1 | xargs -L1 echo

この-Lフラグは、入力が正しく読み取られるようにします。のマニュアルページからxargs

-L number
    Call utility for every number non-empty lines read. 
    A line ending with a space continues to the next non-empty line. [...]

24
ls自動的に-1パイプで行います。
追って通知があるまで一時停止。

5
@Dennis、そのようには見えませんls | xargs -L2 echoし、ls -1 | xargs -L2 echo二つの異なる出力を与えます。前者はすべて1行になっています。
Alex Budovski、2010

2
@Alex:同じ出力が得られます。
追って通知があるまで一時停止。

6
xargsシェル関数やシェル組み込みコマンドではなく、実行可能ファイルのみを実行できます。前者の場合、最善の解決策はおそらくreadループ内のものです。
pabouk 2013

12
この回答に何の-L1ための説明が含まれているといいのですが。
Wyck、2018年

162

各行で基本的なプリペンド操作を使用できます。

ls -1 | while read line ; do echo $line ; done

または、より複雑な操作のために出力をsedにパイプすることもできます。

ls -1 | sed 's/^\(.*\)$/echo \1/'

1
sedのコマンドは動作していないよう:sh: cho: not found a sh: cho: not found取っているように見えるeのsedコマンドか何かであることをエコーで。
Alex Budovski、2010

whileループの場合は+1 。cmd1 | while read line; do cmd2 $line; done。またはwhile read line; do cmd2 $line; done < <(cmd1)、サブシェルを作成しません。これはあなたの簡易版であるsedコマンド:sed 's/.*/echo &/'
追って通知があるまで一時停止しました。

1
@Alex:二重引用符を単一引用符に変更します。
追って通知があるまで一時停止。

5
"$line"単語の分割を避けるために、whileループで引用します。
イグニス

3
を使用read -r lineしてread、エスケープされた文字が乱れるのを防ぎます。たとえば、エスケープを失ったecho '"a \"nested\" quote"' | while read line; do echo "$line"; doneを与え"a "nested" quote"ます。私たちがそうecho '"a \"nested\" quote"' | while read -r line; do echo "$line"; doneするならば、我々は"a \"nested\" quote"期待通りに得る。参照してくださいwiki.bash-hackers.org/commands/builtin/read
Warbo

9

forループを使用できます

*のファイル; 行う
   「$ file」をエコー
できた

問題のコマンドが複数の引数を受け入れる場合、問題のユーティリティを複数回ではなく1回だけ起動する必要があるため、xargsを使用するとほとんどの場合より効率的です。


1
つまり、xargsの適切で安全な使用法を説明する価値があります。printf '%s\0' * | xargs -0 ...-それ以外の場合、空白や引用符を含むファイル名を使用すると、非常に危険です
Charles Duffy

8

GNU sedであれば、実際にsedを使用してそれを行うことができます。

... | sed 's/match/command \0/e'

使い方:

  1. 一致をコマンド一致で置き換える
  2. 置換実行コマンド
  3. 置換された行をコマンド出力で置き換えます。

素晴らしい。私は最後にeコマンドを置くのを忘れていましたが、あなたの返事を見て、それはうまくいき ました。SEDが行と一致するときに、1000〜15000のランダムなIDを追加しようとしました。 cat /logs/lfa/Modified.trace.log.20150904.pw | sed -r 's/^(.*)(\|006\|00032\|)(.*)$/echo "\1\2\3 - ID `shuf -i 999-14999 -n 1`"/e'
sgsi


2

xargsは、バックスラッシュと引用符で失敗します。それは次のようなものである必要があります

ls -1 |tr \\n \\0 |xargs -0 -iTHIS echo "THIS is a file."

xargs -0オプション:

-0, --null
          Input  items are terminated by a null character instead of by whitespace, and the quotes and backslash are
          not special (every character is taken literally).  Disables the end of file string, which is treated  like
          any  other argument.  Useful when input items might contain white space, quote marks, or backslashes.  The
          GNU find -print0 option produces input suitable for this mode.

ls -1アイテムを改行文字で終了するため、trそれらをnull文字に変換します。

このアプローチは、手動で繰り返すよりも約50倍遅くなりますfor ...Michael Aaron Safyanの回答を参照)(3.55秒vs. 0.066秒)。しかし、locate、find、ファイルからの読み取り(tr \\n \\0 <file)などの他の入力コマンドの場合、xargsこのように操作する必要があります。


1

私にとってより良い結果:

ls -1 | xargs -L1 -d "\n" CMD

良いが完璧ではない。find . -mindepth 1 -maxdepth 1 -print0 | xargs -0 commandの出力がls -1があいまいなます。あなたがそれぞれにリードをしたくない場合-printf '%P\0'よりも使用して-print0ください./
Charles Duffy

1

たとえば、リストで複数のコマンドを実行するためにgawkを使用したい

ls -l | gawk '{system("/path/to/cmd.sh "$1)}'

ただし、エスケープ可能なキャラクターのエスケープは少し毛むくじゃらになります。

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