シェルスクリプトとは
シェルは、ターミナルへ入力したコマンドを解釈して実行するプログラムです。シェルスクリプトは、そのコマンドをファイルへ保存し、変数、条件分岐、繰り返し、関数などを加えて再利用できるようにしたものです。
- 指定したファイルのバックアップ
- ログから必要な行だけを集める処理
- 複数のファイルをまとめて整理する処理
- 開発環境の確認や準備を自動化する処理
シェルスクリプトは短く書けますが、ファイル名の空白、入力値、権限、コマンドの失敗を意識する必要があります。この講座では、まず動く例を試し、そのあと安全に書き換える順番で進みます。
実行環境を確認する
Linux、WSL、macOSのターミナルなど、Bashが動く環境を使います。まず自分がどのシェルを使っているかと、Bashのバージョンを確認しましょう。
printf '現在のシェル: %s\n' "$SHELL"
bash --version | head -n 1
pwd現在のシェル: /bin/bash
GNU bash, version 5.2.21(1)-release (x86_64-pc-linux-gnu)
/home/mityバージョンやホームディレクトリは環境によって異なります。以降は、重要なファイルを置かない練習用ディレクトリを作って作業します。
mkdir -p ~/shellscript-practice
cd ~/shellscript-practice
pwd/home/mity/shellscript-practicepwd、対象確認にlsを実行する習慣を付けます。最初のスクリプトを作る
hello.shというファイルを作り、Bashへ処理を渡します。先頭のシバンは、このファイルをどのプログラムで実行するかを示します。
#!/usr/bin/env bash
printf 'こんにちは、シェルスクリプト!\n'bash hello.sh
chmod u+x hello.sh
./hello.shこんにちは、シェルスクリプト!
こんにちは、シェルスクリプト!bash hello.shはBashへファイルを渡す方法、./hello.shは実行権限を付けたファイルを直接実行する方法です。./は現在のディレクトリを表します。
変数と引用符
変数へ値を入れるときは、変数名の前に$を付けません。値を取り出すときに$nameと書きます。代入の=の前後に空白は入れません。
#!/usr/bin/env bash
name='Mity'
score=85
printf '%sさんの得点は%s点です。\n' "$name" "$score"Mityさんの得点は85点です。文字列は基本的にダブルクォートで囲み、変数の展開を止めたい固定文字列にはシングルクォートを使います。特にパスや入力値を使うときは、空白で分割されないよう必ず引用します。
directory="$HOME/shellscript-practice"
today=$(date '+%Y-%m-%d')
printf '作業場所: %s\n日付: %s\n' "$directory" "$today"バッククォートではなく$(...)を使うと、入れ子にしやすく読みやすくなります。詳しくは変数・引用・終了ステータスで練習します。
コマンドを組み合わせる
コマンドの標準出力は、画面へ表示するだけでなく、ファイルへ保存したり別のコマンドへ渡したりできます。
| 書き方 | 役割 |
|---|---|
> file | 出力をファイルへ書き込み、既存内容を上書き |
>> file | 出力をファイル末尾へ追加 |
command1 | command2 | 左の出力を右の入力へ渡す |
command1 && command2 | 左が成功したときだけ右を実行 |
printf 'apple\norange\ngrape\n' > fruits.txt
grep 'an' fruits.txt
cat fruits.txt | wc -lorange
3grepは一致する行、wc -lは行数を表示します。パイプをつなぐと処理を小さく分けられますが、途中の結果を確認しながら組み立てることが大切です。
条件分岐
ifと[[ ... ]]を使うと、ファイルの存在や文字列、数値を確認できます。
file='fruits.txt'
if [[ -f "$file" ]]; then
printf '%s は通常ファイルです。\n' "$file"
elif [[ -d "$file" ]]; then
printf '%s はディレクトリです。\n' "$file"
else
printf '%s は見つかりません。\n' "$file" >&2
exit 1
fifruits.txt は通常ファイルです。-fは通常ファイル、-dはディレクトリを意味します。>&2でエラーメッセージを標準エラーへ送り、exit 1で失敗を呼び出し元へ伝えています。
count=$(wc -l < fruits.txt)
if (( count >= 3 )); then
printf '3行以上あります。\n'
else
printf '3行未満です。\n'
fi3行以上あります。繰り返しと配列
複数のファイルや値を順番に処理するときはforを使います。回数が条件で決まる処理や、入力を一行ずつ読む処理にはwhileを使います。
colors=('red' 'green' 'blue')
for color in "${colors[@]}"; do
printf '色: %s\n' "$color"
done色: red
色: green
色: blue"${colors[@]}"は配列の要素を一つずつ保ったまま展開します。単純な$colorsでは先頭要素だけになるため、配列全体を扱うときは書き方に注意します。
while IFS= read -r line; do
printf '読み込み: %s\n' "$line"
done < fruits.txtIFS=とread -rを付けると、行頭の空白やバックスラッシュを意図せず変換しにくくなります。
ファイルとテキストを処理する
ファイル処理では、対象を確認してから作成・コピー・移動します。まずは練習用ディレクトリの中だけで試してください。
mkdir -p reports/archive
printf 'done\n' > reports/today.txt
cp reports/today.txt reports/archive/today.txt
find reports -type f -name '*.txt' -printreports/archive/today.txt
reports/today.txtfindの検索条件は、いきなり-deleteへつなげず、最初は-printで対象を確認します。ファイル名に空白があっても扱えるよう、パスを変数へ入れるときも展開するときも引用します。
printf 'INFO start\nERROR timeout\nINFO retry\n' > application.log
cat application.logINFO start
ERROR timeout
INFO retrylog='application.log'
if [[ ! -f "$log" ]]; then
printf 'ログがありません: %s\n' "$log" >&2
exit 1
fi
grep -n 'ERROR' "$log" > errors.txt || true
printf 'エラー一覧を errors.txt に保存しました。\n'エラー一覧を errors.txt に保存しました。一致する行がないとgrepは終了ステータス1を返します。検索結果が空でもスクリプトを失敗にしたくない場合に|| trueを使う例ですが、エラーを隠すために常用してはいけません。
関数と引数
処理を関数へ分けると、同じ確認を複数の場所で再利用できます。関数内の変数にはlocalを付け、外側の変数を意図せず変更しないようにします。
show_usage() {
printf '使い方: %s ファイル名\n' "$0"
}
if [[ $# -ne 1 ]]; then
show_usage >&2
exit 2
fi
target="$1"
printf '対象: %s\n' "$target"bash usage.sh reports対象: reports$0はスクリプト名、$1は1番目の引数、$#は引数の個数です。引数が足りないときに使い方を表示して終了すると、実行方法が分かりやすくなります。
is_readable() {
local path="$1"
[[ -r "$path" ]]
}
if is_readable 'fruits.txt'; then
printf '読み込み可能です。\n'
fi読み込み可能です。関数の最後に実行したコマンドの終了ステータスが、関数の戻り値になります。値を表示する関数と、成功・失敗を返す関数を使い分けます。
安全な書き方と次の学習
自動化する処理ほど、成功したときだけでなく失敗したときの動きを決めておく必要があります。未定義変数、パイプ途中の失敗、入力値を確認し、ログを残すようにします。
#!/usr/bin/env bash
set -Eeuo pipefail
log() {
printf '[%s] %s\n' "$(date '+%F %T')" "$*"
}
trap 'printf "エラー: %s行目\n" "$LINENO" >&2' ERR
log '処理を開始します。'[2026-10-09 10:00:00] 処理を開始します。set -e、set -u、set -o pipefailは便利ですが、例外的に失敗を許したい処理との相性を確認する必要があります。意味を理解してから少しずつ追加しましょう。
各テーマの詳しい例は、上部の講座一覧から進めます。最後に総合練習問題で、ログ集計とバックアップを組み合わせた小さなスクリプトを作ります。