bashスクリプト入門:最初のスクリプト、変数、条件、ループ、cron
bashスクリプトは、シェルのコマンドを書いたテキストファイルです。1行目は #!/bin/bash で始まり、chmod +x で実行権限を与えて ./script.sh のように実行します。変数、引数 $1 と $2、if 条件、for と while のループを使います。定期実行は crontab -e で設定します。
bashスクリプトとは、ターミナルで1行ずつ入力していたコマンドを書き並べた、普通のテキストファイルです。スクリプトは #!/bin/bash というshebangの行で始まり、chmod +x で実行権限を与え、./script.sh として起動します。この記事は、bashスクリプト入門者のための解説です。最初の1行から、変数、引数、条件、ループ、エラー処理、cronによる定期実行まで扱います。
1分で最初のbashスクリプト
nanoなど好きなエディタでファイルを作成します。
nano hello.sh
#!/bin/bash
# 最初のスクリプト:挨拶とシステムの簡単な情報
echo "こんにちは、$USER さん!"
echo "今日は $(date +%Y-%m-%d) です"
echo "現在の場所:$(pwd)"
保存(Ctrl+O、Enter、Ctrl+X)したら、実行権限を与えて起動します。
$ chmod +x hello.sh
$ ./hello.sh
こんにちは、alice さん!
今日は 2026-10-12 です
現在の場所:/home/alice
# で始まる行はコメントです。$(コマンド) は、コマンドの出力を文字列の中に埋め込みます。
Shebang:1行目の役割
#!/bin/bash は、ファイルをどのインタープリタで実行するかをシステムに伝えます。この行がないと、そのとき使えるシェルで実行されます。一部のシステムでは /bin/sh がbashではなく、より簡易な dash であり、bash特有の構文が動かないことがあります。
#!/usr/bin/env bash という書き方もよく使われます。これは PATH からbashを探すため、bashが /bin 以外にインストールされている場合に便利です。
スクリプトの実行方法と ./ が必要な理由
実行方法は2つあります。
./hello.sh:shebangと実行権限xが必要です。現在のディレクトリはPATHに含まれないため、./を付けます。bash hello.sh:インタープリタを明示するので、実行権限は不要です。
./ を付けずに command not found と表示されたり、chmod +x を忘れて Permission denied と表示されたりするのは、まさにこの2つが原因です。自分のスクリプトをどこからでも名前だけで呼び出したい場合は、~/bin または /usr/local/bin に置きます。
変数
変数は = の前後に空白を入れずに代入し、$ を付けて参照します。
#!/bin/bash
name="backup"
dir="/var/backups"
today=$(date +%F)
echo "ファイル:${dir}/${name}-${today}.tar.gz"
波括弧は、変数名と後に続く文字列を区切ります。name = "backup" のように空白を入れるとエラーです。bashは name という名前のコマンドを実行しようとします。
引用符が重要
二重引用符の中では変数が展開されますが、一重引用符の中では展開されません。変数はほぼ常に二重引用符で囲むべきです。囲まないと、空白を含むファイル名が2つの引数に分かれてしまいます。
file="my report.txt"
rm $file # エラー:「my」と「report.txt」を削除しようとする
rm "$file" # 正しい
echo '$HOME' # そのまま $HOME と表示
echo "$HOME" # /home/alice と表示
環境変数
$HOME、$USER、$PATH などはシステムが設定します。独自の変数は export で子プロセスに渡せます。恒久的な環境設定は ~/.bashrc または ~/.profile に書きます。
スクリプトの引数
起動時に渡した値は、$1、$2 などで参照できます。
| 変数 | 内容 |
|---|---|
$0 | スクリプト名 |
$1、$2… | 最初、2番目の引数 |
$# | 引数の数 |
"$@" | すべての引数を1つずつ |
$? | 直前のコマンドの終了コード |
$$ | 現在のスクリプトのPID |
#!/bin/bash
echo "スクリプト $0 は $# 個の引数を受け取りました"
echo "1番目:$1"
$ ./args.sh nginx 80
スクリプト ./args.sh は 2 個の引数を受け取りました
1番目:nginx
ユーザーからの入力は read で読み取ります。例:read -p "名前を入力:" name。
条件 if
すべてのコマンドは終了コードを返します。0は成功、それ以外は失敗を表します。if はこのコードを調べます。比較には通常 [[ ... ]] を使い、括弧の内側の空白は必須です。
#!/bin/bash
if [[ $# -eq 0 ]]; then
echo "使い方:$0 <ディレクトリ>"
exit 1
fi
if [[ -d "$1" ]]; then
echo "ディレクトリ $1 は存在します"
elif [[ -f "$1" ]]; then
echo "$1 はファイルであり、ディレクトリではありません"
else
echo "$1 は見つかりません"
fi
| 判定 | 意味 |
|---|---|
-f file | 通常のファイルが存在する |
-d dir | ディレクトリが存在する |
-e path | パスが存在する |
-x file | ファイルを実行できる |
-z "$var" / -n "$var" | 文字列が空 / 空でない |
"$a" == "$b"、!= | 文字列の比較 |
-eq -ne -lt -le -gt -ge | 数値の比較 |
任意のコマンドの結果を直接判定することもできます。
if systemctl is-active --quiet nginx; then
echo "nginx は動作中です"
else
echo "nginx は停止しています"
fi
選択肢が多い場合は case が便利です。
case "$1" in
start) echo "起動" ;;
stop) echo "停止" ;;
*) echo "不明なコマンド:$1"; exit 1 ;;
esac
ループ for と while
for:リストを順に処理する
for host in web1 web2 db1; do
echo "$host を確認中"
ping -c 1 -W 1 "$host" > /dev/null && echo " 応答あり" || echo " 応答なし"
done
for file in /var/log/*.log; do
echo "$file:$(wc -l < "$file") 行"
done
for i in {1..5}; do
echo "$i 回目の試行"
done
while:条件が成り立つ間繰り返す
代表的な例は、ファイルを1行ずつ読むことです。
while read -r user; do
echo "ユーザー:$user"
done < users.txt
サービスの起動を待つ例です。
count=0
until curl -sf http://localhost:8080/health > /dev/null; do
count=$((count + 1))
if [[ $count -ge 10 ]]; then
echo "サービスが起動しませんでした"
exit 1
fi
sleep 3
done
echo "サービスの準備ができました"
bashの算術演算は二重括弧で書きます:$((a + b))。整数のみを扱えます。
関数
繰り返し現れるコードは関数にまとめます。関数の中でも $1、$2 などの引数を使えます。local を付けると、変数が関数の外に漏れません。
log() {
local level="$1"; shift
echo "$(date '+%F %T') [$level] $*"
}
log INFO "バックアップを開始します"
log ERROR "ディスクの空き容量が不足しています"
エラー処理:set -euo pipefail
デフォルトでは、bashはコマンドが失敗しても処理を続けます。そのため、バックアップスクリプトが何もコピーしていないのに「成功」として終わることがあります。そこで、スクリプトの先頭に厳格モードを書くのが一般的です。
#!/bin/bash
set -euo pipefail
-e:最初のエラーで停止します。-u:宣言されていない変数の参照をエラーにします(DIRが空のときにrm -rf "$DIR/"が実行される事故を防ぎます)。-o pipefail:パイプラインのどれか1つでも失敗すれば、パイプライン全体を失敗とみなします。
デバッグには bash -x script.sh を使います。bashが各コマンドを実行前に表示します。実行前に典型的なミスを見つけるには shellcheck が役立ちます。
実例:バックアップスクリプト
#!/bin/bash
set -euo pipefail
SRC="${1:-/var/www}"
DST="/var/backups/site"
KEEP_DAYS=7
STAMP=$(date +%F_%H-%M)
mkdir -p "$DST"
tar -czf "$DST/site-$STAMP.tar.gz" -C "$(dirname "$SRC")" "$(basename "$SRC")"
echo "アーカイブを作成しました:$DST/site-$STAMP.tar.gz"
# KEEP_DAYS 日より古いアーカイブを削除
find "$DST" -name "site-*.tar.gz" -mtime +"$KEEP_DAYS" -delete
${1:-/var/www} は「1番目の引数を使い、なければ /var/www を使う」という意味です。スクリプトを /usr/local/bin/backup-site.sh として保存し、実行可能にします:sudo chmod +x /usr/local/bin/backup-site.sh。
定期実行:cron
cronは、決まった時刻にコマンドを実行するサービスです。ユーザーのジョブは crontab -e で編集し、crontab -l で表示します。rootのジョブは sudo crontab -e で編集します。
ジョブの1行は、5つの時刻フィールドとコマンドからなります。
# 分 時 日 月 曜日 コマンド
30 2 * * * /usr/local/bin/backup-site.sh >> /var/log/backup.log 2>&1
| スケジュール | 実行タイミング |
|---|---|
*/5 * * * * | 5分ごと |
0 * * * * | 毎時0分 |
30 2 * * * | 毎日2時30分 |
0 9 * * 1-5 | 平日の9時 |
0 3 1 * * | 毎月1日の3時 |
@reboot | 起動後に1回 |
手動では動くのに、cronでは動かない理由
- 環境が違う。cronは最小限の
PATHで動き、~/.bashrcを読みません。コマンドやファイルはフルパスで書くか、crontabの冒頭でPATHを設定してください。 - カレントディレクトリが違う。ジョブはホームディレクトリで始まります。スクリプトの場所ではありません。絶対パスを使ってください。
- % 記号。crontabでは
%は改行を意味します。そのためdate +%Fをジョブの行に直接書く場合は\%とエスケープが必要です。コマンドをスクリプトに入れる方が簡単です。 - 出力が失われる。出力はログにリダイレクトしてください(
>> file 2>&1)。そうしないと、エラーが見えません。cronの実行履歴はjournalctl -u cronで確認できます(RHEL系ではサービス名はcrond)。
systemdを使うシステムでは、cronの代わりにsystemdタイマーを使えます。タイマーは出力をジャーナルに記録し、実行を逃した場合に追いつくこともできます。
まとめ
bashスクリプトの入門は、shebang #!/bin/bash を持つファイルに実行権限を与え、コマンドを順に並べることから始まります。変数は引用符で囲み、if で引数を確認し、for と while でループを書き、厳格モード set -euo pipefail を有効にしましょう。そうすれば、cronで定期的に実行できる信頼性の高い自動化ツールになります。bashスクリプト、環境、cronは、Linuxコースで実践を通して解説しています。
よくある質問
bashとshの違いは何ですか?
shは標準のPOSIXシェルで、bashはその拡張版です。配列、二重括弧などの便利な機能があります。bashの構文を使ったスクリプトはshではなく、bashで実行する必要があります。
bashスクリプトの拡張子は何にすればいいですか?
Linuxは拡張子を見ません。インタープリタはshebangで決まります。.sh は人が見て分かるようにするための慣習です。
スクリプトにパスワードを安全に渡すにはどうすればいいですか?
引数で渡さないでください。引数は ps で他のユーザーにも見えます。権限600のファイルに保存した環境変数を使うか、read -s で画面に表示せずに入力してください。
スクリプトがrootで実行されているか確認するにはどうすればいいですか?
ユーザーIDを0と比較します。if [[ $EUID -ne 0 ]]; then echo "root権限が必要です"; exit 1; fi のように書きます。
SSHを切断した後も、スクリプトをバックグラウンドで動かし続けるにはどうすればいいですか?
nohup ./script.sh & を使うか、tmuxやscreenのセッションを使います。恒久的に動かす場合は、スクリプトをsystemdサービスとして登録してください。
このテーマはコース「Linux: с нуля до уверенного администрирования」で実践とともに詳しく解説しています。
- Bash-скрипты: от команды в терминале до рабочего скрипта автоматизации
- Пакеты, окружение и первая автоматизация: apt, профиль и cron