
今日私はCygwin bashシェルでファイルをすばやく編集しようとしましたが、.hgignore
間違った行を追加しました。これが最善のアプローチであるかどうかはわかりませんが、head -1 .hgignore
問題のある行を削除する方法をすばやく考えました(以前はファイルに1行しかありませんでした)。もちろん、実行されると、最初の行は唯一の出力として提供されます。
ただし、出力をリダイレクトしてrewrite fileを使用しようとすると、head -1 .hgignore > .hgignore
ファイルは空です。なぜこれが起こるのですか?貼り付けようとするとhead -1 .hgignore >> .hgignore
正しく貼り付けられますが、明らかに望ましい結果ではありません。この場合、切り捨てられたリダイレクトが機能しないのはなぜですか?
答え1
私の考えでは、ブルースが答えると思います。ここで何が起こっていますか?ハウジング配管付き。
私が好きな小さなユーティリティの1つsponge
はその他のユーティリティ。ターゲット出力ファイルを開いてデータを書き込む前に、利用可能なすべての入力を「シンク」してこの問題を解決します。これにより、意図したとおりに正確にパイプラインを作成できます。
$ head -1 .hgignore | sponge .hgignore
貧しい人々の解決策は、出力を一時ファイルにパイプし、パイプが完了した後(たとえば、次の実行コマンド)、一時ファイルを元のファイルの場所に戻すことです。
$ head -1 .hgingore > .hgignore.tmp
$ mv .hgignore{.tmp,}
答え2
シェルが次のコマンドラインを受け取ると、command > file.out
シェル自体はというファイルを開きます(作成することもできます)file.out
。シェルは、ファイル記述子 0 を開いて取得したファイル記述子に設定します。これがI / Oリダイレクトが機能する方法です。各プロセスは、ファイル記述子0、1、2について知っています。
最も難しい部分はどのように開いているfile.out
。ほとんどの場合、file.out
書き込みのためにオフセット0(つまり切り取り)で開くことを望みます。これはシェルが実行する操作です。 .hgignoreを切り取り、書き込み用に開き、ファイル記述子を0にコピーしてexecを実行しますhead
。即時ファイル破損。
Bashシェルでは、set noclobber
次のようにしてこの動作を変更できます。
答え3
存在する
head -n 1 file > file
file
head
始める前に切り捨てましたが、書いている場合:
head -n 1 file 1<> file
file
読み書きモードでは開かれません。ただしhead
、書き込みが完了するとファイルが切り捨てられないため、上記の行は機能しません(head
最初の行自体のみが書き換えられ、他の行は変更されていません)。
ただし、head
戻ってからfd
まだ開いている間は別の実行を呼び出すことができますtruncate
。
たとえば、
{ head -n 1 file; perl -e 'truncate STDOUT, tell STDOUT'; } 1<> file
ここで重要なのは、truncate
上記の内容がhead
ファイルの最初の行からfd 1にカーソルを移動するだけです。私たちに必要ではない最初の行を書き換えますが、そこには何の害もありません。
POSIX ヘッダーを使用すると、最初の行を書き換えることなく実際に外れることができます。
{ head -n 1 > /dev/null
perl -e 'truncate STDIN, tell STDIN'
} <> file
head
ここでは、標準入力でカーソル位置を移動するという事実を使用します。通常、head
入力をチャンクで読み取るとパフォーマンスが向上しますが、POSIXでは可能であればseek
最初の行を超えるとすぐに返す必要があります。ただし、すべての実装がこれを行うわけではありません。
read
または、この場合はシェルのコマンドを使用できます。
{ read -r dummy; perl -e 'truncate STDIN, tell STDIN'; } <> file
答え4
ExモードでVimを使用できます。
ex -sc '2,d|x' .hgignore
2,
2行目を最後まで選択してください。d
削除x
保存して閉じる