シェルスクリプトの関数を子プロセスへ渡す設計|export -fで別プロセス側から自作処理を使えるようにする

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOMELinux技術 リナックスマスター.JP(Linuxマスター.JP)シェルスクリプト > シェルスクリプトの関数を子プロセスへ渡す設計|export -fで別プロセス側から自作処理を使えるようにする
「bashで関数を作ったのに、xargsやfind -execのbash -cの中から呼び出せない」
並列処理や複数ファイル処理を自動化しようとすると、こういう壁にぶつかる。

原因はシンプルだ。bashの関数はデフォルトで現在のシェルにのみ存在し、子プロセスには引き継がれない。変数を環境変数としてexportするのと同じように、関数も子プロセスへ明示的にエクスポートしなければならない。

この記事では、export -f を使ってbash関数を子プロセスへ渡す設計パターンを解説する。環境変数として渡される内部の仕組みや、bash以外のシェルには伝わらない制約も合わせて理解することで、安全に設計できるようになる。

この記事のポイント

export -f 関数名 で関数を子プロセスのbashへ渡せる
・渡された関数は環境変数 BASH_FUNC_関数名%% として格納される
・xargsやfind -execでは bash -c '...' 経由で呼び出す
・sh/dash/zshなどbash以外のシェルへは関数は引き継がれない


「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

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

-Fxx はエクスポートフラグが立っている関数のみを表示するオプションだ。-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が起動すると、この 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 の値に依存してシェルを選択する設計は、デプロイ先の環境によって shzsh が起動してしまう可能性があるため、本番スクリプトでは避けること。

セキュリティ: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対策適用済み

古いOSをベースにしたコンテナイメージや、長期間アップデートされていない環境では念のためバージョンを確認しておくとよい。

トラブルシュート:よくあるエラーと対処

「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を明示する
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、20年以上の運用経験を持つ現役エンジニアが基礎から教えます。
シェルスクリプト講座を見る >>

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録
宮崎 智広

この記事を書いた人

宮崎 智広(みやざき ともひろ)

株式会社イーネットマーキュリー代表。現役のLinuxサーバー管理者として20年以上の実務経験を持ち、これまでに累計3,100名以上のエンジニアを指導してきたLinux教育のプロフェッショナル。「現場で本当に使える技術」を体系的に伝えることをモットーに、実践型のLinuxセミナーの開催や無料マニュアルの配布を通じてLinux人材の育成に取り組んでいる。

趣味は、キャンプにカメラ、トラウト釣り。好きな食べ物は、ラーメンにお酒。休肝日が作れない、酒量を減らせないのが悩み。最近、ドラマ「フライトエンジェル」を観て涙腺が崩壊しました。