シェルスクリプト 中級

Bashの関数:処理をまとめて再利用する

Bashスクリプトで関数を定義・呼び出す方法を学ぶ。引数の受け渡し・戻り値・ローカル変数を使ってコードを整理する技術を解説する

同じ処理を何度も書いていませんか? Bashの関数を使うと、まとまった処理に名前を付けて何度でも呼び出せます。 スクリプトが長くなってきたとき、関数で整理すると格段に読みやすくなります。

関数の基本的な書き方

Bashの関数は2通りの書き方があります。どちらも同じ意味です。

# 書き方1(function キーワードあり)
function greet() {
  echo "こんにちは!"
}

# 書き方2(function キーワードなし・より一般的)
greet() {
  echo "こんにちは!"
}

定義した関数は、関数名を書くだけで呼び出せます。

greet
# → こんにちは!

注意: 関数は必ず定義してから呼び出す必要があります。定義より前に呼び出すとエラーになります。

引数を受け取る

関数への引数は、$1$2$3 … という位置パラメータで受け取ります。 スクリプト全体の引数と同じ仕組みです。

greet_user() {
  echo "こんにちは、$1 さん!"
}

greet_user "田中"
# → こんにちは、田中 さん!

greet_user "山田" "鈴木"
# → こんにちは、山田 さん!($2 以降は無視)

$# で引数の数、$@ で全引数をまとめて参照できます。

show_args() {
  echo "引数の数: $#"
  echo "全引数: $@"
}

show_args apple banana cherry
# → 引数の数: 3
# → 全引数: apple banana cherry

ローカル変数で副作用を防ぐ

関数内で普通に変数を使うと、その変数はスクリプト全体(グローバル)に影響します。 local キーワードを使うと、その変数を関数内だけに限定できます。

count=10  # グローバル変数

bad_func() {
  count=99  # グローバルを上書きしてしまう
}

good_func() {
  local count=99  # この関数内だけの変数
}

bad_func
echo $count   # → 99(上書きされた!)

count=10
good_func
echo $count   # → 10(変わらない)

関数内の変数は原則として local を付けるクセをつけると、思わぬバグを防げます。

戻り値(終了ステータス)

Bashの関数は return で**終了ステータス(0〜255の整数)**を返します。 0 は成功、それ以外は失敗を表す慣例です。

is_even() {
  if (( $1 % 2 == 0 )); then
    return 0  # 成功(偶数)
  else
    return 1  # 失敗(奇数)
  fi
}

is_even 4
echo $?   # → 0(偶数)

is_even 7
echo $?   # → 1(奇数)

if 文と組み合わせると読みやすくなります。

if is_even 8; then
  echo "8 は偶数です"
fi
# → 8 は偶数です

文字列や計算結果を「返す」

終了ステータスは整数のみなので、文字列や計算結果を返すには echo で出力し、 呼び出し側でコマンド置換 $(...) を使って受け取ります。

add() {
  local result=$(( $1 + $2 ))
  echo $result
}

sum=$(add 3 7)
echo "合計: $sum"
# → 合計: 10

実践例:ログ出力関数

実務的なスクリプトでよく使われるパターンです。

#!/bin/bash

# タイムスタンプ付きでメッセージを出力する関数
log() {
  local level="$1"
  local message="$2"
  echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$level] $message"
}

# ファイルの存在チェック関数
check_file() {
  local filepath="$1"
  if [[ -f "$filepath" ]]; then
    log "INFO" "$filepath が見つかりました"
    return 0
  else
    log "ERROR" "$filepath が見つかりません"
    return 1
  fi
}

# 使用例
check_file "/etc/hosts"
check_file "/etc/nonexistent"

実行結果の例:

[2026-07-21 10:30:00] [INFO] /etc/hosts が見つかりました
[2026-07-21 10:30:00] [ERROR] /etc/nonexistent が見つかりません

練習問題

問題1

以下の仕様を満たす関数 max を作成してください。

  • 2つの整数を引数として受け取る
  • 大きい方の数を標準出力に出力する
max 5 12   # → 12
max 99 3   # → 99
max 7 7    # → 7

問題2

ディレクトリのパスを引数に受け取り、そのディレクトリ内の .log ファイルの数を返す関数 count_logs を作成してください。

count_logs /var/log   # → (/var/log内の.logファイル数)

問題3

次のスクリプトには問題があります。何が問題で、どう修正すればよいか説明してください。

#!/bin/bash

result=0

calculate() {
  result=$(( $1 * $2 ))
  echo "計算結果: $result"
}

calculate 3 4
echo "result の値: $result"

解答

問題1の解答

max() {
  if (( $1 >= $2 )); then
    echo $1
  else
    echo $2
  fi
}

max 5 12   # → 12
max 99 3   # → 99
max 7 7    # → 7

問題2の解答

count_logs() {
  local dir="$1"
  local count
  count=$(find "$dir" -maxdepth 1 -name "*.log" 2>/dev/null | wc -l)
  echo "$count"
}

count_logs /var/log

find で指定ディレクトリ直下(-maxdepth 1)の .log ファイルを検索し、 wc -l で行数(=ファイル数)を数えています。 2>/dev/null はアクセス権エラーを無視するためのものです。

問題3の解答

問題点: 関数内の変数 resultlocal で宣言されていないため、グローバル変数の result を上書きしています。 この場合は意図的にグローバルを更新しているように見えますが、関数が増えていくと予期しない変数の上書きが起こりやすくなります。

修正方法:

#!/bin/bash

calculate() {
  local result=$(( $1 * $2 ))  # local を付けてスコープを限定
  echo "計算結果: $result"
}

calculate 3 4
# result はこちらでは未定義(または別途定義したグローバル値)

関数内で使う一時変数には local を付ける習慣が、バグを防ぐうえで重要です。