シェルスクリプトにおける条件分岐において、case文は複数のコマンドライン引数を効率的にルーティングするための標準的な構文です。本稿では、実際のサーバー運用環境で頻繁に要求される「Webサーバープロセスの制御」と「認証情報ファイルの安全な管理」を題材に、保守性と堅牢性を考慮した実装アプローチを解説します。
Nginxプロセスの起動・停止管理スクリプト
従来のSysVinit互換スクリプトとして動作させるためには、chkconfigコメントヘッダーの定義と、標準的な終了コードの返却が必須要件となります。プロセスの実行状態は単にPIDファイルの存在を確認するだけでなく、実際にカーネル上でプロセスが生存しているかをシグナル検証で確認する構成に設計します。
スクリプト本体は以下のように実装します。
#!/bin/sh
# chkconfig: 2345 50 50
# description: Manages Nginx HTTP server lifecycle
NGINX_BIN="/usr/local/nginx/sbin/nginx"
PID_PATH="/usr/local/nginx/logs/nginx.pid"
RETURN_CODE=0
. /etc/init.d/functions
verify_status() {
if [ -s "$PID_PATH" ] && kill -0 "$(cat "$PID_PATH")" 2>/dev/null; then
return 0
fi
return 1
}
execute_start() {
if verify_status; then
echo "Nginx process is already active."
return 0
fi
$NGINX_BIN
RETURN_CODE=$?
if [ $RETURN_CODE -eq 0 ]; then
action "Starting Nginx service" /bin/true
else
action "Starting Nginx service" /bin/false
fi
return $RETURN_CODE
}
execute_stop() {
if ! verify_status; then
echo "Nginx process is not running."
return 1
fi
$NGINX_BIN -s stop
RETURN_CODE=$?
if [ $RETURN_CODE -eq 0 ]; then
action "Stopping Nginx service" /bin/true
else
action "Stopping Nginx service" /bin/false
fi
return $RETURN_CODE
}
case "$1" in
start)
execute_start
;;
stop)
execute_stop
;;
restart|reload)
execute_stop
sleep 1
execute_start
;;
*)
echo "Usage: $(basename $0) {start|stop|restart|reload}"
exit 1
;;
esac
exit $RETURN_CODE
実装上では、verify_status関数内でファイルの存在チェックとkill -0によるシグナル検証を組み合わせ、プロセス終了後の残留PIDファイルによる誤判定を防止しています。/etc/init.d/functionsが提供するactionヘルパーを呼び出すことで、システム標準の出力フォーマット(成功時は緑色の[OK]、失敗時は赤色の[FAILED])を統一しています。各操作関数は正確な終了ステータスを返すように設計されており、chkconfigや外部プロセス監視ツールとの連携安定性を確保しています。
認証リストファイルのユーザー管理スクリプト
平文で管理される認証リストに対して、安全な追加・削除・検索操作を実現するには、ファイルレベルの不変属性制御と厳密な文字列マッチングが不可欠です。特にgrepによる部分一致を排除し、意図しないレコードの誤操作を防ぐ構成を採用します。
管理スクリプトは以下のように構築します。
#!/usr/bin/env bash
set -euo pipefail
AUTH_DB="/etc/openvpn_authfile.conf"
# 管理者権限の検証
if [ "$(id -u)" -ne 0 ]; then
echo "Error: Root privileges are mandatory for database modification." >&2
exit 1
fi
usage_info() {
echo "Usage: $(basename "$0") {-add|-del|-search} <username>"
exit 2
}
[ $# -ne 2 ] && usage_info
# ファイル初期化とロック解除
unlock_db() {
touch "$AUTH_DB" 2>/dev/null
chattr -i "$AUTH_DB" 2>/dev/null || true
}
# ファイル保護と同期
lock_db() {
sync
chattr +i "$AUTH_DB" 2>/dev/null || true
}
operation="$1"
target="$2"
case "$operation" in
-add|--create)
unlock_db
# 完全一致による重複チェック
if grep -qxF "$target" "$AUTH_DB"; then
echo "Error: User '$target' already exists."
lock_db
exit 3
fi
cp -p "$AUTH_DB" "${AUTH_DB}.backup.$(date +%Y%m%d%H%M%S)"
echo "$target" >> "$AUTH_DB"
[ $? -eq 0 ] && echo "Success: Added '$target' to authentication list."
lock_db
;;
-del|--remove)
unlock_db
if ! grep -qxF "$target" "$AUTH_DB"; then
echo "Error: User '$target' not found in database."
lock_db
exit 3
fi
cp -p "$AUTH_DB" "${AUTH_DB}.backup.$(date +%Y%m%d%H%M%S)"
sed -i "/^${target}\$/d" "$AUTH_DB"
[ $? -eq 0 ] && echo "Success: Removed '$target' from authentication list."
lock_db
;;
-search|--find)
if grep -qxF "$target" "$AUTH_DB"; then
echo "Result: '$target' is currently registered."
else
echo "Result: '$target' is not present in the list."
fi
exit 0
;;
*)
usage_info
;;
esac
本スクリプトでは、grep -qxFオプションを適用し、行全体かつ固定文字列として完全一致を検索します。これにより、類似したユーザー名(例:adminとadministrator)の混同による誤削除・誤登録を完全に排除しています。ファイル書き込み処理の前後にchattr -i/+iを適用することで、root権限を持つプロセスであっても直接のecho >やrmによる破壊的変更を防ぎます。また、set -euo pipefailによる厳格なエラー制御と、タイムスタンプ付与によるバックアップ生成を組み合わせており、操作のトレーサビリティとスクリプトの実行安定性を両立させています。