web-dev-qa-db-ja.com

コマンドshuf file> fileが空のファイルを残しているのに、同様のコマンドでは空にならないのはなぜですか?

私はこれが別の質問の複製の一種であることを知っています( なぜこの並べ替えコマンドで空のファイルが得られるのですか? )が、与えられた回答に応じて質問を拡張したいと思いました。

コマンド

shuf example.txt > example.txt

シェルはシャッフルする前にファイルを切り捨て、シャッフルする空のファイルのみを残すため、空のファイルを返します。しかしながら、

cat example.txt | shuf > example.txt

期待通りにシャッフルされたファイルを生成します。

単純なリダイレクトが機能しないのにパイプラインメソッドが機能するのはなぜですか?コマンドが実行される前にファイルが切り捨てられる場合、2番目の方法でも空のファイルを残しておくべきではありませんか?

9
toryan

問題は、> example.txtがファイルの読み取りを開始する前に、shuf example.txtがそのファイルへの書き込みを開始することです。したがって、まだ出力がなかったため、example.txtは空であり、shufは空のファイルを読み取ります。この場合、shufは出力を行わないため、最終結果は空のままです。

他のコマンドでも同じ問題が発生する可能性があります。 > example.txtは、cat example.txtがファイルの読み取りを開始する前にファイルを強制終了する場合があります。シェルがそれらを実行する順序と、実際にファイルを開くのにcatがかかる時間によって異なります。

このような問題を完全に回避するには、shuf example.txt > example.txt.shuf && mv example.txt.shuf example.txtを使用できます。

または、代わりにshuf example.txt --output=example.txtを使用することもできます。

21
frostschutz

パッケージ moreutils にはコマンドspongeがあります:

   sponge  reads  standard input and writes it out to the specified file.
   Unlike a Shell redirect, sponge soaks up all its input before  opening
   the output file. This allows constricting pipelines that read from and
   write to the same file.

その方法であなたは行うことができます:

shuf example.txt | sponge example.txt

残念ながら、moreutilsパッケージにはparallelという名前のユーティリティもあり、これはgnu parallelよりもはるかに役に立ちません。moreutilsによってインストールされたparallelを削除しました)

9
Timo

あなたはとても幸運です

cat example.txt | shuf > example.txt

このコマンドが実行しているようにexample.txtを空にしません。

shuf example.txt > example.txt

リダイレクトは、コマンドが実行され、パイプラインコンポーネントが同時に実行される前に、シェルによって実行されます。

-o/--outputオプションを使用することは、shufの最良の解決策ですが、(非常にわずかな)リスクを冒したい場合は、処理されたファイルが読み込まれる前に切り捨てられます:

shuf example.txt | (sleep 1;rm example.txt;cat > example.txt)

そして、Oleの提案のおかげで、これはより単純で高速なものです

(rm example.txt; shuf > example.txt) < example.txt
2
jlliagre

GNU bashmanual から(詳細については、セクション .7コマンドの実行 も参照):)

3.1.1シェル操作

以下は、シェルがコマンドを読み取って実行するときのシェルの動作の簡単な説明です。基本的に、シェルは次のことを行います。

  1. その入力をファイル( Shell Scripts を参照)、-c呼び出しオプションの引数として指定された文字列( Bashを呼び出す を参照)、またはユーザーの端末から読み取ります。
  2. Quoting で説明されている引用規則に従って、入力を単語と演算子に分割します。これらのトークンはmetacharactersで区切られます。エイリアスの展開はこのステップで実行されます( エイリアス を参照)。
  3. トークンを解析して単純なコマンドと複合コマンドに変換します( Shell Commands を参照)。
  4. さまざまなシェル展開( Shell Expansions を参照)を実行し、展開されたトークンをファイル名( Filename Expansion を参照)およびコマンドと引数のリストに分割します
  5. 必要なリダイレクト( Redirections を参照)を実行し、引数リストからリダイレクト演算子とそのオペランドを削除します。
  6. コマンドを実行します( コマンドの実行 を参照)。
  7. 必要に応じて、コマンドが完了するまで待機し、その終了ステータスを収集します( 終了ステータス を参照)。

ファイルが存在しない状況を考慮してください。しかし、2番目の例では、ほとんどの場合ファイルが作成されます。ファイルがそのように作成されておらず、存在せず、リダイレクトが発生しなかった場合、catはそのようなファイルがないと文句を言うでしょう...ほとんどの場合はそうではありません。 2番目のコマンドで取得したシャッフルを再現するには、すばらしい 多数の試行 が必要でした。実際、2番目の式はほとんどの場合空のファイルを残すはずです。

1
user44370

私が見つけた最も短いバージョン:

(rm foo && shuf > foo) < foo

これにより、ファイルが開き、リンクが解除されて、ファイルが切り捨てられます。これにより、ファイルを開く前に切り詰められたファイルを回避できます。これは、出力を同じファイルにリダイレクトしているときに通常表示されるものです。

0
Ole Tange

VimはExモードで使用できます。

ex -sc '%!shuf' -cx example.txt
  1. %すべての行を選択

  2. !コマンドを実行

  3. x保存して閉じる

0
Steven Penny