T-CREATOR

Shell Script とは?初心者が最短で理解する基本構文・実行モデル・活用領域

Shell Script とは?初心者が最短で理解する基本構文・実行モデル・活用領域

Shell Script と聞いて、「難しそう」「黒い画面で何をしているのかわからない」と感じたことはありませんか?

実は、Shell Script は開発現場で最も頻繁に使われる基本技術の 1 つです。サーバー管理からアプリケーション開発まで、幅広い場面で活躍します。本記事では、Shell Script の基本から実行の仕組み、そして実務で役立つ活用例まで、初心者の方でも最短で理解できるように丁寧に解説していきますね。

この記事を読み終える頃には、Shell Script の本質を理解し、自分で簡単なスクリプトを書けるようになっているはずです。

背景

Shell Script が生まれた理由

コンピュータを操作する方法は大きく分けて 2 つあります。1 つはマウスでクリックする GUI(Graphical User Interface)、もう 1 つはキーボードでコマンドを入力する CLI(Command Line Interface)ですね。

Shell Script は、この CLI での操作を自動化するために生まれました。

mermaidflowchart TB
  user["ユーザー"] -->|コマンド入力| shell["Shell<br/>コマンド解釈プログラム"]
  shell -->|システムコール| kernel["カーネル<br/>OS の中核"]
  kernel -->|制御| hardware["ハードウェア<br/>CPU/メモリ/ディスク"]
  hardware -->|結果| kernel
  kernel -->|応答| shell
  shell -->|出力表示| user

図で理解できる要点:

  • Shell はユーザーとカーネルの橋渡し役として動作します
  • コマンドを解釈してシステムに伝え、結果をユーザーに返します
  • この一連の流れを自動化したものが Shell Script です

Shell の種類と歴史

Unix/Linux の世界では、いくつかの Shell が開発されてきました。

#Shell 名特徴主な用途
1sh最も基本的な Shell互換性重視のスクリプト
2bashsh の拡張版で最も普及Linux の標準 Shell
3zshbash より高機能macOS Catalina 以降の標準
4fishモダンで使いやすい対話的な操作向け

現在では bash(Bourne Again Shell)が最も広く使われており、本記事でも bash を基準に解説しますね。

Shell Script の役割

Shell Script は、繰り返し実行するコマンドをまとめて自動化するためのツールです。

例えば、毎日同じ手順でバックアップを取る作業があったとします。手動で実行すると以下のようになります。

bashcd /home/user/data
tar -czf backup.tar.gz ./*
mv backup.tar.gz /backup/$(date +%Y%m%d)_backup.tar.gz
echo "バックアップ完了"

これらのコマンドを 1 つのファイルにまとめて保存すれば、1 回の実行で全ての作業が完了するわけです。これが Shell Script の基本的な考え方になります。

課題

手動操作の限界

開発やサーバー運用では、同じコマンドを何度も実行する場面が頻繁にありますね。

手動で実行する場合、以下のような課題が発生します。

#課題具体例影響
1入力ミスコマンドのタイプミス予期しないエラーや削除
2手順の抜け途中の工程を忘れる不完全な処理結果
3時間のロス毎回同じ操作を繰り返す開発時間の浪費
4属人化作業者しか手順がわからないチーム開発の妨げ

特に深夜のデプロイ作業や、複数のサーバーに同じ設定を適用する場合、人間の注意力には限界があります。

複雑な処理の管理

システムが大きくなるにつれて、実行する処理も複雑になっていきます。

mermaidflowchart TB
  start["デプロイ開始"] --> git["Git から最新コードを取得"]
  git --> install["依存パッケージをインストール"]
  install --> build["アプリケーションをビルド"]
  build --> test["テストを実行"]
  test --> check{テスト結果}
  check -->|成功| deploy["本番環境にデプロイ"]
  check -->|失敗| rollback["ロールバック処理"]
  deploy --> notify["Slack に通知"]
  rollback --> notify
  notify --> done["完了"]

図で理解できる要点:

  • デプロイには複数の工程があり、条件分岐も含まれます
  • テスト失敗時の処理など、エラーハンドリングも必要です
  • これらを手動で管理するのは非常に困難になります

チーム開発での共有

複数人で開発する場合、作業手順を共有する必要があります。

口頭やドキュメントでの共有には以下の問題がありますね。

  • ドキュメントが更新されず、古い情報のままになる
  • 人によって解釈が異なり、実行結果にばらつきが出る
  • 新メンバーへの教育コストが高い

Shell Script として手順をコード化すれば、実行可能なドキュメントとして機能します。これにより、誰でも同じ結果を得られるようになるのです。

解決策

Shell Script による自動化

Shell Script を使えば、上記の課題を全て解決できます。

コマンドの組み合わせをファイル化することで、以下のメリットが得られますね。

#メリット説明
1再現性毎回同じ手順で実行される
2効率化1 コマンドで複雑な処理を実行
3共有可能Git で管理してチームで共有
4ドキュメント代わりスクリプト自体が手順書になる

基本的なスクリプトの構造

Shell Script は非常にシンプルな構造をしています。最小限のスクリプトを見てみましょう。

bash#!/bin/bash

この 1 行目は シバン(Shebang) と呼ばれ、どの Shell でスクリプトを実行するかを指定します。#!​/​bin​/​bash は「bash で実行してください」という意味ですね。

次に、実際にコマンドを記述していきます。

bash# 変数の定義
BACKUP_DIR="/backup"
DATE=$(date +%Y%m%d)

# で始まる行はコメントです。変数は 変数名=値 の形式で定義します。$() の中のコマンドは実行結果が代入されますね。

bash# ディレクトリの移動
cd /home/user/data

# ファイルの圧縮
tar -czf backup.tar.gz ./*

コマンドをそのまま記述するだけで、順番に実行されます。

bash# バックアップファイルの移動
mv backup.tar.gz "${BACKUP_DIR}/${DATE}_backup.tar.gz"

# 完了メッセージの表示
echo "バックアップが完了しました: ${DATE}"

変数を使う場合は ${変数名} の形式で参照します。これで基本的なスクリプトの完成です。

実行モデルの理解

Shell Script の実行の流れを理解することが重要です。

mermaidflowchart LR
  script["スクリプトファイル<br/>backup.sh"] -->|読み込み| shell["Shell プロセス"]
  shell -->|1行ずつ解釈| cmd1["コマンド実行"]
  cmd1 -->|次の行| cmd2["コマンド実行"]
  cmd2 -->|次の行| cmd3["コマンド実行"]
  cmd3 -->|終了| result["実行完了"]

図で理解できる要点:

  • スクリプトは上から順番に 1 行ずつ実行されます
  • 各コマンドは前のコマンドの完了を待ってから実行されます
  • エラーが発生しても、デフォルトでは次の行に進みます

実行権限の付与

Shell Script を実行するには、ファイルに実行権限を与える必要があります。

bashchmod +x backup.sh

chmod コマンドは、ファイルの権限を変更するコマンドです。+x は実行権限を追加する意味になりますね。

権限を確認するには以下のコマンドを使います。

bashls -l backup.sh

実行結果の例を見てみましょう。

text-rwxr-xr-x 1 user group 256 Jan 10 12:00 backup.sh
```

`rwxr-xr-x` の部分が権限を表しています。最初の `x` が所有者の実行権限です。

## スクリプトの実行方法

実行権限を付与したスクリプトは、以下の方法で実行できます。

````bash
./backup.sh

カレントディレクトリにあるスクリプトを実行する場合は、.​/​ を付けます。

別の方法として、bash コマンドで直接実行することもできますね。

bashbash backup.sh

この場合は実行権限がなくても実行できます。シバンの指定も無視され、明示的に bash で実行されるわけです。

具体例

例 1: 開発環境のセットアップスクリプト

プロジェクトに新しく参加したメンバーが、開発環境を構築する場面を想定しましょう。

まず、スクリプトファイルを作成します。

bash#!/bin/bash

次に、必要なツールがインストールされているか確認する処理を追加します。

bash# Node.js のバージョン確認
echo "Node.js のバージョンを確認中..."
if ! command -v node &> /dev/null; then
    echo "Error: Node.js がインストールされていません"
    exit 1
fi

NODE_VERSION=$(node -v)
echo "Node.js ${NODE_VERSION} を検出しました"

command -v は、指定したコマンドが存在するか確認するコマンドです。&> ​/​dev​/​null は出力を非表示にする記法になりますね。

続いて、依存パッケージのインストール処理を記述します。

bash# パッケージのインストール
echo "依存パッケージをインストール中..."
yarn install

if [ $? -ne 0 ]; then
    echo "Error: パッケージのインストールに失敗しました"
    exit 1
fi

$? は直前のコマンドの終了ステータスを表す特殊変数です。0 は成功、それ以外は失敗を意味します。

環境変数ファイルの作成も自動化しましょう。

bash# .env ファイルの作成
if [ ! -f .env ]; then
    echo ".env ファイルを作成中..."
    cp .env.example .env
    echo ".env ファイルを作成しました。必要に応じて編集してください。"
else
    echo ".env ファイルは既に存在します"
fi

[ ! -f .env ] は「.env ファイルが存在しない場合」という条件式です。

最後に完了メッセージを表示します。

bash# セットアップ完了
echo ""
echo "=============================="
echo "セットアップが完了しました!"
echo "=============================="
echo ""
echo "次のコマンドで開発サーバーを起動できます:"
echo "  yarn dev"

このスクリプトを setup.sh として保存すれば、新メンバーは .​/​setup.sh を実行するだけで環境構築が完了します。

例 2: 定期的なログ管理スクリプト

アプリケーションのログファイルが肥大化するのを防ぐため、古いログを圧縮・削除するスクリプトを作成しましょう。

bash#!/bin/bash

# ログディレクトリの設定
LOG_DIR="/var/log/myapp"
ARCHIVE_DIR="/var/log/myapp/archive"
DAYS_TO_KEEP=7

変数で設定を定義しておくと、後で変更しやすくなりますね。

アーカイブディレクトリが存在しない場合は作成します。

bash# アーカイブディレクトリの作成
if [ ! -d "$ARCHIVE_DIR" ]; then
    mkdir -p "$ARCHIVE_DIR"
    echo "アーカイブディレクトリを作成しました: $ARCHIVE_DIR"
fi

-d は「ディレクトリが存在するか」を確認する条件です。mkdir -p は親ディレクトリも含めて作成します。

bash# 昨日以前のログファイルを圧縮
echo "古いログファイルを圧縮中..."
find "$LOG_DIR" -name "*.log" -type f -mtime +0 | while read -r logfile; do
    filename=$(basename "$logfile")
    gzip -c "$logfile" > "$ARCHIVE_DIR/${filename}.gz"
    echo "圧縮完了: $filename"
    rm "$logfile"
done

find コマンドでログファイルを検索し、while read ループで 1 つずつ処理します。-mtime +0 は「1 日以上前に更新されたファイル」を意味しますね。

最後に、古いアーカイブを削除します。

bash# 指定日数より古いアーカイブを削除
echo "${DAYS_TO_KEEP}日より古いアーカイブを削除中..."
find "$ARCHIVE_DIR" -name "*.gz" -type f -mtime +${DAYS_TO_KEEP} -delete

echo "ログ管理が完了しました"

このスクリプトを cron で定期実行すれば、ログ管理を完全に自動化できます。

例 3: デプロイ前チェックスクリプト

本番環境にデプロイする前に、複数の検証を自動で実行するスクリプトです。

bash#!/bin/bash

# 色付き出力の設定
RED='\033[0;31m'
GREEN='\033[0;32m'
NC='\033[0m' # No Color

色を使うと、成功・失敗が視覚的にわかりやすくなります。

チェック結果を管理するための変数を初期化します。

bash# チェック結果の初期化
ERRORS=0

Git の状態を確認する処理を追加しましょう。

bash# Git の状態チェック
echo "Git の状態を確認中..."
if [ -n "$(git status --porcelain)" ]; then
    echo -e "${RED}✗ コミットされていない変更があります${NC}"
    ERRORS=$((ERRORS + 1))
else
    echo -e "${GREEN}✓ 作業ディレクトリはクリーンです${NC}"
fi

git status --porcelain は変更があるファイルを簡潔な形式で出力します。-n は「文字列が空でない」を確認する条件ですね。

テストの実行結果を確認します。

bash# テストの実行
echo "テストを実行中..."
yarn test > /dev/null 2>&1

if [ $? -eq 0 ]; then
    echo -e "${GREEN}✓ 全てのテストが成功しました${NC}"
else
    echo -e "${RED}✗ テストが失敗しました${NC}"
    ERRORS=$((ERRORS + 1))
fi

> ​/​dev​/​null 2>&1 は、標準出力とエラー出力の両方を非表示にする記法です。

TypeScript の型チェックも実行しましょう。

bash# TypeScript の型チェック
echo "型チェックを実行中..."
yarn tsc --noEmit > /dev/null 2>&1

if [ $? -eq 0 ]; then
    echo -e "${GREEN}✓ 型エラーはありません${NC}"
else
    echo -e "${RED}✗ 型エラーが検出されました${NC}"
    ERRORS=$((ERRORS + 1))
fi

最後に、チェック結果をまとめて表示します。

bash# 結果の判定
echo ""
echo "=============================="
if [ $ERRORS -eq 0 ]; then
    echo -e "${GREEN}全てのチェックが成功しました!${NC}"
    echo "デプロイを実行できます。"
    exit 0
else
    echo -e "${RED}${ERRORS}個のエラーが検出されました${NC}"
    echo "エラーを修正してから再度実行してください。"
    exit 1
fi

終了ステータスを適切に設定することで、CI/CD パイプラインと連携しやすくなります。

例 4: 複数サーバーへの一括デプロイ

複数のサーバーに同じ処理を実行する場合のスクリプト例です。

bash#!/bin/bash

# デプロイ対象サーバーのリスト
SERVERS=(
    "user@server1.example.com"
    "user@server2.example.com"
    "user@server3.example.com"
)

配列を使ってサーバーリストを管理します。

デプロイコマンドを定義しましょう。

bash# デプロイコマンドの定義
DEPLOY_COMMAND="cd /var/www/myapp && git pull && yarn install && yarn build && pm2 reload all"

複数のコマンドを && で繋げることで、1 つでも失敗したら後続の処理を中断できますね。

各サーバーに対してループ処理を実行します。

bash# 各サーバーにデプロイ
for server in "${SERVERS[@]}"; do
    echo "=============================="
    echo "デプロイ先: $server"
    echo "=============================="

    ssh "$server" "$DEPLOY_COMMAND"

    if [ $? -eq 0 ]; then
        echo "✓ $server へのデプロイが完了しました"
    else
        echo "✗ $server へのデプロイが失敗しました"
    fi
    echo ""
done

"${SERVERS[@]}" は配列の全要素を展開する記法です。ssh コマンドでリモートサーバーに接続し、コマンドを実行します。

デプロイ完了の通知を送る処理も追加できます。

bash# デプロイ完了通知(Slack へ送信する例)
WEBHOOK_URL="https://hooks.slack.com/services/YOUR/WEBHOOK/URL"
MESSAGE="デプロイが完了しました: $(date '+%Y-%m-%d %H:%M:%S')"

curl -X POST -H 'Content-type: application/json' \
    --data "{\"text\":\"$MESSAGE\"}" \
    "$WEBHOOK_URL"

このようにして、複数サーバーへのデプロイを 1 回のコマンド実行で完了させられます。

まとめ

Shell Script は、コマンドラインでの操作を自動化する強力なツールです。

本記事では、Shell Script の基本から実行モデル、実務で役立つ具体例までご紹介しました。重要なポイントをまとめておきますね。

#ポイント内容
1自動化の重要性繰り返し作業を Shell Script で自動化すると効率が大幅に向上します
2基本構造シバン、変数、コマンドの組み合わせで構成されます
3実行モデル上から順番に 1 行ずつ実行され、条件分岐やループも可能です
4エラーハンドリング終了ステータス($?)を確認して適切に処理します
5活用領域環境構築、ログ管理、デプロイ、サーバー管理など幅広く使えます

Shell Script を書く際は、以下の点に気を付けると良いでしょう。

まず、コメントを充実させることです。数ヶ月後に見返したときに、何をしているのか理解できるようにしておきますね。

次に、変数で設定を管理することです。スクリプトの冒頭で変数を定義しておけば、後で変更しやすくなります。

そして、エラーハンドリングを忘れないことです。set -e を使えば、エラー発生時に自動的にスクリプトを終了できます。

bash#!/bin/bash
set -e  # エラーが発生したら即座に終了

最後に、小さく始めて段階的に拡張することです。最初から複雑なスクリプトを書くのではなく、シンプルなものから始めて、必要に応じて機能を追加していくと良いでしょう。

Shell Script は、一度書けば何度でも使えます。日々の作業で「これ、毎回同じことやってるな」と感じたら、それは Shell Script 化のチャンスですね。

ぜひ、実際に手を動かしてスクリプトを書いてみてください。最初は簡単なものから始めて、徐々に複雑な処理に挑戦していくことで、確実にスキルアップできますよ。

関連リンク