並列処理や複数ファイル処理を自動化しようとすると、こういう壁にぶつかる。
原因はシンプルだ。bashの関数はデフォルトで現在のシェルにのみ存在し、子プロセスには引き継がれない。変数を環境変数としてexportするのと同じように、関数も子プロセスへ明示的にエクスポートしなければならない。
この記事では、
export -f を使ってbash関数を子プロセスへ渡す設計パターンを解説する。環境変数として渡される内部の仕組みや、bash以外のシェルには伝わらない制約も合わせて理解することで、安全に設計できるようになる。この記事のポイント
・export -f 関数名 で関数を子プロセスのbashへ渡せる
・渡された関数は環境変数 BASH_FUNC_関数名%% として格納される
・xargsやfind -execでは bash -c '...' 経由で呼び出す
・sh/dash/zshなどbash以外のシェルへは関数は引き継がれない
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
bash関数はなぜデフォルトで子プロセスに引き継がれないのか
bashには「現在のシェル」と「子プロセス」という2つの実行空間がある。スクリプト内で関数を定義しても、その関数はシェルの内部状態として存在するにすぎない。
bash -c "..." や外部スクリプトの起動では新しいbashプロセスが生まれるため、親プロセスの関数定義は存在しない状態でスタートする。変数の場合は
export 変数名 で環境変数として渡すことができる。関数も同様に、export -f 関数名 で子プロセスへ渡す仕組みがbashには用意されている。以下のコードで違いを確認してみよう。
# 変数と関数を定義する MY_VAR="hello" my_func() { echo "my_func called"; } # 変数はexportしてある、関数はまだexportしていない export MY_VAR # 子プロセスから呼び出す bash -c 'echo $MY_VAR' # -> hello(変数は渡る) bash -c 'my_func' # -> bash: my_func: command not found(関数は渡らない)
export -fの基本:関数を子プロセスへエクスポートする
1. export -fで関数を登録する
基本構文は次のとおりだ。# 関数を定義する log_info() { echo "[INFO] $(date '+%Y-%m-%d %H:%M:%S') $1" } # 子プロセスへエクスポートする export -f log_info
export -f は順番が逆になってはいけない。定義される前にエクスポートしようとするとエラーになる。2. bash -cで子プロセスを起動して呼び出す
エクスポートした関数は、bash -c で起動した子プロセスから直接呼び出せる。$ log_info() { echo "[INFO] $(date '+%Y-%m-%d %H:%M:%S') $1"; } $ export -f log_info $ bash -c 'log_info "接続確認完了"' [INFO] 2026-09-14 09:23:47 接続確認完了
bash -c の引数はシングルクォートで囲む。ダブルクォートにすると、引数内の変数が親シェルで先に展開されてしまい、意図した動作にならない場合がある。3. declare -Fxでエクスポート状態を確認する
現在のシェルでどの関数がエクスポート対象になっているかを確認するにはdeclare -Fx を使う。$ declare -Fx declare -fx log_info
-Fx の x はエクスポートフラグが立っている関数のみを表示するオプションだ。-F だけだと全関数が列挙されるため、エクスポート済みの関数を絞り込みたいときは -Fx を使う。内部の仕組み:環境変数BASH_FUNC_%%への格納
export -f がどう機能しているかを理解しておくと、デバッグ時に役立つ。bashは関数をエクスポートするとき、環境変数として格納する。変数名のルールはbashのバージョンによって異なる。
・bash 4.2以前:
関数名=() { ... } という名前の環境変数として格納・bash 4.3以降(ShellShock脆弱性対策後):
BASH_FUNC_関数名%% という名前の環境変数として格納実際に確認してみると、次のように見える。
$ export -f log_info $ env | grep BASH_FUNC BASH_FUNC_log_info%%=() { echo "[INFO] $(date '+%Y-%m-%d %H:%M:%S') $1" }
BASH_FUNC_xxx%% 形式の環境変数を検出して関数として再構築する。これがexport -fの実体だ。つまり、bashが環境変数を関数として解釈する仕組みを利用しているため、この機能はbash固有のものになる。sh/dash/zshなどの別シェルはこの環境変数を関数として解釈しないため、エクスポートした関数は存在しないものとして扱われる。
実践設計:xargsとfind -execへの適用
1. ログ関数をxargsのサブプロセスで使う
xargsが起動するサブプロセスにも関数を渡せる。ポイントはxargs bash -c の形にすることだ。#!/bin/bash # ログ関数の定義とエクスポート log_info() { echo "[INFO] $(date '+%Y-%m-%d %H:%M:%S') $1" } export -f log_info # ファイルリストを渡しながら関数を呼び出す ls /var/log/*.log | xargs -I{} bash -c 'log_info "処理中: $1"' _ {}
[INFO] 2026-09-14 09:24:12 処理中: /var/log/messages [INFO] 2026-09-14 09:24:12 処理中: /var/log/secure [INFO] 2026-09-14 09:24:12 処理中: /var/log/cron
bash -c '...' _ {} の _ は $0(スクリプト名)に相当するプレースホルダだ。$1 以降に実際の引数が入るため、この位置に _ またはダミー文字列を置く必要がある。ファイルパスを $1 で受け取ることで、パスにスペースが含まれていても正しく展開される。シェルスクリプトの設計力を体系的に身につけたい方は、シェルスクリプト実践講座(Linux Master Pro)もご覧ください。現場で使える設計パターンを体系的に学べます。
2. find -exec bash -cで複数ファイルを処理する
findと組み合わせる場合も同じ設計になる。次の例では、直近1日以内に更新された.logファイルをすべて検証する関数を子プロセスへ渡している。#!/bin/bash validate_file() { local file="$1" if [ ! -s "$file" ]; then echo "[WARN] 空ファイル: $file" return 1 fi echo "[OK] $file ($(wc -l < "$file") 行)" } export -f validate_file find /var/log -name "*.log" -mtime -1 -exec bash -c 'validate_file "$1"' _ {} \;
[OK] /var/log/messages (3842 行) [WARN] 空ファイル: /var/log/boot.log [OK] /var/log/secure (217 行)
-exec bash -c '...' _ {} \; の形式が基本パターンだ。find が {} に展開するファイルパスを $1 として受け取るため、スペースを含むパス名も安全に処理できる。bash以外のシェルへは渡らない制約と対処
export -f で渡した関数は、子プロセスが bash の場合のみ引き継がれる。以下のシェルではエクスポートした関数を使えない。・
/bin/sh(多くのLinuxではdashが実体)・zsh
・ksh / mksh
これらのシェルは
BASH_FUNC_xxx%% 形式の環境変数を関数として解釈しないため、エクスポートした関数は存在しないものとして扱われる。環境変数の設定方法はシェルごとに異なる。bashは
export を使い、csh 環境変数設定の解説 で解説しているcshの場合は setenv を使う。ただし関数のエクスポートについては、cshやzshにはbashの export -f に相当する仕組みはない。シェル制約への対処として、次の2点を徹底すると安全だ。
・スクリプトの先頭行を
#!/bin/bash で固定する・子プロセスの起動には
bash -c を明示する(sh -c や $SHELL -c は使わない)環境変数
SHELL の値に依存してシェルを選択する設計は、デプロイ先の環境によって sh や zsh が起動してしまう可能性があるため、本番スクリプトでは避けること。セキュリティ:ShellShock脆弱性と現代bashの対策
export -f を語るうえでShellShock(CVE-2014-6271、2014年発覚)に触れておく必要がある。bash 4.2以前では関数の環境変数名に制限がなく、任意の文字列を関数名として環境変数に仕込むことができた。これを悪用し、CGIやSSH経由で任意コードを実行させる攻撃が可能だった。bash 4.3以降ではこの問題に対し、関数の環境変数名を
BASH_FUNC_xxx%% という専用の形式に変更することで、通常の環境変数と混在しないように対策が取られた。現在のRHEL 9/Rocky Linux 9/Ubuntu 24.04環境で配布されているbashはこの修正が適用済みだ。バージョンを確認するには次のコマンドを使う。
# bashのバージョン確認 $ bash --version GNU bash, version 5.1.8(1)-release (x86_64-redhat-linux-gnu) # バージョン4.3以上であればShellShock対策適用済み
トラブルシュート:よくあるエラーと対処
「command not found」と出て関数が呼び出せない
原因として多いのは次の2つだ。・
export -f の呼び忘れ(関数定義だけでexportしていない)・子プロセスに渡したシェルがbash以外(
/bin/sh がdashの環境で sh -c を使った)bash -c 'declare -F 関数名' を実行して、子プロセスで関数が見えているか確認する。$ bash -c 'declare -F log_info' declare -f log_info # 表示されればエクスポート成功 $ bash -c 'declare -F log_info' # 何も出ない場合はexport -fが未実行
関数の定義より先にexport -fを呼んでしまった
export -f は定義済みの関数しかエクスポートできない。定義の後に export -f を呼ぶこと。エラーメッセージは次のようになる。$ export -f not_defined_func bash: export: `not_defined_func': not a function
子プロセスで定義した関数が親プロセスに影響しない
export -f は親から子への一方向の伝達だ。子プロセス側で関数を上書きしても、親プロセスには影響しない。子プロセスで共通処理を呼びたい場合はエクスポートを使い、逆方向(子から親)への関数の受け渡しにはexport -fは使えない。結果を返したい場合はファイル、パイプ、または終了ステータスで受け渡しを設計すること。本記事のまとめ
| やりたいこと | コマンド・設計ポイント |
|---|---|
| 関数を子プロセスへ渡す | export -f 関数名 |
| エクスポート済み関数を確認する | declare -Fx |
| xargsのサブプロセスで関数を呼ぶ | xargs -I{} bash -c '関数名 "$1"' _ {} |
| find -execのサブプロセスで関数を呼ぶ | find ... -exec bash -c '関数名 "$1"' _ {} \; |
| 子プロセス側で関数が見えるか確認する | bash -c 'declare -F 関数名' |
| bash以外のシェルに渡そうとしたとき | 関数は引き継がれない。bash -cを明示する |
シェルスクリプト講座を見る >>
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら
- 前のページへ:シェルスクリプトの関数でreturnとexitを取り違えない設計|errexitが効かない条件と終了ステータスの落とし穴
- この記事の属するカテゴリ:シェルスクリプトへ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。
登録無料・いつでも解除できます