シェル関数定義での括弧 `()`の使用は何ですか?


20

関数は次のように定義されます:

do_something () {
    do it
}

名前「do_something」とアクションコードをカプセル化する中かっこは理解できましたが、()Bashスクリプトには名前付きパラメーターがないため、ここでの目的はわかりません。次のように定義する方が良い場合があります。

do_something {
  do it
}

これは現在の構文と競合せず、名前付きパラメーターがないことをさらに宣言します。()ここの使い方は何ですか?


回答:


44

がなければ、()構文は本当にあいまいになります。

そこにする必要があり、いくつかの機能を定義するための明確な構文、および、実質的に変更することなく、他のシェルの構文を、このことはできません。

do_something {
    # one or more commands go here
}

あなたはこれを「現在の構文と混同しない」と言ったが、そうだ!最初の行を実行しようとしても、構文エラーは発生しません。エラーが発生しますが、構文に関するエラーではありません。のある2行目}は構文エラーですが、1行目はそうではありません。代わりに、do_something {というコマンドを実行し、そのコマンドへの引数としてdo_something渡し{ます:

$ do_something {
do_something: command not found

というコマンドが既にある場合はdo_something、それを実行しています。呼び出された関数が既にある場合はdo_somethingそれを呼び出しています。一般に、構文が明確であることは重要ですが、特に誤って関数を呼び出すことなく関数を再定義できることも重要です。関数の定義と呼び出しは同じように見えるべきではありません。

シェルの処理方法{(

通りtype {を教えてくれる、{シェルキーワードです。これにより、次のようになり[[ます。そうでなければコマンドになるような状況で使用される場合、{特別なセマンティクスを持ちます。具体的には、コマンドのグループ化を実行します。ただし、他の状況では、エスケープせずにリテラル{文字を示すために使用できます。これには、コマンドの2番目以降のワードとして渡す状況が含まれます。

もちろん、Bash {現在とは異なる方法で処理するように設計されている可能性があります。ただし、その構文はPOSIXシェルと互換性がなくなり、Bashは実際にはBourneスタイルのシェルではなくなり、多くのシェルスクリプトを実行できなくなります。

対照的に(、シェルのメタキャラクターです。それは、常にそれがコマンドで表示され、引用されていない場合は特別扱い(とされる' '" "または\)。したがって、構文にあいまいさはありません。

do_something() {
    # one or more commands go here
}

それは他の何も意味することができませんでした。Bashに関数がなかった場合echo foo(bar)、構文エラーになります。同じ理由で構文エラーになります。

()表記が本当に嫌いな場合functionは、sudodusで言及されているように、キーワードを使用して省略できます。これは、他のほとんどのBourneスタイルのシェルで関数を定義するための構文の一部ではないことに注意してください(一部はサポートされていますが、そのように定義された関数は異なるセマンティクスを持っているため、それを使用するスクリプトは移植できません)。(この構文が明確にできる理由は、functionそれ自体がBashのキーワードであり、それに続くものが関数定義の開始であることを意味するからです。)

最後に、ほとんどの関数定義{は実際に使用されますが、複合コマンドは許可されます。本体を常にサブシェルで実行したい関数がある場合は、( )ではなくを使用できます{ }


1
より高いレベルでは、人間の期待と読みやすさについてです。ほとんどの言語、特にC、Fortran、およびbashスクリプト言語が開発された頃に一般的な他の言語では、関数/サブルーチンには常に、括弧で囲まれた引数リストがあります。そのパラダイムを変えると、言語を理解しにくくなります。
-jamesqf

15

()トークンを使用すると、関数を宣言していることをシェルインタプリタを伝えることです。

$ do_something () { echo 'do it'; } ; do_something
do it

の代替bashfunction

function do_something {
 echo 'do it'
}

または、ワンライナーとしてテストできます

$ bash -c "function do_something { echo 'do it'; } ; do_something"
do it

2
functionキーワードは、後方互換性のためのbashサポートしていることを事前にPOSIXのkshのイズムです。しかし、bashはそれうまくサポートしてません(古いkshがその形式で宣言された関数で行ったように、変数を関数ローカルのデフォルトにしない)。したがって、新しいコードには理想的ではありません。参照してくださいwiki.bash-hackers.org/scripting/obsolete
チャールズ・ダフィー

@CharlesDuffy、この説明をありがとう:-)
sudodus

1
@CharlesDuffy kshとbashは、kshでは変数をローカルに、bashではグローバルにする構文を受け入れると言っていますか?それは正しく聞こえません。その投稿チェック(ksh93)に一部基づいている私の理解では、kshはbashよりも少ない状況で変数をローカルに作成し、それ以上ではありません。bashでは、関数内ではtypesetなく-g、常にローカル変数を宣言します。ではfunction、キーワードは、bashとkshのスコープの変数と同じように、彼らはないのですか?
エリアケイガン

@EliahKagan私はチャールズがで実証されているために参照されているものと思わギレの答えここに
Sergiy Kolodyazhnyy

9

まさに真のブレーススタイル-あなたの!

他の完全に細かいブレーススタイルを検討してください。

foo
{
  ...
}
foo
  {
    ...
  }
foo
  while ...; do ...; done # Look, ma! No braces!
foo
( ... ) # Look, ma! No braces and a subshell to boot!

シェルは、それらが関数fooリストであり、コマンドの後にコマンドリストが続くだけではないことをどのように判断できますか?これらのすべての場合において()functionキーワードまたはキーワードのような追加の曖昧さ回避要素が必要です。


OTBSが新しい行にブレースを許可する1つの場所は、関数の定義のためです。
ムル

3
CやC ++の関数とは異なり、Bourneスタイルのシェルスクリプトの関数定義は別の関数定義の本体に直接入れ子にできるため、OTBSとして特徴付けることが正しいという議論があると思います。区別するに値しません。(または、これは、メソッドがOTBSではなく同じ行で開き括弧を取得するJavaプログラミングで一般的なスタイルです。)どちらにしても、構文が動作するために括弧の配置に厳しい制約を課す方法について優れた点を示します。まったくない()か、それに似たものです。
エリアケイガン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.