Linuxシェルスクリプト入門
日常業務を自動化する
Bashシェルスクリプトの基本構文から実践的な自動化スクリプトまで解説。変数、条件分岐、繰り返し、関数を使って日常業務を効率化しましょう。
こんな人向けの記事です
- シェルスクリプトを初めて書く
- 日常のLinux作業を自動化したい
- Bashの基本構文を体系的に学びたい
Step 1シェルスクリプトとは
シェルスクリプトは、Linuxのコマンドをファイルにまとめて一括実行するためのプログラムです。普段ターミナルで手動実行しているコマンドを自動化できます。
シェルスクリプトのメリット:
- 作業の自動化: 定型作業をワンコマンドで実行
- ミスの防止: 手動入力によるヒューマンエラーを削減
- 再現性: 同じ手順を何度でも正確に再実行
- 記録: 手順がスクリプトとして残るためドキュメント代わりになる
最初のスクリプトを書く
シェルスクリプトの1行目にはシバン(shebang)を記述します。これはスクリプトをどのシェルで実行するかを指定する宣言です。
hello.sh
#!/bin/bash # これはコメントです echo "Hello, Shell Script!" echo "現在の日時: $(date)" echo "現在のユーザー: $(whoami)" echo "現在のディレクトリ: $(pwd)"
スクリプトの実行方法
ターミナル
# 実行権限を付与 chmod +x hello.sh # 方法1: 直接実行 ./hello.sh # 方法2: bashコマンドで実行(権限付与不要) bash hello.sh # 方法3: sourceで現在のシェルで実行 source hello.sh
注意:
./hello.sh で実行するには、必ず chmod +x で実行権限を付与してください。権限がないと「Permission denied」エラーになります。
| 実行方法 | 特徴 | 用途 |
|---|---|---|
./script.sh | サブシェルで実行 | 一般的なスクリプト実行 |
bash script.sh | 権限不要でサブシェル実行 | テスト・デバッグ時 |
source script.sh | 現在のシェルで実行 | 環境変数の設定など |
Step 2変数と環境変数
変数はデータを格納する入れ物です。Bashでは変数の宣言に型指定は不要で、すべて文字列として扱われます。
変数の基本
variables.sh
#!/bin/bash
# 変数の代入(=の前後にスペースを入れない)
name="Taro"
age=25
message="こんにちは"
# 変数の参照($を付ける)
echo "名前: $name"
echo "年齢: ${age}歳"
echo "$message, ${name}さん!"
# コマンドの結果を変数に格納
today=$(date +%Y-%m-%d)
file_count=$(ls | wc -l)
echo "今日の日付: $today"
echo "ファイル数: $file_count"
注意: 変数の代入時に
= の前後にスペースを入れるとエラーになります。name = "Taro" は間違いで、name="Taro" が正しい書き方です。
特殊変数
| 変数 | 意味 | 例 |
|---|---|---|
$0 | スクリプト名 | ./backup.sh |
$1, $2... | 引数(1番目、2番目...) | ./backup.sh /home |
$# | 引数の個数 | 3 |
$@ | すべての引数(個別) | "arg1" "arg2" "arg3" |
$? | 直前のコマンドの終了ステータス | 0(成功) |
$$ | 現在のプロセスID | 12345 |
環境変数
env_vars.sh
#!/bin/bash # よく使う環境変数 echo "ホームディレクトリ: $HOME" echo "ユーザー名: $USER" echo "パス: $PATH" echo "シェル: $SHELL" echo "ホスト名: $HOSTNAME" # 環境変数の設定(exportで子プロセスに引き継ぐ) export MY_APP_ENV="production" export MY_APP_PORT=8080 # 環境変数の確認 env | grep MY_APP # 変数と環境変数の違い local_var="ローカル" # このシェルだけで有効 export global_var="グローバル" # 子プロセスでも有効
変数と環境変数の違い:
- 変数: 現在のシェルでのみ有効。子プロセスには引き継がれない
- 環境変数:
exportで宣言し、子プロセスにも引き継がれる - 永続化:
~/.bashrcや~/.bash_profileに記述すると起動時に自動設定
Step 3条件分岐(if・case)
条件分岐を使うと、状況に応じて異なる処理を実行できます。ファイルの存在確認やコマンドの成否判定などに活用します。
if文の基本
condition.sh
#!/bin/bash
# 基本的なif文
score=85
if [ $score -ge 80 ]; then
echo "優秀です"
elif [ $score -ge 60 ]; then
echo "合格です"
else
echo "再試験です"
fi
# 文字列の比較
name="admin"
if [ "$name" = "admin" ]; then
echo "管理者としてログイン"
fi
# ファイルの存在確認
if [ -f "/etc/hosts" ]; then
echo "/etc/hosts は存在します"
fi
if [ -d "/var/log" ]; then
echo "/var/log ディレクトリが存在します"
fi
テスト演算子
| カテゴリ | 演算子 | 意味 |
|---|---|---|
| 数値比較 | -eq | 等しい(equal) |
-ne | 等しくない(not equal) | |
-gt | より大きい(greater than) | |
-ge | 以上(greater or equal) | |
-lt | より小さい(less than) | |
-le | 以下(less or equal) | |
| 文字列比較 | = | 等しい |
!= | 等しくない | |
-z | 空文字列 | |
| ファイル | -f | 通常ファイルが存在する |
-d | ディレクトリが存在する | |
-r | 読み取り可能 | |
-w | 書き込み可能 |
case文
case文は、1つの値に対して複数のパターンマッチングを行う場合に便利です。
case_example.sh
#!/bin/bash
# 引数で動作を切り替えるスクリプト
case "$1" in
start)
echo "サービスを起動します"
;;
stop)
echo "サービスを停止します"
;;
restart)
echo "サービスを再起動します"
;;
status)
echo "サービスの状態を確認します"
;;
*)
echo "使い方: $0 {start|stop|restart|status}"
exit 1
;;
esac
# ファイル拡張子で処理を分岐
filename="report.pdf"
case "$filename" in
*.txt)
echo "テキストファイルです" ;;
*.pdf)
echo "PDFファイルです" ;;
*.jpg|*.png|*.gif)
echo "画像ファイルです" ;;
*)
echo "不明なファイル形式です" ;;
esac
Step 4繰り返し(for・while)
繰り返し処理は、複数のファイルに対する一括操作やログの監視など、自動化の中核となる構文です。
for文
for_loop.sh
#!/bin/bash
# リストを使ったfor文
for fruit in apple banana cherry; do
echo "フルーツ: $fruit"
done
# 連番の生成
for i in {1..5}; do
echo "番号: $i"
done
# ステップ付き連番(1から10まで2刻み)
for i in {1..10..2}; do
echo "奇数: $i"
done
# ファイルを一括処理
for file in /var/log/*.log; do
echo "ログファイル: $file ($(wc -l < "$file") 行)"
done
# C言語スタイルのfor文
for ((i=0; i<5; i++)); do
echo "カウント: $i"
done
while文
while_loop.sh
#!/bin/bash
# 基本的なwhile文
count=1
while [ $count -le 5 ]; do
echo "カウント: $count"
count=$((count + 1))
done
# ファイルを1行ずつ読み込む
while IFS= read -r line; do
echo "行: $line"
done < /etc/hostname
# CSVファイルの処理
while IFS=',' read -r name age city; do
echo "名前: $name, 年齢: $age, 都市: $city"
done << 'CSV'
田中,30,東京
鈴木,25,大阪
佐藤,35,福岡
CSV
# 無限ループ(Ctrl+Cで停止)
# while true; do
# echo "監視中... $(date)"
# sleep 5
# done
ループ制御:
- break: ループを即座に終了する
- continue: 現在の回をスキップして次の回へ進む
- break 2: 2重ループの外側まで一気に抜ける
loop_control.sh
#!/bin/bash
# breakとcontinueの例
for i in {1..10}; do
# 偶数はスキップ
if [ $((i % 2)) -eq 0 ]; then
continue
fi
# 7を超えたら終了
if [ $i -gt 7 ]; then
break
fi
echo "値: $i"
done
# 出力: 1, 3, 5, 7
Step 5関数の定義と活用
関数を使うと、よく使う処理をまとめて名前を付け、何度でも呼び出せます。スクリプトの可読性と保守性が大幅に向上します。
関数の基本構文
functions.sh
#!/bin/bash
# 関数の定義(2つの書き方)
# 方法1: function キーワード
function greet() {
echo "こんにちは、$1さん!"
}
# 方法2: キーワード省略(POSIX互換)
say_goodbye() {
echo "さようなら、$1さん!"
}
# 関数の呼び出し
greet "太郎"
say_goodbye "花子"
引数と戻り値
func_args.sh
#!/bin/bash
# 複数の引数を受け取る関数
create_user() {
local username="$1"
local role="$2"
echo "ユーザー作成: $username (役割: $role)"
}
create_user "admin" "管理者"
# 戻り値を使う関数(returnは0-255の整数のみ)
is_file_exists() {
if [ -f "$1" ]; then
return 0 # 成功(true)
else
return 1 # 失敗(false)
fi
}
if is_file_exists "/etc/hosts"; then
echo "ファイルが存在します"
fi
# 文字列を返す場合はechoを使う
get_timestamp() {
echo "$(date +%Y%m%d_%H%M%S)"
}
timestamp=$(get_timestamp)
echo "タイムスタンプ: $timestamp"
# ローカル変数
calculate() {
local result=$(( $1 + $2 ))
echo $result
}
sum=$(calculate 10 20)
echo "合計: $sum"
注意: 関数内の変数は
local を付けないとグローバルスコープになります。意図しない変数の上書きを防ぐため、関数内では必ず local を使いましょう。
Step 6実践スクリプト集
ここまでの知識を組み合わせて、実際の業務で使える自動化スクリプトを紹介します。
バックアップスクリプト
backup.sh
#!/bin/bash
# === 設定 ===
BACKUP_SOURCE="/home/user/documents"
BACKUP_DEST="/backup"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="${BACKUP_DEST}/backup_${DATE}.tar.gz"
LOG_FILE="${BACKUP_DEST}/backup.log"
KEEP_DAYS=30
# === 関数 ===
log_message() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
check_directory() {
if [ ! -d "$1" ]; then
mkdir -p "$1"
log_message "ディレクトリを作成: $1"
fi
}
# === メイン処理 ===
log_message "=== バックアップ開始 ==="
# バックアップ先の確認
check_directory "$BACKUP_DEST"
# バックアップの実行
if tar -czf "$BACKUP_FILE" -C "$(dirname "$BACKUP_SOURCE")" "$(basename "$BACKUP_SOURCE")" 2>> "$LOG_FILE"; then
SIZE=$(du -h "$BACKUP_FILE" | cut -f1)
log_message "成功: $BACKUP_FILE ($SIZE)"
else
log_message "エラー: バックアップに失敗しました"
exit 1
fi
# 古いバックアップの削除
deleted=$(find "$BACKUP_DEST" -name "backup_*.tar.gz" -mtime +${KEEP_DAYS} -delete -print | wc -l)
if [ "$deleted" -gt 0 ]; then
log_message "${KEEP_DAYS}日以前のバックアップを${deleted}件削除しました"
fi
log_message "=== バックアップ完了 ==="
ログ監視スクリプト
log_monitor.sh
#!/bin/bash
# === 設定 ===
WATCH_LOG="/var/log/syslog"
ALERT_KEYWORDS=("ERROR" "CRITICAL" "FATAL" "panic")
CHECK_INTERVAL=10
ALERT_LOG="/tmp/alert_$(date +%Y%m%d).log"
# === 関数 ===
send_alert() {
local keyword="$1"
local line="$2"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo "[$timestamp] ALERT [$keyword]: $line" >> "$ALERT_LOG"
echo -e "\033[31m[ALERT]\033[0m $keyword が検出されました"
}
check_log() {
local last_line_count="$1"
local current_line_count=$(wc -l < "$WATCH_LOG")
if [ "$current_line_count" -gt "$last_line_count" ]; then
local new_lines=$(tail -n $((current_line_count - last_line_count)) "$WATCH_LOG")
for keyword in "${ALERT_KEYWORDS[@]}"; do
while IFS= read -r line; do
send_alert "$keyword" "$line"
done <<< "$(echo "$new_lines" | grep -i "$keyword")"
done
fi
echo "$current_line_count"
}
# === メイン処理 ===
echo "=== ログ監視開始: $WATCH_LOG ==="
echo "監視キーワード: ${ALERT_KEYWORDS[*]}"
echo "チェック間隔: ${CHECK_INTERVAL}秒"
echo "Ctrl+C で停止"
line_count=$(wc -l < "$WATCH_LOG")
while true; do
line_count=$(check_log "$line_count")
sleep "$CHECK_INTERVAL"
done
システム情報レポートスクリプト
system_report.sh
#!/bin/bash
# === システム情報レポート生成 ===
REPORT_FILE="/tmp/system_report_$(date +%Y%m%d).txt"
separator() {
echo "========================================" >> "$REPORT_FILE"
}
section() {
echo "" >> "$REPORT_FILE"
separator
echo " $1" >> "$REPORT_FILE"
separator
}
{
echo "システム情報レポート"
echo "生成日時: $(date '+%Y-%m-%d %H:%M:%S')"
echo "ホスト名: $(hostname)"
section "OS情報"
uname -a
section "ディスク使用量"
df -h | grep -v tmpfs
section "メモリ使用量"
free -h
section "CPU情報"
nproc
echo "CPU数: $(nproc)"
section "ネットワーク"
ip addr show 2>/dev/null || ifconfig 2>/dev/null
section "稼働時間"
uptime
section "ログインユーザー"
who
} > "$REPORT_FILE" 2>&1
echo "レポートを生成しました: $REPORT_FILE"
echo "ファイルサイズ: $(du -h "$REPORT_FILE" | cut -f1)"
スクリプト作成のベストプラクティス:
- シバンを書く: 必ず
#!/bin/bashを1行目に記述 - コメントを残す: 処理の目的や注意点を記述
- 変数を引用符で囲む:
"$var"のようにダブルクォートで囲む - エラーハンドリング:
set -eでエラー時に即停止させる - ローカル変数: 関数内では
localを使う
シェルスクリプト学習チェックリスト
#!/bin/bashの意味と役割を理解した- 変数の代入・参照ができる
- 環境変数と通常の変数の違いを理解した
- if文とテスト演算子で条件分岐が書ける
- case文でパターンマッチングができる
- for文・while文でループ処理が書ける
- 関数の定義・引数・戻り値を扱える
- 実用的なバックアップスクリプトが書ける