5

cygwinでは、次のコードが正常に機能します

$ cat junk
bat
bat
bat

$ cat junk | sort -k1,1 |tr 'b' 'z' > junk

$ cat junk
zat
zat
zat

しかし、Linuxシェル(GNU / Linux)では、上書きが機能しないようです

[41] othershell: cat junk
cat
cat
cat
[42] othershell: cat junk |sort -k1,1 |tr 'c' 'z'
zat
zat
zat
[43] othershell: cat junk |sort -k1,1 |tr 'c' 'z' > junk
[44] othershell: cat junk

どちらの環境もBASHを実行します。

テキスト操作を行った後、この警告のために、tmpファイルを作成せざるを得ないことがあるので、これを求めています。しかし、私はPerlで、いくつかの操作/操作の後に元のファイルを上書きするために「i」フラグを与えることができることを知っています。私が知らないファイルを上書きするためのunixパイプラインに絶対確実な方法があるかどうかを尋ねたいだけです。

4

5 に答える 5

9

ここでの4つの主要なポイント:

  1. 「猫の無駄な使い方。」そうしないでください。
  2. 実際には、 sortで何かを並べ替えているわけではありません。そうしないでください。
  3. あなたのパイプラインはあなたがそれが何をしていると思うかを言いません。そうしないでください。
  4. ファイルからの読み取り中に、ファイルをインプレースで上書きしようとしています。そうしないでください。

一貫性のない動作が発生する理由の1つは、パイプラインの出力全体をリダイレクトするのではなく、リダイレクトのあるプロセスにパイプしていることです。違いは微妙ですが、重要です。

必要なのは、コマンドグループを使用して複合コマンドを作成し、パイプライン全体の入力と出力をリダイレクトできるようにすることです。あなたの場合、これは正しく機能するはずです:

{ sort -k1,1 | tr 'c' 'z'; } < junk > sorted_junk

ソートするものがない場合は、 sortコマンドもスキップすることをお勧めします。次に、コマンドをグループ化することなく、コマンドを実行できます。

tr 'c' 'z' < junk > sorted_junk

リダイレクトとパイプラインを可能な限りシンプルに保ちます。スクリプトのデバッグがはるかに簡単になります。

ただし、何らかの理由でパイプラインを悪用したい場合は、 moreutilsパッケージのスポンジユーティリティを使用できます。マニュアルページには次のように書かれています。

スポンジは標準入力を読み取り、指定されたファイルに書き込みます。シェルリダイレクトとは異なり、スポンジは出力ファイルを開く前にすべての入力を吸収します。これにより、同じファイルの読み取りと書き込みを行うパイプラインを制限できます。

したがって、元のコマンドラインは次のように書き直すことができます。

cat junk | sort -k1,1 | tr 'c' 'z' | sponge junk

また、スポンジがパイプラインからEOFを受信するまでジャンクは上書きされないため、期待した結果が得られます。

于 2012-05-14T20:38:52.403 に答える
7

一般的に、これは壊れることが予想されます。パイプライン内のプロセスはすべて並行して起動されるため、> junk通常、行の最後にあるは、パイプラインの先頭にあるプロセスがファイルからの読み取りを終了する(または開始する)前に、入力ファイルを切り捨てます。

Cygwinの下でbashを実行しても、これを回避しましょう。これに依存するべきではありません。一般的な解決策は、一時ファイルにリダイレクトし、パイプラインが完了したときに名前を変更することです。

于 2012-05-14T15:48:43.997 に答える
3

そのファイルを編集したい場合は、エディターを使用できます。

ex junk << EOF
%!(sort -k1,1 |tr 'b' 'z')
x
EOF
于 2012-05-14T23:09:45.523 に答える
0

パイプラインで同じファイルをオーバーライドすることはお勧めできません。間違いをすると、元に戻すことができないためです(バックアップがあるか、バージョン管理下にある場合を除く)。

これは、パイプラインの入力と出力が自動的にバッファリングされるために発生します(これにより、機能しているように見えます)が、実際には並行して実行されています。プラットフォームが異なれば、(設定に基づいて)異なる方法で出力をバッファリングできるため、ファイルが空になる場合もあれば(ファイルが最初に作成されるため)、ファイルが半分完成する場合もあります。

解決策は、ファイルが完全にバッファリングされ処理された入力を持つEOFに遭遇したときにのみファイルがオーバーライドされるときに、何らかの方法を使用することです。

これは、次の方法で実現できます。

  • 出力ファイルを開く前にすべての入力を吸収できるユーティリティを使用します。

    これは、(パッケージからspongeの反対として)によって行うことができます。unbufferexpect

  • I / Oリダイレクト構文の使用は避けてください(コマンドを開始する前に空のファイルを作成する可能性があります)。

    たとえば、tee(標準ストリームをバッファリングする)を使用します。たとえば、次のようになります。

    cat junk | sort | tee junk
    

    sortこれは、すべての入力が並べ替えを処理することを想定しているため、でのみ機能します。したがって、コマンドでを使用しない場合はsort、を追加してください。

    使用できるもう1つのツールはstdbuf、バッファサイズを指定できる標準ストリームのバッファリング操作を変更するツールです。

  • ファイルをインプレースで編集できるテキストプロセッサ(sedまたはなどex)を使用します。

    例:

    $ ex -s +'%!sort -k1' -cxa myfile.txt
    $ sed -i '' s/foo/bar/g myfile.txt
    
于 2015-04-20T11:15:43.027 に答える
0

次の簡単なスクリプトを使用して、希望どおりに機能させることができます。

$ cat junk | sort -k1,1 |tr 'b' 'z' | overwrite_file.sh junk

上書きファイル.sh

#!/usr/bin/env bash

OUT=$(cat -)

FILENAME="$*"

echo "$OUT" | tee "$FILENAME"

更新されたファイルをstdoutに送信したくない場合は、代わりにこのアプローチを使用できることに注意してください。

上書きファイル_no_output.sh

#!/usr/bin/env bash

OUT=$(cat -)

FILENAME="$*"

echo "$OUT" > "$FILENAME"
于 2018-07-19T13:47:22.197 に答える