Bashは、保存時に更新を実行中のスクリプトに自動的に再読み込み(注入)します。なぜですか?何か実用的ですか?


10

私はbashスクリプトを書いていて、スクリプトがwhileループでの入力を待っている間に、コードを更新(スクリプトファイルをディスクに保存)していました。ターミナルに戻って前回のスクリプトの呼び出しを続行した後、bashはファイルの構文に関するエラーを出しました。

/home/aularon/bin/script: line 58: unexpected EOF while looking for matching `"'
/home/aularon/bin/script: line 67: syntax error: unexpected end of file

だから私は次のことをやってみました:

1つ目:スクリプトを作成し、self-update.sh呼び出しましょう。

#!/bin/bash
fname=$(mktemp)
cat $0 | sed 's/BEFORE\./AFTER!./' > $fname
cp $fname $0
rm -f $fname
echo 'String: BEFORE.';

スクリプトが行うことは、コードを読み取り、「BEFORE」という単語を「AFTER」に変更してから、新しいコードで自分自身を書き直すことです。

2回実行:

chmod +x self-update.sh
./self-update.sh

第三不思議...

aularon@aularon-laptop:~$ ./self-update.sh 
String: AFTER!.

今、私は同じ呼び出しでAFTERが出力されるとは思っていませんでした、確かに2回目の実行では、最初の実行ではありません。

だから私の質問は、それは意図的なものですか?それともbashがスクリプトを実行する方法が原因ですか?行ごとまたはコマンドごと。そのような行動の良い使い方はありますか?その例は?


編集:ファイルを再フォーマットしてすべてのコマンドを1行に入れようとしましたが、現在は機能しません。

#!/bin/bash
fname=$(mktemp);cat $0 | sed 's/BEFORE\./AFTER!./' > $fname;cp $fname $0;rm -f $fname;echo 'String: BEFORE.';

出力:

aularon@aularon-laptop:~$ ./self-update.sh #First invocation
String: BEFORE.
aularon@aularon-laptop:~$ ./self-update.sh #Second invocation
String: AFTER!.

echo文字列を次の行に移動するときに、それを書き換え(cp)呼び出しから分離します。

#!/bin/bash
fname=$(mktemp);cat $0 | sed 's/BEFORE\./AFTER!./' > $fname;cp $fname $0;rm -f $fname;
echo 'String: BEFORE.';

そして今それは再び動作します:

aularon@aularon-laptop:~$ ./self-update.sh 
String: AFTER!.


1
ソースを調べましたが、これについての説明は本当に見つかりません。数年前にシェルスクリプトとして実装されたいくつかの自己解凍インストーラーを見たことがあるとは思いますが、これらのスクリプトには、実行可能なシェルコマンドのいくつかの行と、これらのコマンドによって読み取られる巨大なデータブロックが含まれています。したがって、これはこの方法で設計されていると想定します。これにより、bashがスクリプト全体をメモリに読み込む必要がなくなり、巨大な自己解凍スクリプトでは機能しなくなります。
Martin von Wittich、2014年


いいね!これらの自己インストールスクリプトを見たことを覚えていますが、AMD Catalystドライバー(独自仕様)はまだこのように出荷されており、自己解凍してインストールされます。これら、チャンクでファイルを読み取るこのような動作に依存します。例をありがとう!
aularon 2014年

回答:


12

これは仕様によるものです。Bashはチャンクでスクリプトを読み取ります。したがって、スクリプトの一部を読み取り、可能な行を実行してから、次のチャンクを読み取ります。

だからあなたはこのようなものに遭遇します:

  • Bashはスクリプトの最初の256バイト(バイト0〜255)を読み取ります。
  • その最初の256バイト内には、実行にしばらくかかるコマンドがあり、bashはそのコマンドを開始し、終了するのを待ちます。
  • コマンドの実行中、スクリプトは更新され、変更された部分は既に読み取った256バイトの後になります。
  • コマンドbashの実行が終了すると、ファイルの読み取りが続行され、元の場所から再開され、バイト256〜511が取得されます。
  • スクリプトのその部分は変更されましたが、bashはそれを知りません。

これがさらに問題になるのは、バイト256の前に何かを編集する場合です。たとえば、数行を削除するとします。次に、バイト256にあったスクリプトのデータは、別の場所、たとえばバイト156(100バイト前)にあります。このため、bashが読み続けると、元々356だったものが取得されます。

これは単なる例です。Bashは、必ずしも256バイトを同時に読み取るとは限りません。一度にどれだけ読み取るかは正確にはわかりませんが、問題ではありません。動作は同じです。


いいえ、チャンク単位で読み取りますが、前のコマンドが戻った後、次のコマンドを確実に読み取れるはずの場所に巻き戻します。
ステファンChazelas

@StephaneChazelasどこから入手したの?私はstraceを実行しましたがstat、変更されたかどうかを確認するためのファイルほどではありません。lseek呼び出しはありません。
Patrick

strace出力の関連部分については、pastie.org / 8662761を参照してください。中にがどのようににecho foo変わったかを確認してください。バージョン2まではこのように動作していたので、バージョンの問題ではないと思います。echo barsleep
ステファンChazelas

どうやらそれは行ごとにファイルを読んでいるので、私はそのファイルを試してみました。質問を編集して、その振る舞いを強調します。
aularon 2014年

@Patrick回答を更新して反映できる場合は、1行ずつ読むので、私はあなたの回答を受け入れることができます。(その問題に関する私の編集を確認してください)。
aularon 2014年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.