.bashrcや.zshrcに定義した関数を他のシェルスクリプトから呼び出す

Bash

はじめに

Unix系OSで日頃よく使うコマンドは、 .bashrc.zshrcaliasfunction を定義しておき、コマンドラインから呼び出すことだと思います。

僕も .bashrc.bashrc に自分のお気に入りのエイリアスや関数を定義していますが、この関数を別のシェルスクリプトから呼び出したいというシーンが有りました。

ちょっと工夫をしないと呼び出せなかったこともあり、やり方を掲載したいと思います。

検証環境

$ uname -moi
x86_64 x86_64 GNU/Linux

$ bash -version | head -n 1
GNU bash, version 4.2.46(2)-release (x86_64-redhat-linux-gnu)

まずは .bashrc に関数を定義してみる

まずは自分の .bashrc に関数を定義してみます。
ただ渡された引数を出力するだけの print_params という、関数です。

$ cat <<'EOF' >>~/.bashrc
print_params() {
  echo "parms are ${@}."
}
EOF

.bashrc に追加した関数は以下のいずれかの方法で反映できます。

  1. ログインし直す
  2. bash コマンドを実行し、シェルを新しいプロセスで立ち上げる
  3. . ~/.bashrc コマンドを実行し、現在事項中プロセスのシェルに関数を反映させる

今回は3つめの方法で反映させます。

$ . ~/.bashrc

関数が作成されたかどうか確認してみます。

# 指定された名前が関数かどうかを確認
$ type print_params
print_params is a function
print_params ()
{
    echo "parms are ${@}."
}

# 関数を実行してみる
$ print_params 1 2 3
parms are 1 2 3.

正しく関数が作成されました。
ここからは、作成された関数を他のシェルから呼び出してみます。

.bashrc に定義されている関数は別のシェルスクリプトからは呼び出せない

test.sh というファイルを作成し、先程 .bashrc で定義した関数を呼び出してみます。

# シェルスクリプトを作成
$ cat <<'EOF' >./test.sh
#!/usr/bin/env bash

print_params 1 1 2 3 5 8
EOF

# 実行権限を付与
$ chmod +x ./test.sh

このスクリプトを実行してみます。

$ ./test.sh
./test.sh: line 3: print_params: command not found

エラーが発生してしまいました。
print_params というコマンドが見つからない、と言っています。

親シェルプロセスで定義したシェル関数は子シェルプロセスから見えない

「親シェルプロセス」で定義した関数は「子シェルプロセス」から見えません。

Bashの関数は、現在実行中のシェルプロセス内でしか利用することはできません。

./test.sh のように、シェルスクリプトを実行すると、「子シェルプロセス」が自動的に立ち上がり、シェルのロジックが実行されます。
したがって、「親シェルプロセス」で定義した関数 print_params が見えなかったのです。

これを解決する方法はないのでしょうか?

ビルトインコマンド export で関数を公開する

Bashのビルトインコマンド export は、変数を「子シェルプロセス」に 公開 するために利用します。

実は export コマンドを使って、関数も「子シェルプロセス」に 公開 することができます。

先程の ./test.sh を実行する前に関数を公開してみましょう。

$ export -f print_params

この状態で、先程の ./test.sh を実行してみます。

$ ./test.sh
parms are 1 1 2 3 5 8.

エラーは表示されず、正しく実行できました。

再度ログインしてきた場合にも公開されるようにする

一度ログアウト、あるいは端末を終了し、再度Bashを起動してみましょう。
./test.sh を実行すると、またまた関数が見つからないことによるエラーが発生するかと思います。

毎回ログインするたびに export -f print_params するのは面倒ですので、こちらも ~/.bashrc に記述しておきましょう。

cat <<EOF >>~/.bashrc
export -f print_params
EOF

これで、今後はログインした直後に自動的に関数が子プロセスに公開されるようになります。

ちょっとしたコールバック関数を用意してみる

この仕組を利用して、ちょっとしたコールバック関数を用意することもできます。

callback という名前の関数にコマンドライン引数をそのまま引き渡すシェルスクリプトを用意してみます。

$ cat <<'EOF' >./calc.sh
#!/usr/bin/env bash

callback $1 $2
EOF

$ chmod +x calc.sh

この状態では、当然実行するとエラーとなります。

$ ./calc.sh 3 5
./calc.sh: line 3: callback: command not found

ここで callback という名前の関数を作成、公開してみます。
関数は2つの引数を加算した結果を出力する関数です。

callback() {
  echo $(($1 + $2))
}
export -f callback

再度先程のスクリプトを実行してみます。

$ ./calc.sh 3 5
8

加算結果が返却されました。

今度は減算してみましょう。

callback() {
  echo $(($1 - $2))
}

一度関数を公開していれば、関数のロジック変更後に再度公開する必要はありません。
スクリプトを実行してみます。

$ ./calc.sh 3 5
-2

ひとこと

便利そうですが、関数を使わずシェルスクリプトを使えばなんとかなるということもあり、あまり利用するケースは多くないかもしれません。

関数のメリットはシェルスクリプトの読み込みのオーバヘッドが減らせる点でしょうか。

同じロジック( echo 1 するだけ)のシェルスクリプト x.shx_func を10000回呼び出した場合の処理速度の違いは以下の通りでした。

$ time bash -c "seq 1 10000 | xargs -n 1 ./x.sh" > /dev/null
real    0m30.506s
user    0m6.018s
sys     0m13.288s

$ time bash -c "seq 1 10000 | xargs -n 1 bash -c 'x_func'" > /dev/null
real    0m12.925s
user    0m4.268s
sys     0m8.613s

Bash