Shell

#9
GitHub#5
RedMonk#14
IEEESpectrum#14
JetBrains#7
スクリプト言語システム管理自動化DevOpsコマンドライン

スクリプト言語

Shell(シェル)

概要

Shellは、Unix系OSでコマンドライン操作や自動化スクリプトを記述するためのスクリプト言語です。システム管理、ファイル操作、プロセス制御、CI/CDパイプラインなど、様々な場面で重要な役割を果たしています。Bash、Zsh、Fishなど複数の種類があります。

詳細

最も一般的なBash(Bourne Again Shell)は、1989年にGNUプロジェクトの一部として開発されました。Unix系システムの標準シェルとして広く採用され、Linux、macOS、WSLなどで使用されています。

Shellスクリプトは、コマンドの組み合わせ、条件分岐、ループ、関数などを使って、複雑なタスクを自動化できます。DevOpsの普及により、Infrastructure as Code、CI/CD、コンテナ管理などでの重要性が増しています。

書き方の例

Hello World

#!/bin/bash
echo "Hello, World!"

変数と基本操作

#!/bin/bash

# 変数の定義
name="太郎"
age=25
current_date=$(date)

# 変数の使用
echo "名前: $name"
echo "年齢: ${age}歳"
echo "現在時刻: $current_date"

# 数値計算
x=10
y=5
sum=$((x + y))
echo "$x + $y = $sum"

# 文字列操作
text="Hello World"
echo "文字数: ${#text}"
echo "大文字: ${text^^}"
echo "小文字: ${text,,}"

コマンドライン引数と条件分岐

#!/bin/bash

# コマンドライン引数の確認
if [ $# -eq 0 ]; then
    echo "使用法: $0 <ファイル名>"
    exit 1
fi

filename=$1

# ファイルの存在確認
if [ -f "$filename" ]; then
    echo "ファイル '$filename' が見つかりました"
    
    # ファイルの属性確認
    if [ -r "$filename" ]; then
        echo "読み取り可能です"
    fi
    
    if [ -w "$filename" ]; then
        echo "書き込み可能です"
    fi
    
    if [ -x "$filename" ]; then
        echo "実行可能です"
    fi
    
    # ファイル情報の表示
    echo "ファイルサイズ: $(stat -f%z "$filename" 2>/dev/null || stat -c%s "$filename" 2>/dev/null) バイト"
    echo "最終更新: $(stat -f%Sm "$filename" 2>/dev/null || stat -c%y "$filename" 2>/dev/null)"
    
elif [ -d "$filename" ]; then
    echo "'$filename' はディレクトリです"
    echo "含まれるファイル数: $(ls -1 "$filename" | wc -l)"
else
    echo "ファイル '$filename' が見つかりません"
    exit 1
fi

ループと配列

#!/bin/bash

# 配列の定義
fruits=("りんご" "バナナ" "オレンジ" "ぶどう")

# 配列の表示(for文)
echo "果物リスト:"
for fruit in "${fruits[@]}"; do
    echo "- $fruit"
done

# インデックスを使ったループ
echo -e "\n番号付きリスト:"
for i in "${!fruits[@]}"; do
    echo "$((i+1)). ${fruits[i]}"
done

# while文の例
echo -e "\n1から5までの数値:"
counter=1
while [ $counter -le 5 ]; do
    echo "数値: $counter"
    ((counter++))
done

# ファイル処理のループ
echo -e "\n現在のディレクトリのファイル:"
for file in *.txt; do
    if [ -f "$file" ]; then
        echo "テキストファイル: $file"
    fi
done

関数と引数処理

#!/bin/bash

# 簡単な関数
greet() {
    local name=$1
    local time=$2
    echo "${time}${name}さん!"
}

# ファイルバックアップ関数
backup_file() {
    local source_file=$1
    local backup_dir=$2
    
    # 引数チェック
    if [ $# -ne 2 ]; then
        echo "使用法: backup_file <ソースファイル> <バックアップディレクトリ>"
        return 1
    fi
    
    # ソースファイルの存在確認
    if [ ! -f "$source_file" ]; then
        echo "エラー: ファイル '$source_file' が見つかりません"
        return 1
    fi
    
    # バックアップディレクトリの作成
    mkdir -p "$backup_dir"
    
    # バックアップファイル名の生成
    local filename=$(basename "$source_file")
    local timestamp=$(date +"%Y%m%d_%H%M%S")
    local backup_file="${backup_dir}/${filename}.backup.${timestamp}"
    
    # ファイルのコピー
    if cp "$source_file" "$backup_file"; then
        echo "バックアップ完了: $backup_file"
        return 0
    else
        echo "エラー: バックアップに失敗しました"
        return 1
    fi
}

# 関数の使用
greet "太郎" "おはよう"
greet "花子" "こんばんは"

# バックアップ関数のテスト
echo "sample.txt" > sample.txt
backup_file "sample.txt" "./backup"

システム管理スクリプト

#!/bin/bash

# システム情報収集スクリプト
collect_system_info() {
    echo "=== システム情報レポート ==="
    echo "生成日時: $(date)"
    echo ""
    
    # OS情報
    echo "=== OS情報 ==="
    if command -v lsb_release >/dev/null 2>&1; then
        lsb_release -a
    elif [ -f /etc/os-release ]; then
        cat /etc/os-release
    else
        uname -a
    fi
    echo ""
    
    # CPU情報
    echo "=== CPU情報 ==="
    if [ -f /proc/cpuinfo ]; then
        grep "model name" /proc/cpuinfo | head -1
        echo "CPU数: $(nproc)"
    fi
    echo ""
    
    # メモリ情報
    echo "=== メモリ情報 ==="
    if command -v free >/dev/null 2>&1; then
        free -h
    fi
    echo ""
    
    # ディスク使用量
    echo "=== ディスク使用量 ==="
    df -h
    echo ""
    
    # プロセス情報(CPU使用率上位5つ)
    echo "=== CPU使用率上位プロセス ==="
    ps aux --sort=-%cpu | head -6
    echo ""
    
    # ネットワーク接続
    echo "=== ネットワーク接続 ==="
    if command -v ss >/dev/null 2>&1; then
        ss -tuln
    elif command -v netstat >/dev/null 2>&1; then
        netstat -tuln
    fi
}

# ログローテーション関数
rotate_logs() {
    local log_dir=$1
    local days_to_keep=$2
    
    if [ $# -ne 2 ]; then
        echo "使用法: rotate_logs <ログディレクトリ> <保持日数>"
        return 1
    fi
    
    echo "ログローテーション開始: $log_dir"
    
    # 古いログファイルを削除
    find "$log_dir" -name "*.log" -type f -mtime +$days_to_keep -delete
    
    # 圧縮済みログファイルも削除
    find "$log_dir" -name "*.log.gz" -type f -mtime +$days_to_keep -delete
    
    echo "ログローテーション完了"
}

# 実行例
collect_system_info > system_report_$(date +%Y%m%d).txt
rotate_logs "/var/log/myapp" 30

エラーハンドリングとデバッグ

#!/bin/bash

# エラー時に終了
set -e

# 未定義変数使用時に終了  
set -u

# パイプラインの最初の失敗で終了
set -o pipefail

# デバッグモード(実行されるコマンドを表示)
# set -x

# エラーハンドリング関数
error_exit() {
    echo "エラー: $1" >&2
    exit 1
}

# クリーンアップ関数
cleanup() {
    echo "クリーンアップ処理を実行中..."
    # 一時ファイルの削除など
    rm -f /tmp/script_temp_*
}

# シグナルハンドリング(スクリプト終了時にクリーンアップ)
trap cleanup EXIT
trap 'error_exit "スクリプトが中断されました"' INT TERM

# ファイル処理の例
process_file() {
    local input_file=$1
    local output_file=$2
    
    # 入力ファイルの確認
    [ -f "$input_file" ] || error_exit "入力ファイルが見つかりません: $input_file"
    
    # 出力ディレクトリの作成
    local output_dir=$(dirname "$output_file")
    mkdir -p "$output_dir" || error_exit "出力ディレクトリの作成に失敗: $output_dir"
    
    # 処理実行
    if ! grep -v "^#" "$input_file" > "$output_file"; then
        error_exit "ファイル処理に失敗しました"
    fi
    
    echo "処理完了: $output_file"
}

# 実行例
echo "スクリプト開始"
process_file "/etc/hosts" "/tmp/hosts_filtered.txt"
echo "スクリプト正常終了"

メリット・デメリット

メリット

  • システム統合: OSコマンドとの直接連携が可能
  • 自動化: 繰り返し作業の効率的な自動化
  • 軽量: 追加インストール不要で即座に利用可能
  • DevOps対応: CI/CD、インフラ管理での標準ツール
  • テキスト処理: パイプ、リダイレクトによる強力なテキスト処理
  • 豊富なコマンド: Unix系ツールとの組み合わせで幅広い処理が可能

デメリット

  • 可読性: 複雑なスクリプトは理解が困難
  • エラーハンドリング: デフォルトのエラー処理が不十分
  • 移植性: OSやシェルによる差異
  • デバッグ: デバッグツールが限定的
  • 大規模開発: 大規模なアプリケーション開発には不適

主要リンク

ランキング情報

  • 総合ランキング: 9位
  • GitHub使用率: 5位
  • RedMonk言語ランキング: 14位
  • IEEE Spectrum: 14位
  • JetBrains開発者調査: 7位

Shellは、システム管理やDevOpsにおいて不可欠なスクリプト言語として、自動化とシステム統合の重要な役割を担っています。