Unixでは、ほとんどのエディターは、編集されたコンテンツを含む新しい一時ファイルを作成することで機能します。編集したファイルを保存すると、元のファイルが削除され、一時ファイルの名前が元の名前に変更されます。(もちろん、データ損失を防ぐためのさまざまな保護手段があります。)これは、たとえば、sed
またはperl
-i
( "in-place")フラグを呼び出されたときにです。「古い名前の新しい場所」と呼ばれるべきでした。
UNIXは(少なくともローカルファイルシステムの場合)開かれたファイルが「削除」されて同じ名前の新しいファイルが作成されたとしても、閉じられるまで存在し続けることを保証するため、これはうまく機能します。(Unixシステムがファイルを「削除」することを実際に「リンク解除」と呼ぶことは偶然ではありません。)一般的に、シェルインタープリターがソースファイルを開いており、上記の方法でファイルを「編集」する場合、元のファイルがまだ開いているため、シェルは変更を認識しません。
[注:すべての標準ベースのコメントと同様に、上記は複数の解釈の対象となり、NFSなどのさまざまなコーナーケースがあります。例外についてはコメントを記入してください。]
もちろん、ファイルを直接変更することもできます。ファイル内のデータを上書きすることはできますが、後続のデータをすべてシフトせずに削除または挿入することはできないため、編集目的にはあまり便利ではありません。さらに、そのシフトを行っている間、ファイルの内容は予測不能になり、ファイルを開いていたプロセスが影響を受けます。これを回避するには(たとえば、データベースシステムの場合と同様に)、高度な修正プロトコルと分散ロックのセットが必要です。典型的なファイル編集ユーティリティの範囲をはるかに超えるもの。
そのため、シェルによって処理されているファイルを編集する場合、次の2つのオプションがあります。
ファイルに追加できます。これは常に機能するはずです。
まったく同じ長さの新しいコンテンツでファイルを上書きできます。これは、シェルがファイルのその部分をすでに読み込んでいるかどうかに応じて、機能する場合と機能しない場合があります。ほとんどのファイルI / Oには読み取りバッファが関係しているため、また、知っているすべてのシェルが複合コマンド全体を実行する前に読み取るため、これを回避できる可能性はほとんどありません。確かに信頼できません。
ファイルの実行中にスクリプトファイルに追加する可能性を実際に必要とするPosix規格の文言は知りません。そのため、Posix準拠のすべてのシェルでは機能しません。場合によってはPOSIX準拠のシェル。だからYMMV。しかし、私が知る限り、bashで確実に動作します。
証拠として、ここで使用していますbashのビールプログラムの悪名高い99本のボトルの「ループフリー」の実装ですdd
、それは常に最後の行で現在実行中の行を、代入しているため上書きはおそらく安全である(上書きと追加します正確に同じ長さのコメント付きのファイル;最終的な結果が自己修正動作なしで実行できるように、私はそれをしました。)
#!/bin/bash
if [[ $1 == reset ]]; then
printf "%s\n%-16s#\n" '####' 'next ${1:-99}' |
dd if=/dev/stdin of=$0 seek=$(grep -bom1 ^#### $0 | cut -f1 -d:) bs=1 2>/dev/null
exit
fi
step() {
s=s
one=one
case $beer in
2) beer=1; unset s;;
1) beer="No more"; one=it;;
"No more") beer=99; return 1;;
*) ((--beer));;
esac
}
next() {
step ${beer:=$(($1+1))}
refrain |
dd if=/dev/stdin of=$0 seek=$(grep -bom1 ^next\ $0 | cut -f1 -d:) bs=1 conv=notrunc 2>/dev/null
}
refrain() {
printf "%-17s\n" "# $beer bottles"
echo echo ${beer:-No more} bottle$s of beer on the wall, ${beer:-No more} bottle$s of beer.
if step; then
echo echo Take $one down, pass it around, $beer bottle$s of beer on the wall.
echo echo
echo next abcdefghijkl
else
echo echo Go to the store, buy some more, $beer bottle$s of beer on the wall.
fi
}
####
next ${1:-99} #