sh で自作システムのアップデートスクリプトを書く
ユーザに実行させること
curl -fsSL https://example.com/path/to/update.sh | sh
スーパーユーザ権限が必要な場合は sudo あるいは su を実行させる。
シェルスクリプトを書く
次のような骨組みで書く。目を引くエラー表示を共通化した。
#!/bin/sh ## システム SYS_ROOT=/path/to/system ## パッチファイルのあるディレクトリ PATCH_URL="https://example.com/updates/update-201505.patch" ## エラー表示 function error_mark() { echo " " echo "################################################################" echo " ERROR" echo "################################################################" } ## メインルーチン ## function() が閉じていなければエラーとなるはずなので、エラーチェックとして利用する function update_wrap() { if ! [ -d $SYS_ROOT ]; then error_mark echo "システムがインストールされていません。" exit 1 fi ####### ####### (これからここに書いていく) ####### echo "アップデートが完了しました。" return 0 } update_wrap
メインルーチンを関数として定義しているのは、次のような危険性があるためだ。
スクリプトを取ってくる時にコネクションの問題等でスクリプトを中途半端に取ってきてしまい、 例えば
rm -rf ~/tmp/tmpdir/の様なコマンドがあった場合、これが
rm -rf ~/と言う形で実行されてしまう可能性があります。
cURLを使ったインストール方法の危険性
もしファイルが途中で途切れていた場合はシンタックスエラーになるため、中途半端に実行されることがない。
作業ディレクトリを作成する
シェルスクリプトにおいて $$ と書くと、プロセスIDが展開される。このスクリプトの場合、二重起動しないように制御しなければならないが、ここではしていない。多重起動が必要なスクリプトの備忘録として。
WORK_DIR="$SYS_ROOT/.update.$$" mkdir $WORK_DIR if ! [ -d $WORK_DIR ]; then error_mark echo "作業フォルダを作成できませんでした。" echo "システムへの書き込み権限があるかどうか確認してください。" exit 1 fi cd $WORK_DIR
アップデートファイルを取得する
curl を使う。
curl -fsSL $PATCH_URL > update.patch if [ $? -ne 0 ]; then error_mark echo "アップデートファイルを取得できませんでした。" echo "アップデートサーバがメンテナンス中の可能性があります。" exit 1 fi
オプションの意味は次の通り。
- -f (--fail): サーバエラー発生時、黙って終了する
- -s (--silent): プログレスなどのメッセージを表示しない
- -S (--show-error): -s と併用し、失敗時にはエラーメッセージを表示する
- -L (--location): HTTPリダイレクトがあった場合はリダイレクト先を取得する
パッチを作成する
diff コマンドを使う。
diff -u -r old-dir new-dir > patchfile
オプションの意味は次の通り。
パッチを適用する
patch コマンドを使う。
patch -f -s -p1 -d $SYS_ROOT < update.patch if [ $? -ne 0 ]; then error_mark echo "アップデートに失敗しました。" echo "アップデート対象バージョンかどうか確認してください。" exit 1 fi
改行コードが混在していると、1 out of 1 hunk FAILED
のようなメッセージが出てパッチが適用できない場合がある。
そのような場合、パッチの作成前のファイルとシステム上のファイル両方の改行コードが LF 以外(Windows で作業した場合 CR+LF)になっていないかチェックするとよいだろう。確認には nkf を使うことができる。
nkf -g filename
一括変換は find, sed, nkf を組み合わせて次のようにできる。
# 確認 find $SYS_ROOT -type f -name '*.php' -or -name '*.html' | sed 's/^/n kf -WwLu --overwrite /' | less # 実行 find $SYS_ROOT -type f -name '*.php' -or -name '*.html' | sed 's/^/nkf -WwLu --overwrite /' | sh
sed を使って条件を絞り込むこともできる。この例では vendor および media ディレクトリ以下のファイルを除いている。
# 確認 find $SYS_ROOT -type f -name '*.js' -or -name '*.css' | sed -e 's/^/nkf -WwLu --overwrite /' -e '/^.*\/\(vendor\|media\)\/.*$/d' | less
後片付け
最後に作業ファイルを消しておく。
rm update.patch cd $SYS_ROOT rmdir $WORK_DIR
(rm -rf は怖い)