bash
キーワードを使用しても省略しても関数を定義できますfunction
。違いはありますか?
#!/bin/bash
function foo() {
echo "foo"
}
bar() {
echo "bar"
}
foo
bar
関数の呼び出しは両方とも成功foo
しbar
、違いはわかりません。そのため、読みやすさを向上させるためだけなのか、それとも何か見落としているものがあるのか疑問に思っています...
dash
ちなみに、 ( debian/ubuntu で/bin/sh
シンボリックリンクされている)などの他のシェルでは、キーワードdash
を使用すると失敗しますfunction
。
答え1
2つの異なる構文が存在する理由は歴史的なものです。function
キーワードはkshC()
言語にヒントを得た構文は、ボーンシェル。POSIXBournefoo ()
構文のみを標準化します。Bash と zsh は両方とハイブリッドをサポートしていますfunction foo () { … }
。ATT ksh を除いて、結果の関数はまったく同じです。
構文の落とし穴に注意してください()
。関数名はエイリアス展開の対象となります。
alias f=g
f () { echo foo; }
type f # f is an alias for g
type g # g is a shell function
f # alias f → function g → print foo
\f # no alias lookup → f: not found
g # function g
ATT ksh (pdksh および mksh などのその派生版ではない) では、 で定義された関数function
と Bourne/POSIX 構文で定義された関数との間にいくつかの違いがあります。 で定義された関数ではfunction
、typeset
キーワードはローカル変数を宣言します。関数が終了すると、変数の値は関数に入る前の値にリセットされます。従来の構文では、変数は使用するかどうかに関係なくグローバル スコープを持ちますtypeset
。
$ ksh -c 'a=global; f () { typeset a=local; }; f; echo $a'
local
$ ksh -c 'a=global; function f { typeset a=local; }; f; echo $a'
global
ksh のもう 1 つの違いは、function
キーワードで定義された関数には独自のトラップ コンテキストがあることです。関数の実行中は、関数の外部で定義されたトラップは無視され、関数内部で致命的なエラーが発生した場合、スクリプト全体ではなく関数のみが終了します。また、 は、$0
で定義された関数では関数名ですfunction
が、 で定義された関数ではスクリプト名です()
。
pdksh は ATT ksh をエミュレートしません。pdksh では、typeset
関数に関係なくローカル スコープの変数を作成し、ローカル トラップはありません (ただし、を使用するとfunction
若干の違いが生じます。詳細については、man ページを参照してください)。
bash と zsh は、function
ksh との互換性のために キーワードを導入しました。ただし、これらのシェルではfunction foo { … }
、 と はfoo () { … }
厳密に同一であり、bash および zsh の拡張機能も同様ですfunction foo () { … }
(上で説明したように、定義を解析するときに潜在的なエイリアス展開が行われる場合を除く)。typeset
キーワードは常にローカル変数を宣言し (もちろん は除く-g
)、トラップはローカルではありません (オプションを設定すると、zsh でローカル トラップを取得できますlocal_traps
)。
答え2
私の知る限り、2 番目のバージョンの方が移植性が高いという点を除けば、違いはありません。
答え3
foo() any-command
は、BourneのようなシェルでサポートされているBourne構文ですがbash
、yash
および の最近のバージョンposh
(複合コマンドのみサポート)。 (ただし、 の Bourne シェルおよび AT&T 実装では、 が複合コマンドでない限りはksh
サポートされません)。foo() any-command > redirections
any-command
foo() any-compound-command
(複合コマンドの例: { cmd; }
、、for i do echo "$i"; done
...(cmd)
最もよく使用されるのは{ ...; }
)
は、Bourne のようなシェルでサポートされている POSIX 構文であり、一般的に使用される構文です。
function foo { ...; }
は Korn シェル構文で、Bourne 構文より古いものです。Korn シェルの AT&T 実装専用に記述し、そこで特別な処理が必要な場合にのみ、この構文を使用してください。この構文は POSIX ではありませんが、Korn シェルとの互換性のために および でサポートされています。ただしbash
、これらのシェル (およびKorn シェルの ベースのバリアント) では、この構文は標準構文と何ら変わりなく扱われます。yash
zsh
pdksh
function foo () { ...; }
の構文は次の通りですいいえシェルと使用しないでくださいbash
。これは、、yash
およびKorn シェルの ベースのバリアントによって偶然サポートされているだけです。ちなみに、これは関数構文でもzsh
あります。pdksh
awk
難解なリストを続けて行くと、
function foo() other-compound-command
(または のようにfunction foo() (subshell)
)function foo() for i do; ... done
はさらに悪いです。 、および ではサポートされていますがbash
、ベースのバリアントであっても ksh ではサポートされていません。yash
zsh
pdksh
その間:
function foo() simple command
は のみでサポートされますzsh
。
foo bar() any-command
あるいは:
$function_list() any-command
複数の関数を一度に定義できるのは、zsh
function { body; } args
() any-compound-command args
それは匿名関数呼び出しも、 によってのみサポートされますzsh
。
答え4
すでに何人かの方が正解していますが、私の簡潔な要約は次のとおりです。
2 番目のバージョンは移植性があり、多くの標準シェル (特に POSIX) で動作する可能性があります。
最初のバージョンは bash でのみ動作しますが、関数名の後の括弧を省略できます。
それ以外の場合、それらは bash が解釈した後に同一のエンティティを表します。