関数を定義する

関数を定義する とは

シェルスクリプトの基本、関数を定義する方法をターミナルで実際に試してみましょう。本レッスンでは、関数を定義する の基本から実際の使いどころまでを整理し、現場で迷わず使える形に落とし込みます。

なぜ重要か

スクリプトが 50 行を超えてくると、同じ処理が複数箇所に登場し始めます。ログを書き出す処理、エラー時の通知、ファイルの存在確認。これらをそのままコピペで増やすと、後から仕様変更があったときに修正漏れが発生し、保守不能なスクリプトに育ってしまいます。bash の 関数 (function) は、共通処理を 1 か所にまとめて再利用するための基本的な仕組みで、これを覚えると 100 行を超えるスクリプトの可読性が劇的に上がります。

関数の定義方法

bash の関数は次のいずれかの形で定義します。どちらでも動きますが、簡潔な前者がよく使われます。

ターミナル

greet() { echo "Hello, $1" } function greet() { echo "Hello, $1" }

呼び出し方は普通のコマンドと同じです。関数名のあとに引数を並べるだけです。

ターミナル

greet alice greet bob

プレーンテキスト

Hello, alice Hello, bob

関数の引数

関数内では $1, $2, $@, $# などが「関数の引数」を意味します。スクリプト本体の位置引数とは別物なので、混乱しないように注意してください。

ターミナル

add() { local a=$1 local b=$2 echo $((a + b)) } result=$(add 3 5) echo "$result" # 8

local とスコープ

関数の中で変数を定義するときは local を付けます。これを忘れると、関数で使った変数がスクリプト全体に漏れてしまい、思わぬ副作用の原因になります。

ターミナル

counter() { local i=0 while [[ "$i" -lt 3 ]]; do echo "$i" i=$((i + 1)) done }

関数の戻り値

bash の関数は「数値の終了ステータス」を返します。Python のように任意の値を return できるわけではありません。文字列を返したいときは、関数の中で echo して、呼び出し側で $(...) で取り込むのが定石です。

ターミナル

upper() { echo "$1" | tr a-z A-Z } result=$(upper hello) echo "$result" # HELLO

終了ステータスは return で返します。

ターミナル

is_root() { if [[ "$(id -u)" -eq 0 ]]; then return 0 else return 1 fi } if is_root; then echo "root user" else echo "normal user" fi

0 が成功、1 以上が失敗です。これは Unix の伝統的なルールに従っており、関数を if の条件にそのまま渡せるのが綺麗です。

呼び出しと戻り値の流れ

diagram (will load when visible)

関数の出力は標準出力に流れ、$(...) で文字列として受け取れる、という流れを掴んでおくと bash の関数が一気に使いやすくなります。

ログ関数の例

ターミナル

log() { local level=$1 shift local message="$*" printf '[%s] [%s] %s\n' "$(date +%H:%M:%S)" "$level" "$message" } log INFO "start backup" log ERROR "disk almost full"

shift$1 を取り除くと、残りの引数を $* でまとめて受け取れます。これでメッセージにスペースが入っていても自然に扱えます。

関数の整理パターン

大きめのスクリプトでは、最初に関数を全て定義し、最後で main 関数を呼ぶスタイルが扱いやすいです。

ターミナル

#!/bin/bash set -euo pipefail usage() { echo "usage: $0 <env>" } deploy() { echo "deploy to $1" } main() { if [[ $# -lt 1 ]]; then usage exit 1 fi deploy "$1" } main "$@"

main "$@" でスクリプト引数を全て main 関数に渡し、本体の処理はその中で進めます。Python の if __name__ == "__main__": に近い思想です。

小さなスクリプトでも main 関数にまとめておくと、後で source で読み込んだときに勝手に実行されない、関数だけ使い回したい場合に便利、というメリットがあります。実務で 100 行を超えそうなスクリプトは最初から main スタイルで書く癖を付けると保守しやすくなります。

関数の再利用

別ファイルにまとめた関数群は、source (または .) で読み込めます。

ターミナル

# lib.sh log() { echo "[LOG] $*" }

ターミナル

# main.sh source ./lib.sh log "start"

ライブラリ感覚で関数を共有でき、複数スクリプトでロジックを揃えやすくなります。

source はカレントシェルで実行されるため、関数だけでなくその場で変数も読み込まれます。逆に bash lib.sh だと別プロセスになり、関数は呼び出し元に残らないので注意してください。

まとめ

  • name() { ... } で関数を定義し、コマンドのように呼び出す
  • 関数内の引数は $1, $2, $@, $#
  • スコープは local で閉じるのが安全
  • 文字列の戻り値は echo + $(...) で受け取る
  • 終了ステータスは return 0/1 で返し、if の条件にも使える
  • 大きなスクリプトは main "$@" パターンで構造化する
  • 共通関数は別ファイルにまとめて source で読み込む

次のレッスン

次は 実践スクリプト作成 で、シェルスクリプトの基本、関数を定義する方法をターミナルで実際に試してみましょう を学びます。

事前確認 — 進む前に次の 3 つができることを確認しましょう。

  1. 関数の定義 の要点を自分の言葉で説明できる
  2. このレッスンの最小コード (または操作手順) を見ずに書ける
  3. 練習問題やクイズで間違えた箇所を読み直して理解した

理解度チェック (30 秒)

Q. 関数の定義 とは何か、1 文で説明してください。

この章のポイント

A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。

関連レッスン

課題

  1. greet 関数を定義する
  2. greet "Linux" を呼び出して Hello, Linux を出力

ヒント

script.sh
script.sh
学習モード
script.sh
ターミナル出力