ランタイム代替シェルスクリプト

ランタイム代替シェルスクリプト

「パッチ」スクリプトを実行するボードがあります。パッチスクリプトは常にバックグラウンドで実行され、次の擬似コードを実行するシェルスクリプトです。

while true; do
    # checks if a patch tar file exists and if yes then do patching
    sleep 10
done

このスクリプトは /opt/patch.sh にあり、SystemV init スクリプトによって開始されます。

問題は、スクリプトがtarを見つけたらそれを抽出し、内部に次のシェルスクリプトがあることです。パッチファイルこれはtar関連コンテンツです。

スクリプトが入っているとき/opt/patch.shtarを見つけるには:

tar -xf /opt/update.tar -C /mnt/update
mv /mnt/update/patch.sh /opt/patch.sh
exec /opt/patch.sh

自分を別のスクリプトに置き換えて、同じ場所で実行します。これを行うのに問題がありますか?

ベストアンサー1

ファイルが内部書き込みに置き換えられると(inodeは変更されません)、ファイルを開いたプロセスはファイルを読み取ると新しいデータを表示します。古いファイルのリンクを解除し、同じ名前の新しいファイルを作成して古いファイルを置き換えると、inode番号が変更され、ファイルを開いたままにしたすべてのプロセスはまだinode番号を保持します。古い文書。

mvファイルシステム間で移動が発生したかどうかに応じて、2つの操作のいずれかを実行できます。まったく新しいファイルを入手するには、まず元のファイルのリンクを解除するか、名前を変更します。このような:

mv /opt/patch.sh /opt/patch.sh.old     # or rm
mv /mnt/update/patch.sh /opt/patch.sh

これにより、移動後も実行中のシェルは、古いデータのファイルハンドルを保持し続けます。


つまり、私がテストした限り、Bashはループを実行する前にループ全体を読み取るので、デフォルトファイルへの変更は、実行がループ内に残っている限り、実行中のスクリプトを変更しません。 (フルループに影響を与えるリダイレクトが最後にある可能性があるため、実行する前にループ全体を読み取る必要があります。)

ループを終了した後、Bashは読み取りポインタをループが終了した位置に戻し、ループが終了した後に位置から入力ファイルを読み続けます。

スクリプトで定義されているすべての関数もメモリにロードされるため、スクリプトの基本ロジックを関数に入れて最後に呼び出すだけで、ファイルの変更からスクリプトが非常に安全になります。

#!/bin/sh
main() {
    do_stuff
    exit
}
main

とにかく、スクリプトがオーバーライドされると、何が起こるかをテストするのは難しくありません。

$ cat > old.sh <<'EOF'
#!/bin/bash
for i in 1 2 3 4 ; do
        # rm old.sh
        cat new.sh > old.sh 
        sleep 1
        echo $i
done
echo will this be reached?
EOF
$ cat > new.sh <<'EOF'
#!/bin/bash
echo xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
echo xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
echo xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
echo xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
EOF
$ bash old.sh

コメントアウトすると、rm old.shスクリプトは内部で変更されます。コメントがない場合は、新しいファイルが生成されます。 (この例は、new.shより大きいよりも部分的に依存しますold.sh。まるでそれが短いように、シェルはループの後に新しいスクリプトの終わりを過ぎて読みます。)

おすすめ記事