シェルスクリプト実践:高度な技術と実用例

シェルスクリプトの基礎をマスターしたら、実践的な高度技術を身につけましょう。関数、配列、ファイル操作、エラー処理から実際の業務で使える実用例まで、プロフェッショナルなスクリプト作成技術を解説します。

目次

  1. 関数
  2. 配列
  3. ファイル操作
  4. エラー処理
  5. 実践的なスクリプト例

1. 関数

関数の定義と呼び出し

基本的な関数

#!/bin/bash

# 関数の定義
greet() {
    echo "こんにちは、$1さん!"
}

# 関数の呼び出し
greet "太郎"
greet "花子"

戻り値のある関数

#!/bin/bash

# 数値の平方を計算する関数
square() {
    local num=$1
    local result=$((num * num))
    echo $result
}

# 使用例
number=5
result=$(square $number)
echo "$number の平方は $result です"

複数引数の関数

#!/bin/bash

# ファイル情報を表示する関数
file_info() {
    local filepath=$1
    
    if [ -f "$filepath" ]; then
        echo "ファイル: $filepath"
        echo "サイズ: $(wc -c < "$filepath") bytes"
        echo "行数: $(wc -l < "$filepath") lines"
        echo "最終更新: $(stat -c %y "$filepath")"
    else
        echo "ファイル $filepath が存在しません"
        return 1
    fi
}

# 使用例
file_info "/etc/passwd"
file_info "nonexistent.txt"

高度な関数

ローカル変数とグローバル変数

#!/bin/bash

global_var="グローバル"

demo_scope() {
    local local_var="ローカル"
    global_var="変更されたグローバル"
    
    echo "関数内: local_var = $local_var"
    echo "関数内: global_var = $global_var"
}

echo "関数呼び出し前: global_var = $global_var"
demo_scope
echo "関数呼び出し後: global_var = $global_var"
echo "関数外: local_var = $local_var"  # 空になる

再帰関数

#!/bin/bash

# 階乗を計算する再帰関数
factorial() {
    local n=$1
    
    if [ $n -le 1 ]; then
        echo 1
    else
        local prev=$(factorial $((n - 1)))
        echo $((n * prev))
    fi
}

# 使用例
for i in {1..5}; do
    result=$(factorial $i)
    echo "$i! = $result"
done

エラーハンドリング付き関数

#!/bin/bash

# 安全なディレクトリ作成関数
safe_mkdir() {
    local dir_path=$1
    
    if [ -z "$dir_path" ]; then
        echo "エラー: ディレクトリパスが指定されていません" >&2
        return 1
    fi
    
    if [ -d "$dir_path" ]; then
        echo "ディレクトリ $dir_path は既に存在します"
        return 0
    fi
    
    if mkdir -p "$dir_path" 2>/dev/null; then
        echo "ディレクトリ $dir_path を作成しました"
        return 0
    else
        echo "エラー: ディレクトリ $dir_path の作成に失敗しました" >&2
        return 1
    fi
}

# 使用例
safe_mkdir "/tmp/test_dir"
safe_mkdir "/root/forbidden"  # 権限エラーの例

2. 配列

配列の基本操作

配列の作成と要素アクセス

#!/bin/bash

# 配列の定義
fruits=("apple" "banana" "orange" "grape")
numbers=(1 2 3 4 5)

# 要素へのアクセス
echo "最初の果物: ${fruits[0]}"
echo "3番目の数字: ${numbers[2]}"

# 全要素の表示
echo "全ての果物: ${fruits[@]}"
echo "全ての数字: ${numbers[*]}"

# 配列の長さ
echo "果物の数: ${#fruits[@]}"

配列への要素追加と削除

#!/bin/bash

# 初期配列
colors=("red" "green" "blue")
echo "初期配列: ${colors[@]}"

# 要素の追加
colors+=("yellow")
colors[${#colors[@]}]="purple"
echo "追加後: ${colors[@]}"

# 要素の削除(unset使用)
unset colors[1]  # "green"を削除
echo "削除後: ${colors[@]}"

# インデックスの表示
echo "インデックス: ${!colors[@]}"

連想配列(Bash 4.0以降)

#!/bin/bash

# 連想配列の宣言
declare -A person

# 値の設定
person["name"]="田中太郎"
person["age"]="30"
person["city"]="東京"

# 値の取得
echo "名前: ${person["name"]}"
echo "年齢: ${person["age"]}"
echo "都市: ${person["city"]}"

# 全キーの取得
echo "全キー: ${!person[@]}"

# 全値の取得
echo "全値: ${person[@]}"

配列の実践的な使用例

#!/bin/bash

# ログファイルのパターン配列
log_patterns=("/var/log/*.log" "/tmp/*.log" "$HOME/*.log")

echo "ログファイル検索結果:"
for pattern in "${log_patterns[@]}"; do
    echo "パターン: $pattern"
    
    # ファイルを配列に格納
    files=($pattern)
    
    if [ ${#files[@]} -gt 0 ] && [ -f "${files[0]}" ]; then
        for file in "${files[@]}"; do
            if [ -f "$file" ]; then
                size=$(wc -c < "$file")
                echo "  - $file ($size bytes)"
            fi
        done
    else
        echo "  - マッチするファイルなし"
    fi
    echo
done

3. ファイル操作

ファイルの読み書き

ファイル読み込みの様々な方法

#!/bin/bash

filename="data.txt"

# 方法1: while read を使用
echo "=== while read方式 ==="
while IFS= read -r line || [ -n "$line" ]; do
    echo "行: $line"
done < "$filename"

echo
echo "=== cat + パイプ方式 ==="
cat "$filename" | while read -r line; do
    echo "処理: $line"
done

echo
echo "=== 配列への読み込み ==="
mapfile -t lines < "$filename"
for i in "${!lines[@]}"; do
    echo "行 $((i+1)): ${lines[i]}"
done

ファイル書き込み

#!/bin/bash

output_file="output.txt"

# 新規作成(上書き)
echo "新しいファイル内容" > "$output_file"

# 追記
echo "追加の行1" >> "$output_file"
echo "追加の行2" >> "$output_file"

# 複数行の書き込み
cat << EOF >> "$output_file"
複数行のテキスト
2行目
3行目
EOF

# ファイル内容の確認
echo "ファイル内容:"
cat "$output_file"

CSVファイルの処理

#!/bin/bash

# CSVファイルの処理例
csv_file="employees.csv"

# CSVサンプルの作成
cat << EOF > "$csv_file"
名前,年齢,部署
田中,30,開発部
佐藤,25,営業部
鈴木,35,管理部
EOF

echo "CSV処理結果:"
# ヘッダーをスキップして処理
tail -n +2 "$csv_file" | while IFS=',' read -r name age dept; do
    echo "社員: $name (${age}歳) - $dept"
done

ファイル操作の高度な例

#!/bin/bash

# ファイル同期スクリプト例
source_dir="/path/to/source"
backup_dir="/path/to/backup"

# バックアップ関数
backup_files() {
    local src="$1"
    local dest="$2"
    
    if [ ! -d "$src" ]; then
        echo "エラー: ソースディレクトリが存在しません: $src"
        return 1
    fi
    
    # バックアップディレクトリの作成
    mkdir -p "$dest"
    
    # ファイル同期
    rsync -av --delete "$src/" "$dest/"
    
    echo "バックアップ完了: $src -> $dest"
}

# ログ記録付きでバックアップ実行
log_file="/tmp/backup.log"
{
    echo "バックアップ開始: $(date)"
    backup_files "$source_dir" "$backup_dir"
    echo "バックアップ終了: $(date)"
} >> "$log_file" 2>&1

4. エラー処理

エラー処理のベストプラクティス

set オプションによる厳密なエラー処理

#!/bin/bash

# 厳密なエラー処理の設定
set -euo pipefail

# set -e: コマンドが失敗したら即座に終了
# set -u: 未定義変数を使用したらエラー
# set -o pipefail: パイプラインで一つでも失敗したらエラー

# エラートラップの設定
trap 'echo "エラーが発生しました: 行番号 $LINENO" >&2' ERR

echo "厳密なエラー処理のデモ"

# 正常なコマンド
echo "正常処理1"

# 意図的なエラー(コメントアウトして実行)
# false  # このコマンドは失敗する

echo "この行は実行されません(上のエラーで停止)"

手動エラーチェック

#!/bin/bash

# ファイル処理関数(エラーチェック付き)
process_file() {
    local filename="$1"
    
    # ファイル存在チェック
    if [ ! -f "$filename" ]; then
        echo "エラー: ファイル '$filename' が見つかりません" >&2
        return 1
    fi
    
    # 読み取り権限チェック
    if [ ! -r "$filename" ]; then
        echo "エラー: ファイル '$filename' の読み取り権限がありません" >&2
        return 1
    fi
    
    # ファイル処理
    local line_count
    if ! line_count=$(wc -l < "$filename" 2>/dev/null); then
        echo "エラー: ファイル '$filename' の行数カウントに失敗しました" >&2
        return 1
    fi
    
    echo "ファイル '$filename' の行数: $line_count"
    return 0
}

# 使用例
if process_file "test.txt"; then
    echo "処理が正常に完了しました"
else
    echo "処理が失敗しました"
    exit 1
fi

ログ記録付きエラー処理

#!/bin/bash

LOG_FILE="/tmp/script.log"

# ログ関数
log() {
    local level="$1"
    shift
    local message="$*"
    local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    
    echo "[$timestamp] [$level] $message" | tee -a "$LOG_FILE"
}

# エラー処理関数
handle_error() {
    local exit_code=$?
    local line_no=$1
    
    log "ERROR" "スクリプトがエラーで終了しました (終了コード: $exit_code, 行: $line_no)"
    exit $exit_code
}

# エラートラップの設定
trap 'handle_error $LINENO' ERR

# メイン処理
log "INFO" "スクリプト開始"

# 危険な操作の例
dangerous_operation() {
    log "INFO" "危険な操作を実行中..."
    
    # 何らかの処理
    sleep 1
    
    # 意図的なエラー(50%の確率)
    if [ $((RANDOM % 2)) -eq 0 ]; then
        log "ERROR" "操作が失敗しました"
        return 1
    fi
    
    log "INFO" "操作が成功しました"
    return 0
}

if dangerous_operation; then
    log "INFO" "すべての処理が完了しました"
else
    log "WARN" "一部の処理が失敗しましたが、継続します"
fi

log "INFO" "スクリプト終了"

5. 実践的なスクリプト例

例1: システムバックアップスクリプト

#!/bin/bash

# システムバックアップスクリプト
set -euo pipefail

# 設定
BACKUP_ROOT="/backup"
LOG_FILE="/var/log/backup.log"
RETENTION_DAYS=7
DATE=$(date +%Y%m%d_%H%M%S)

# バックアップ対象
BACKUP_DIRS=(
    "/etc"
    "/home"
    "/var/www"
    "/usr/local/bin"
)

# ログ関数
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"
}

# クリーンアップ関数
cleanup() {
    log "古いバックアップの削除を開始"
    find "$BACKUP_ROOT" -type f -name "backup_*.tar.gz" -mtime +$RETENTION_DAYS -delete
    log "古いバックアップの削除完了"
}

# メインバックアップ関数
main_backup() {
    local backup_file="$BACKUP_ROOT/backup_$DATE.tar.gz"
    
    log "バックアップ開始: $backup_file"
    
    # バックアップディレクトリの作成
    mkdir -p "$BACKUP_ROOT"
    
    # tar コマンドでバックアップ作成
    if tar -czf "$backup_file" "${BACKUP_DIRS[@]}" 2>/dev/null; then
        local size=$(du -h "$backup_file" | cut -f1)
        log "バックアップ成功: $backup_file (サイズ: $size)"
    else
        log "エラー: バックアップの作成に失敗しました"
        exit 1
    fi
}

# 権限チェック
if [ "$(id -u)" -ne 0 ]; then
    echo "このスクリプトはroot権限で実行してください"
    exit 1
fi

# メイン実行
log "システムバックアップ処理開始"
main_backup
cleanup
log "システムバックアップ処理完了"

例2: ログ監視スクリプト

#!/bin/bash

# ログ監視スクリプト
set -euo pipefail

# 設定
LOG_FILE="/var/log/syslog"
ALERT_PATTERNS=("ERROR" "CRITICAL" "FAILED")
CHECK_INTERVAL=10
LAST_CHECK_FILE="/tmp/log_monitor_last_check"

# アラート送信関数
send_alert() {
    local message="$1"
    local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    
    # ここでメール送信やSlack通知などを実装
    echo "[$timestamp] ALERT: $message" >> "/var/log/alerts.log"
    
    # 簡易通知(システムログ)
    logger "LOG_MONITOR_ALERT: $message"
}

# ログ監視メイン処理
monitor_logs() {
    local last_position=0
    
    # 前回のチェック位置を読み込み
    if [ -f "$LAST_CHECK_FILE" ]; then
        last_position=$(cat "$LAST_CHECK_FILE")
    fi
    
    # ファイルサイズ取得
    local current_size=$(wc -c < "$LOG_FILE")
    
    # 新しいログエントリがある場合のみ処理
    if [ "$current_size" -gt "$last_position" ]; then
        # 新しい部分のみ抽出
        local new_lines=$(tail -c +$((last_position + 1)) "$LOG_FILE")
        
        # パターンマッチング
        for pattern in "${ALERT_PATTERNS[@]}"; do
            if echo "$new_lines" | grep -q "$pattern"; then
                local matches=$(echo "$new_lines" | grep "$pattern")
                send_alert "検出されたパターン '$pattern' in $LOG_FILE: $matches"
            fi
        done
        
        # 現在位置を保存
        echo "$current_size" > "$LAST_CHECK_FILE"
    fi
}

# メインループ
echo "ログ監視開始: $LOG_FILE"
echo "監視パターン: ${ALERT_PATTERNS[*]}"

while true; do
    monitor_logs
    sleep "$CHECK_INTERVAL"
done

例3: ファイル整理スクリプト

#!/bin/bash

# ファイル整理スクリプト
set -euo pipefail

# 設定
SOURCE_DIR="$HOME/Downloads"
ORGANIZE_ROOT="$HOME/Organized"
LOG_FILE="/tmp/file_organizer.log"

# ファイルタイプと移動先の対応
declare -A FILE_TYPES=(
    ["pdf"]="Documents/PDF"
    ["doc,docx"]="Documents/Word"
    ["xls,xlsx"]="Documents/Excel"
    ["ppt,pptx"]="Documents/PowerPoint"
    ["txt"]="Documents/Text"
    ["jpg,jpeg,png,gif"]="Images"
    ["mp4,avi,mov"]="Videos"
    ["mp3,wav,flac"]="Audio"
    ["zip,rar,7z,tar,gz"]="Archives"
)

# ログ関数
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"
}

# ファイルタイプ判定関数
get_file_type() {
    local filename="$1"
    local extension="${filename##*.}"
    extension=$(echo "$extension" | tr '[:upper:]' '[:lower:]')
    
    for types in "${!FILE_TYPES[@]}"; do
        if [[ ",$types," == *",$extension,"* ]]; then
            echo "${FILE_TYPES[$types]}"
            return 0
        fi
    done
    
    echo "Others"
}

# ファイル移動関数
move_file() {
    local source_file="$1"
    local file_type="$2"
    local dest_dir="$ORGANIZE_ROOT/$file_type"
    local filename=$(basename "$source_file")
    
    # 移動先ディレクトリの作成
    mkdir -p "$dest_dir"
    
    # 重複ファイル名の処理
    local dest_file="$dest_dir/$filename"
    local counter=1
    
    while [ -f "$dest_file" ]; do
        local name="${filename%.*}"
        local ext="${filename##*.}"
        dest_file="$dest_dir/${name}_${counter}.${ext}"
        ((counter++))
    done
    
    # ファイル移動
    if mv "$source_file" "$dest_file"; then
        log "移動完了: $filename -> $file_type/"
    else
        log "エラー: $filename の移動に失敗しました"
    fi
}

# メイン処理
organize_files() {
    log "ファイル整理開始: $SOURCE_DIR"
    
    if [ ! -d "$SOURCE_DIR" ]; then
        log "エラー: ソースディレクトリが存在しません: $SOURCE_DIR"
        exit 1
    fi
    
    local file_count=0
    
    # ファイル処理
    while IFS= read -r -d '' file; do
        if [ -f "$file" ]; then
            local file_type=$(get_file_type "$(basename "$file")")
            move_file "$file" "$file_type"
            ((file_count++))
        fi
    done < <(find "$SOURCE_DIR" -maxdepth 1 -type f -print0)
    
    log "ファイル整理完了: $file_count 個のファイルを処理しました"
}

# 統計表示
show_statistics() {
    log "=== 整理結果統計 ==="
    
    for dir in "$ORGANIZE_ROOT"/*; do
        if [ -d "$dir" ]; then
            local count=$(find "$dir" -type f | wc -l)
            local dirname=$(basename "$dir")
            log "$dirname: $count 個のファイル"
        fi
    done
}

# メイン実行
organize_files
show_statistics

⚠️ 実践でよくある間違いと落とし穴

実用的なスクリプト作成で陥りやすい高度な問題と、プロフェッショナルな解決策を解説します。

🚫 間違い1: 配列の誤った使用

❌ 問題のある例

# 配列を文字列として扱う
files=("file1.txt" "file 2.txt" "file3.txt")
for file in $files; do    # スペース区切りで分割される
    echo $file
done

# インデックスの勘違い
echo ${files[1, 2]}      # 構文エラー

配列が正しく展開されず、スペースを含むファイル名で問題が発生します。

✅ 正しい使用法

# 配列の正しい展開
files=("file1.txt" "file 2.txt" "file3.txt")
for file in "${files[@]}"; do    # 配列として正しく展開
    echo "$file"
done

# 複数のインデックス指定
echo "${files[1]}" "${files[2]}"  # 個別に指定

"${array[@]}" で配列全体を安全に展開します。

🚫 間違い2: 関数のスコープ理解不足

❌ 予期しない動作

#!/bin/bash
counter=0

increment() {
    counter=$((counter + 1))    # グローバル変数を変更
    local result=$counter       # ローカル変数
    echo $result
}

increment
echo "Global counter: $counter"  # 意図しない変更

グローバル変数が意図せず変更されてしまいます。

✅ 適切なスコープ管理

#!/bin/bash
global_counter=0

increment() {
    local local_counter=$1      # 引数を受け取る
    local_counter=$((local_counter + 1))
    echo $local_counter         # 結果を返す(echo)
}

# 関数の戻り値を使用
result=$(increment $global_counter)
global_counter=$result          # 明示的に更新

localを適切に使い、関数の影響範囲を制御します。

🚫 間違い3: パイプラインでの変数変更の罠

❌ 期待と異なる結果

#!/bin/bash
count=0

# パイプラインのサブシェルで実行される
cat file.txt | while read line; do
    count=$((count + 1))
done

echo "Lines: $count"    # 0のまま(変更されない)

パイプラインの右側はサブシェルで実行され、変数変更が親に反映されません。

✅ 正しい方法

#!/bin/bash
count=0

# リダイレクションを使用
while read line; do
    count=$((count + 1))
done < file.txt

echo "Lines: $count"    # 正しくカウントされる

# または、コマンド置換を使用
count=$(wc -l < file.txt)
echo "Lines: $count"

リダイレクションやコマンド置換で問題を回避します。

🚫 間違い4: エラー処理の不備

❌ 不十分なエラー処理

#!/bin/bash

# ファイル処理(エラーチェックなし)
cp source.txt backup.txt
rm source.txt               # コピーが失敗していても削除

# 外部コマンドの結果を無視
curl -o data.json http://api.example.com/data
process_data data.json      # ダウンロード失敗でも処理続行

エラーが連鎖して深刻な問題になることがあります。

✅ 堅牢なエラー処理

#!/bin/bash
set -euo pipefail

# ファイル処理(適切なエラーチェック)
if cp source.txt backup.txt; then
    echo "バックアップ成功"
    rm source.txt
else
    echo "エラー: バックアップに失敗しました" >&2
    exit 1
fi

# 外部コマンドの結果確認
if curl -f -o data.json http://api.example.com/data; then
    process_data data.json
else
    echo "エラー: データの取得に失敗しました" >&2
    exit 1
fi

各ステップでエラーをチェックし、適切に対処します。

🚫 間違い5: セキュリティを考慮しない実装

❌ セキュリティ上の問題

#!/bin/bash

# ユーザー入力をそのまま実行(危険)
read -p "コマンドを入力: " user_command
eval $user_command

# 一時ファイル名が予測可能
temp_file="/tmp/script_data.txt"
echo "sensitive data" > $temp_file

# パスワードをコマンドラインに
mysql -u user -p'password123' -e "SELECT * FROM users"

任意のコマンド実行やパスワード漏洩の危険性があります。

✅ セキュアな実装

#!/bin/bash

# 入力値の検証
read -p "ファイル名を入力: " filename
if [[ "$filename" =~ ^[a-zA-Z0-9._-]+$ ]]; then
    echo "処理: $filename"
else
    echo "エラー: 無効なファイル名" >&2
    exit 1
fi

# 安全な一時ファイル作成
temp_file=$(mktemp)
trap 'rm -f "$temp_file"' EXIT
echo "sensitive data" > "$temp_file"

# 設定ファイルからパスワード読み込み
if [ -f ~/.mysql_config ]; then
    source ~/.mysql_config
    mysql -u "$DB_USER" -p"$DB_PASS" -e "SELECT * FROM users"
fi

入力検証、安全な一時ファイル、設定ファイル活用でセキュリティを確保します。

🚫 間違い6: パフォーマンスを考慮しない実装

❌ 非効率な処理

#!/bin/bash

# 大量のファイルを個別処理(遅い)
for file in /path/to/large/directory/*; do
    wc -l "$file"           # ファイルごとにプロセス起動
done

# 繰り返し同じコマンド実行
for i in {1..1000}; do
    current_time=$(date +%s)    # 毎回dateコマンド実行
    echo "Processing $i at $current_time"
done

大量のプロセス生成で処理が極端に遅くなります。

✅ 効率的な実装

#!/bin/bash

# バッチ処理で効率化
find /path/to/large/directory -name "*" -type f -exec wc -l {} + 

# 一度だけ取得して再利用
start_time=$(date +%s)
for i in {1..1000}; do
    current_time=$((start_time + i))
    echo "Processing $i at $current_time"
done

# 並列処理で高速化
export -f process_file
find /path -name "*.txt" | xargs -P 4 -I {} bash -c 'process_file "$@"' _ {}

バッチ処理、値の再利用、並列処理で性能を向上させます。

🎯 上級者のためのベストプラクティス

📋 ログ出力の標準化

# 統一されたログ関数
log() {
    local level=$1; shift
    local message="$*"
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$level] $message" >&2
}

log "INFO" "処理開始"
log "ERROR" "ファイルが見つかりません: $filename"
log "DEBUG" "変数の値: var=$var"

🔄 設定管理の分離

# config.sh
DB_HOST="localhost"
DB_PORT="3306"
MAX_RETRIES=3
LOG_LEVEL="INFO"

# main.sh
#!/bin/bash
CONFIG_FILE="${1:-./config.sh}"
if [ -f "$CONFIG_FILE" ]; then
    source "$CONFIG_FILE"
else
    echo "設定ファイルが見つかりません: $CONFIG_FILE" >&2
    exit 1
fi

🧪 テスト可能な設計

# テスト可能な関数設計
process_data() {
    local input_file="$1"
    local output_file="$2"
    
    # 入力検証
    [ -f "$input_file" ] || return 1
    
    # 処理
    awk '{print NR ": " $0}' "$input_file" > "$output_file"
    
    # 結果検証
    [ -f "$output_file" ] && [ -s "$output_file" ]
}

# テストスクリプト
test_process_data() {
    local test_input=$(mktemp)
    local test_output=$(mktemp)
    
    echo -e "line1\nline2" > "$test_input"
    
    if process_data "$test_input" "$test_output"; then
        echo "✅ テスト成功"
    else
        echo "❌ テスト失敗"
    fi
    
    rm -f "$test_input" "$test_output"
}

シェルスクリプトのベストプラクティス

📝 コーディング規約

  • 変数名は意味のある名前を使用
  • 関数は1つの責任に集中
  • コメントで処理の意図を明確に
  • インデントを統一(2スペース推奨)
  • 長い行は適切に分割

🔒 セキュリティ

  • 入力値の検証を必ず実施
  • 一時ファイルは安全な場所に作成
  • 権限は最小限に設定
  • 機密情報をハードコードしない
  • 外部コマンドの結果を検証

🐛 デバッグ

  • set -x でデバッグモード有効化
  • 適切なログ出力を実装
  • エラー時の状態を記録
  • 段階的にテストして開発
  • ShellCheck などの静的解析ツール活用

まとめ

シェルスクリプトの実践技術をマスターすることで、効率的で信頼性の高い自動化が実現できます。

重要なポイント

  • 関数でコードの再利用性と保守性を向上
  • 配列で複雑なデータ処理を効率化
  • 適切なエラー処理で堅牢なスクリプトを作成
  • 実践例を参考に業務に応用

次のステップ

📚 さらなる学習

  • 高度なシェル機能 - プロセス置換、コプロセス
  • 他言語との連携 - Python、awk、sed との組み合わせ
  • CI/CD自動化 - GitHubActions、Jenkins での活用
  • システム管理 - cron、systemdとの連携

📋 シェルスクリプトシリーズ全体

  1. 基礎編 - 変数、条件分岐、ループ、関数
  2. 実践編(この記事) - 実務シナリオ、エラーハンドリング、最適化
📢 アフィリエイトリンクについて

当サイトは、Amazon.co.jpを宣伝しリンクすることによってサイトが紹介料を獲得できる手段を提供することを目的に設定されたアフィリエイトプログラムである「Amazonアソシエイト・プログラム」の参加者です。商品価格に影響はありません。