stdin / stdoutリダイレクトを使用したxargs


21

実行したい:

./a.out < x.dat > x.ans

各*のための.datディレクトリ内のファイルA

もちろん、bash / python / whatevereverスクリプトで実行できますが、セクシーなワンライナーを書くのが好きです。私が到達できるのは(まだ標準出力なしで)です:

ls A/*.dat | xargs -I file -a file ./a.out

しかし、xargsの-aはreplace-str 'file'を理解しません。

ご協力ありがとうございます。


ここでの他の良い答えに加えて、GNU xargsの-aオプションを使用できます。
ジェームズヤングマン

回答:


30

まず、出力をファイルリストとして使用しないlsください。シェル拡張またはを使用しますfindls + xargsの誤用の潜在的な結果と適切なxargs使用例については、以下を参照してください。

1.簡単な方法:forループ

のファイルのみを処理する場合A/は、単純なforループで十分です。

for file in A/*.dat; do ./a.out < "$file" > "${file%.dat}.ans"; done

2. pre1なぜ   ls | xargs ですか?

仕事でを使用するlsxargs、悪いことがどのように変わるかの例を次に示します。次のシナリオを検討してください。

  • 最初に、いくつかの空のファイルを作成しましょう。

    $ touch A/mypreciousfile.dat\ with\ junk\ at\ the\ end.dat
    $ touch A/mypreciousfile.dat
    $ touch A/mypreciousfile.dat.ans
    
  • ファイルを参照し、何も含まれていないことを確認してください。

    $ ls -1 A/
    mypreciousfile.dat
    mypreciousfile.dat with junk at the end.dat
    mypreciousfile.dat.ans
    
    $ cat A/*
    
  • を使用してマジックコマンドを実行しxargsます。

    $ ls A/*.dat | xargs -I file sh -c "echo TRICKED > file.ans"
    
  • 結果:

    $ cat A/mypreciousfile.dat
    TRICKED with junk at the end.dat.ans
    
    $ cat A/mypreciousfile.dat.ans
    TRICKED
    

したがって、mypreciousfile.datとの両方を上書きすることができましたmypreciousfile.dat.ans。それらのファイルにコンテンツがあった場合、それは消去されていたでしょう。


2.使用   xargs :での適切な方法  find 

の使用を主張したい場合はxargs-0(ヌル終了名)を使用します。

find A/ -name "*.dat" -type f -print0 | xargs -0 -I file sh -c './a.out < "file" > "file.ans"'

2つのことに注意してください。

  1. このようにして、ファイル名の.dat.ans末尾を作成します。
  2. 一部のファイル名に引用符()が含まれている場合、これは壊れます"

両方の問題は、異なる方法でシェルを呼び出すことで解決できます。

find A/ -name "*.dat" -type f -print0 | xargs -0 -L 1 bash -c './a.out < "$0" > "${0%dat}ans"'

3.内で行われるすべて find ... -exec

 find A/ -name "*.dat" -type f -exec sh -c './a.out < "{}" > "{}.ans"' \;

これもまた、.dat.ansファイルを生成し、ファイル名にが含まれて"いると壊れます。それを実行するにbashは、呼び出し方法を使用して変更します。

 find A/ -name "*.dat" -type f -exec bash -c './a.out < "$0" > "${0%dat}ans"' {} \;

lsの出力を解析しないことに言及するための+1。
rahmu

2
ファイル名は「含まれているオプション2つのブレイク。
thiton

2
非常に良い点、ありがとう!それに応じて更新します。
rozcietrzewiacz

私はちょうど場合に、言及したくzshないと考えることの必要性(ファイル名などに「空白)、シェルとして使用されている(とSH_WORD_SPLITが設定されていない)すべての厄介な特殊なケースを些細な。for file in A/*.dat; do ./a.out < $file > ${file%.dat}.ans ; done作品をすべてのケースで。
jofelを

-1は、標準入力でxargsを実行する方法を理解しようとしており、私の質問はファイルやfind
Mehrdad

2

このようなことを試してください(構文は、使用するシェルによって少し異なる場合があります):

$ for i in $(find A/ -name \*.dat); do ./a.out < ${i} > ${i%.dat}.ans; done


それは機能しません。のようなものを操作しsomefile.dat.dat、すべての出力を単一のファイルにリダイレクトしようとします。
rozcietrzewiacz

あなたが正しい。ソリューションを編集して修正しました。
rahmu

OK-ほとんど良い:) somefile.dat.ans出力だけではあまり良くありません。
rozcietrzewiacz

1
編集済み!「%」については知りませんでした。ヒントのおかげで、それは魅力のように機能します。
ラーム

1
a -type fileを追加するのは良いことです(できません< directory)。これにより、unusual-filename-fairyが悲しくなります。
マット

2

単純なパターンの場合、forループが適切です。

for file in A/*.dat; do
    ./a.out < "${file}" > "${file%.dat}.ans" # Never forget the QUOTES!
done

ファイルを一覧表示するために別のユーティリティが必要なより複雑な場合(zshまたはbash 4には、ほとんど見つける必要のない強力なパターンがありますが、POSIXシェル内に留まるか、ダッシュのような高速シェルを使用する場合は、何でも見つける必要があります非自明)、読み取りが最も適切ですが:

find A -name '*.dat' -print | while IFS= read -r file; do
   ./a.out < "${file}" > "${file%.dat}.ans" # Never forget the QUOTES!
done

読み取りは(デフォルトで)行指向であるため、これはスペースを処理します。デフォルトではエスケープシーケンスを解釈するため、改行は処理されず、バックスラッシュも処理されません(実際には改行を渡すことができますが、findはその形式を生成できません)。多くのシェルには-0読み取りオプションがあり、すべての文字を処理できるシェルでは読み取り可能ですが、残念ながらPOSIXではありません。



1

xargsで少なくともシェル呼び出しが必要だと思います:

ls A/*.dat | xargs -I file sh -c "./a.out < file > file.ans"

編集:ファイル名に空白が含まれている場合、このアプローチは機能しないことに注意してください。動作しません。find -0とxargs -0を使用してxargsにスペースを正しく認識させたとしても、-cシェル呼び出しはそれらを無視します。しかし、OPは明示的にxargsソリューションを要求し、これが私が思いついた最高のxargsソリューションです。ファイル名の空白が問題になる場合は、find -execまたはシェルループを使用します。



@rozcietrzewiacz:一般的には確かですが、xargs voodooをやろうとしている誰かがこれを知っていると思います。これらのファイル名は別のシェル拡張を経るので、とにかく振る舞わなければなりません。
チトン

1
あなたはシェルの展開について間違っています。lsエスケープとせずに出力スペースのようなものをという問題があります。
-rozcietrzewiacz

@rozcietrzewiacz:はい、私はその問題を理解しています。これらのスペースを適切にエスケープしてxargsに入れると仮定します。shの-c文字列で置き換えられ、shが-c文字列をトークン化し、すべてが壊れます。
チトン

はい。おわかりのように、解析lsは良くありません。しかし、findで安全に使用xargs できます-私の答えの提案No 2を参照してください。
-rozcietrzewiacz

1

複雑にする必要はありません。forループでそれを行うことができます:

for file in A/*.dat; do
  ./a.out < "$file" >"${file%.dat}.ans"
done

この${file%.dat}.ansビットは、.datファイル名のサフィックスをファイル名から削除し$file、代わりに.ans末尾に追加します。

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