私はこれが別の質問の複製の一種であることを知っています( なぜこの並べ替えコマンドで空のファイルが得られるのですか? )が、与えられた回答に応じて質問を拡張したいと思いました。
コマンド
shuf example.txt > example.txt
シェルはシャッフルする前にファイルを切り捨て、シャッフルする空のファイルのみを残すため、空のファイルを返します。しかしながら、
cat example.txt | shuf > example.txt
期待通りにシャッフルされたファイルを生成します。
単純なリダイレクトが機能しないのにパイプラインメソッドが機能するのはなぜですか?コマンドが実行される前にファイルが切り捨てられる場合、2番目の方法でも空のファイルを残しておくべきではありませんか?
問題は、> 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
を使用することもできます。
パッケージ 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
を削除しました)
あなたはとても幸運です
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
GNU bash
manual から(詳細については、セクション .7コマンドの実行 も参照):)
3.1.1シェル操作
以下は、シェルがコマンドを読み取って実行するときのシェルの動作の簡単な説明です。基本的に、シェルは次のことを行います。
- その入力をファイル( Shell Scripts を参照)、-c呼び出しオプションの引数として指定された文字列( Bashを呼び出す を参照)、またはユーザーの端末から読み取ります。
- Quoting で説明されている引用規則に従って、入力を単語と演算子に分割します。これらのトークンは
metacharacters
で区切られます。エイリアスの展開はこのステップで実行されます( エイリアス を参照)。- トークンを解析して単純なコマンドと複合コマンドに変換します( Shell Commands を参照)。
- さまざまなシェル展開( Shell Expansions を参照)を実行し、展開されたトークンをファイル名( Filename Expansion を参照)およびコマンドと引数のリストに分割します
- 必要なリダイレクト( Redirections を参照)を実行し、引数リストからリダイレクト演算子とそのオペランドを削除します。
- コマンドを実行します( コマンドの実行 を参照)。
- 必要に応じて、コマンドが完了するまで待機し、その終了ステータスを収集します( 終了ステータス を参照)。
ファイルが存在しない状況を考慮してください。しかし、2番目の例では、ほとんどの場合ファイルが作成されます。ファイルがそのように作成されておらず、存在せず、リダイレクトが発生しなかった場合、cat
はそのようなファイルがないと文句を言うでしょう...ほとんどの場合はそうではありません。 2番目のコマンドで取得したシャッフルを再現するには、すばらしい 多数の試行 が必要でした。実際、2番目の式はほとんどの場合空のファイルを残すはずです。
私が見つけた最も短いバージョン:
(rm foo && shuf > foo) < foo
これにより、ファイルが開き、リンクが解除されて、ファイルが切り捨てられます。これにより、ファイルを開く前に切り詰められたファイルを回避できます。これは、出力を同じファイルにリダイレクトしているときに通常表示されるものです。
VimはExモードで使用できます。
ex -sc '%!shuf' -cx example.txt
%
すべての行を選択
!
コマンドを実行
x
保存して閉じる