マニュアルによるとシェル関数とシェルコマンドの違いは「それを起動するシェルプロセスで実行されるか、フォークされて別プロセスで実行されるか」らしいです。
% function echo-pid-function {
function> echo $$
function> }
% cat << 'EOF' > echo-pid-command
heredoc> #!/bin/zsh
heredoc> echo $$
heredoc> EOF
% echo $$
84532
% echo-pid-function
84532 # シェルと同じ
% ./echo-pid-command
84949 # シェルと違う
バックグラウンドに移した場合の挙動の違い
ctrl-zを押した時にフォアグラウンドジョブがバックグラウンドに移りますが、シェル関数はシェルと同じプロセス上で動いているので、シェル関数実行中にctrl-zすると、シェルが新しくフォークされます。
この違いが特に重要なのは、バックグラウンドに移された関数内で環境変数を設定している場合です。
% function test-func { FOO="bar${FOO}" }
% test-func; echo $FOO
bar
% test-func; echo $FOO
barbar
% test-func &
[1] 6053
[1] + done test-func
% echo $FOO # barbarbar にならない
barbar
ちなみにこの挙動はZsh特有で他のシェルとは異なるものなので注意が必要です。
シェル関数の利点
同一プロセス内で実行されることからシェル関数にはコマンドに比べて下記の利点があると言えそうです。
- フォークしないので処理の立ち上がりが若干速い。
- 同じプロセスから実行した全ての関数が同じプロセス上で動作するので、関数同士が簡単に環境変数を使ってやりとりができる。
- コマンドの場合、プロセスが異なるので、親プロセスの環境変数を引き継ぎますが、子供で環境変数を変更してもその値は親プロセスには影響しない。
Zshの補完機能が関数で定義として実装されているのは、特に2つ目の利点の影響が大きそうです。