「function foo() {}」と「foo() {}」の違い

「function foo() {}」と「foo() {}」の違い

bashキーワードを使用しても省略しても関数を定義できますfunction。違いはありますか?

#!/bin/bash

function foo() {
  echo "foo"
}

bar() {
  echo "bar"
}

foo

bar

関数の呼び出しは両方とも成功foobar、違いはわかりません。そのため、読みやすさを向上させるためだけなのか、それとも何か見落としているものがあるのか​​疑問に思っています...

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 構文で定義された関数との間にいくつかの違いがあります。 で定義された関数ではfunctiontypesetキーワードはローカル変数を宣言します。関数が終了すると、変数の値は関数に入る前の値にリセットされます。従来の構文では、変数は使用するかどうかに関係なくグローバル スコープを持ちます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 は、functionksh との互換性のために キーワードを導入しました。ただし、これらのシェルではfunction foo { … }、 と はfoo () { … }厳密に同一であり、bash および zsh の拡張機能も同様ですfunction foo () { … }(上で説明したように、定義を解析するときに潜在的なエイリアス展開が行われる場合を除く)。typesetキーワードは常にローカル変数を宣言し (もちろん は除く-g)、トラップはローカルではありません (オプションを設定すると、zsh でローカル トラップを取得できますlocal_traps)。

答え2

私の知る限り、2 番目のバージョンの方が移植性が高いという点を除けば、違いはありません。

答え3

foo() any-command

は、BourneのようなシェルでサポートされているBourne構文ですがbashyashおよび の最近のバージョンposh(複合コマンドのみサポート)。 (ただし、 の Bourne シェルおよび AT&T 実装では、 が複合コマンドでない限りはkshサポートされません)。foo() any-command > redirectionsany-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 シェルの ベースのバリアント) では、この構文は標準構文と何ら変わりなく扱われます。yashzshpdksh

function foo () { ...; }

の構文は次の通りですいいえシェルと使用しないでくださいbash。これは、、yashおよびKorn シェルの ベースのバリアントによって偶然サポートされているだけです。ちなみに、これは関数構文でもzshあります。pdkshawk

難解なリストを続けて行くと、

function foo() other-compound-command

(または のようにfunction foo() (subshell))function foo() for i do; ... doneはさらに悪いです。 、および ではサポートされていますがbash、ベースのバリアントであっても ksh ではサポートされていません。yashzshpdksh

その間:

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 が解釈した後に同一のエンティティを表します。

関連情報