bash で関数を定義するときに function name { ... } 以外に function name ( ... ) と書くことができると知りました。
bash のマニュアル (Bash Reference Manual) を参照したところ、関数の本体は compound-command と定義されており、他の書き方もできることが分かりました。
bash の関数定義
bash では、関数を定義する構文が 2 種類あります。どちらの構文であっても、関数の本体は compound-command です。
Bash Reference Manual > 3.3 Shell Functions より引用:
Functions are declared using this syntax:
fname () compound-command [ redirections ]or
function fname [()] compound-command [ redirections ]
bash の compound-command
compound-command は Bash Reference Manual > 3.2.5 Compound Commands で説明されているとおり、Looping Constructs, Conditional Constructs, Grouping Commands のいずれかです。
- Looping Constructs ...
for,while,until - Conditional Constructs ...
if,case,[[ expression ]]など - Grouping Commands ...
( ... ),{ ... }
Looping Constructs の使用例
Looping Constructs を用いると、関数定義を次のように書き換えることができます。
# before
function f
{
for x in 1 2 3
do
echo $x
done
}
# after
function f
for x in 1 2 3
do
echo $x
done
Conditional Constructs の使用例
Conditional Constructs を用いると、関数定義を次のように書き換えることができます。
# before
function f
{
case "$1" in
1)
echo "one"
;;
2)
echo "two"
;;
*)
echo "many"
;;
esac
}
# after
function f
case "$1" in
1)
echo "one"
;;
2)
echo "two"
;;
*)
echo "many"
;;
esac
Grouping Commands の使用例
Grouping Commands には current-shell で実行される { ... } と sub-shell で実行される ( ... ) があります。( ... ) を用いると、意図せず current-shell の状態 (変数やカレントディレクトリなど) を変更してしまう心配が無くなります。
function f1
{
x=777
cd /tmp
}
# グローバル変数 x とカレントディレクトリを変更してから f1 を呼び出す.
# f1 の本体は current-shell で実行されるため "x=777, pwd=/tmp" が出力される.
x=123 && cd / && f1
echo "x=$x, pwd=$(pwd)" # => "x=777, pwd=/tmp"
function f2
(
x=777
cd /tmp
)
# グローバル変数 x とカレントディレクトリを変更してから f2 を呼び出す.
# f2 の本体は sub-shell で実行されるため "x=123, pwd=/" が出力される.
x=123 && cd / && f2
echo "x=$x, pwd=$(pwd)" # => "x=123, pwd=/"
どの形式で関数を定義すべきか
ここまでの内容を踏まえて、bash の関数定義はどの書き方をすると良いのでしょうか。
積極的な理由がない限り function name { ... } 形式に統一すると良いと思います。理由は、他の形式を目にする機会が少ない (パッと見た時にバグの可能性を疑ってしまう) ためです。
# NG
function f
for x in 1 2 3
do
echo $x
done
# OK
function f
{
for x in 1 2 3
do
echo $x
done
}
# NG
function f
case "$1" in
...
esac
# OK
function f
{
case "$1" in
...
esac
}
# NG
function f
(
...
)
# OK
function f
{
(
...
)
}