概要
bash はシェルスクリプトを最初にすべて読み込むわけではなく、読み込みながら実行します。
そのため実行中のシェルスクリプトを更新する場合、正しい手段を選択する必要があります。
さらに cron などで定期実行中のシェルスクリプトを更新する場合は、実行状況やタイミングをできるだけ気にせず安全に済ませたいです。
本記事は以下の記事を参考にしています。
- 【Bash】スクリプト実行中にスクリプトを書き換えてみる
- Tips: 実行中のシェルスクリプトを書きかえるときには
- 外部サイト:実行中のBashシェルスクリプトを書き換えると挙動が変わる問題とその対処法
- 外部サイト:bash スクリプトの実行中上書き動作について
- 外部サイト:そのファイル、安全に更新できていますか?(アトミックなファイル操作:前編)
- 外部サイト:inodeから見たmvやcpの動き
- 外部サイト:Linuxで読込中のファイル削除の影響
上記の記事を参考に自分でも色々と試した結果を元に本記事を書いています。
間違いの指摘やより良い方法の提案などのコメントを歓迎します。
前提
以下の状況を想定しています。
- git で管理しているシェルスクリプト群をサーバに反映したい。
- シェルスクリプトを cron で定期実行している。
サーバはざっくり Linux を想定します。
具体的には Amazon Linux と、ローカル環境になりますが Windows の WSL2(Ubuntu) で実験した結果を元にしています。
参考記事によるとシェルスクリプトの動きは OS により多少の違いがあったりもするようですし、その他についても環境によっては本記事と異なる場合があります。
結論
git で管理しているシェルスクリプト群をサーバに安全にリリースする方法として以下の方法を考えます。
- シェルスクリプト群を一時ディレクトリに pull してから本ディレクトリに rsync で反映する。
- 一時ディレクトリと本ディレクトリは同一ファイルシステム上に確保する。
ただし、シェルスクリプト間に依存があり、ファイル更新のタイミングのズレが許容できないケースには対応できません。
例えば処理用シェルスクリプトからパラメータ用シェルスクリプトを実行しているようなケースでその両方を同時に更新する場合、2つのシェルの更新タイミングには隙間が発生します。
その隙間に cron による定期実行が行われる可能性があります。
それを許容できない場合は、cron による定期実行を停止し、実行状況を確認の上で実施するなどの適切な対応を採る必要があります。
解説
単純な git pull はダメ
関連記事などによると実行中シェルスクリプトを影響なく更新するためには既存ファイルの i-node とは別の i-node となるような方法で置き換えれば良いとのことです。要は別のファイルの実体で置き換えてくださいという意味です。
具体的な方法として、更新後の状態のファイルを一度別で作ってから既存のファイルに mv で上から被せるという方法があります。
似たコマンドでも cp による上書きは i-node が維持されるためこれを満たしません。
git pull によるファイル更新を実験したところ cp と同じく i-node は維持されることを確認しました。従って、git pull による直接反映は好ましくありません。
rsync が使いやすそう
rsync は mv と同じような動きをします。
rsync によるローカル物理ファイルの同期は一時ファイルコピーを作ってから mv と同じく rename システムコールでファイルを置き換えます。
rsync には --inplace オプションがあり、これを指定した場合は cp と同じような動きになります。--inplace オプション未指定であれば上記の通り mv と同じような動きとなります。
一時ディレクトリから本ディレクトリへのシェルスクリプト群の一括反映を比較的簡単に書くことができます。
--delete オプションも指定すればシェルスクリプトの削除も反映できます。
実行中のシェルスクリプトを削除しても影響はありません。読込中ファイルの実体を Linux は裏で維持します。
調べていてこの事実には少し驚きましたが、実行中のファイルを置き換えても旧シェルスクリプトが継続して動作するのは同一の仕組みによるものなのだと思います。
cron による定期実行のタイミングを気にする必要がない理由
rsync が内部で使用している rename システムコールはアトミック(並列実行による割り込み不可)な操作であるため cron による定期実行のタイミングを気にする必要はありません。
ただし、rename システムコールは変更元と変更先のファイルシステムが同一であることが条件のようなのでその点は注意しましょう。
条件を満たさない場合、rename システムコールとは別の手段が採用され、ファイル削除とファイル名変更の処理の間に隙間が生まれます。
タイミング次第で cron による定期実行の失敗が予想されます(これ未検証です)。
また、アトミック性は単一ファイルの操作に閉じているため複数ファイルの更新タイミングのズレを防ぐことはできません。
備考
ls の -i オプションを使うことでファイルの i-node 番号の確認ができることを今回初めて知りました。
試行錯誤するにあたってとても便利でした。