実行中にシェルスクリプトを置き換える

実行中にシェルスクリプトを置き換える

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

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 を見つけると、次の処理が実行されます。

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

別のスクリプトに置き換えられ、同じ場所から実行されます。これを行うと、何か問題が発生する可能性がありますか?

答え1

ファイルが上書きされて置き換えられた場合(inodeは同じまま)、そのファイルを開いているプロセスは、ファイルから読み取るときに新しいデータを見ることができます。古いファイルのリンクを解除して同じ名前の新しいファイルを作成することで置き換えられた場合、inode番号は変更され、ファイルを開いているプロセスは引き続き古いファイル。

mv移動がファイルシステム間で行われるかどうかに応じて、どちらかを実行する可能性があります...完全に新しいファイルを取得するには、最初に元のファイルのリンクを解除するか、名前を変更します。次のようになります。

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。 が短い場合、シェルの読み取り位置は、ループ後の新しいスクリプトの末尾を過ぎてしまいます。)

答え2

以前にもこの問題が発生したことがあり、これが問題になる可能性があることは確かです。私の場合、回帰スクリプトは最初に git pull を実行し、実行開始後に更新される可能性があり、問題を引き起こします。

問題は通常、シェルが戻って解釈する行がまだあるかどうかをチェックするときです。これにより、目的のコードがループ内にある場合でもエラーが発生する可能性があります。これを回避するには、次の構造を使用します。この郵便受け

答え3

自己実行型、自己修正型のスクリプトですか? それは良い考えではありません。

より良い解決策は、最小限の機能を持つスタブ デーモン (つまり、スレーブ スクリプトの新しいバージョンをインストールし、それを一定間隔で呼び出す役割) を作成することです。次のようなものです... (テストされていません)

while true; do
  # check if a patch tar file exists and if yes then do patching
  if [ -f "$PATCH" ]; then
      ( cd /usr/local/mydaemon \
      && tar -xzf "$PATCH" \
      && rm -f "$PATCH" ) \
      || exit -1
  fi
  $SCRIPT
  sleep 10
done

関連情報