shell builtinとshellキーワードの違いは何ですか?


33

これら2つのコマンドを実行すると、

$ type cd
cd is a shell builtin
$ type if
if is a shell keyword

cdシェル組み込みでifあり、シェルキーワードであることが明確に示されています。では、シェルの組み込みとキーワードの違いは何ですか?


回答:


45

Bashがコードを解析する方法には、ビルトインとキーワードの間に大きな違いがあります。違いについて説明する前に、すべてのキーワードと組み込み関数をリストしましょう。

ビルトイン:

$ compgen -b
.         :         [         alias     bg        bind      break     
builtin   caller    cd        command   compgen   complete  compopt   
continue  declare   dirs      disown    echo      enable    eval      
exec      exit      export    false     fc        fg        getopts   
hash      help      history   jobs      kill      let       local     
logout    mapfile   popd      printf    pushd     pwd       read      
readarray readonly  return    set       shift     shopt     source    
suspend   test      times     trap      true      type      typeset   
ulimit    umask     unalias   unset     wait                          

キーワード:

$ compgen -k
if        then      else      elif      fi        case      
esac      for       select    while     until     do        
done      in        function  time      {         }         
!         [[        ]]        coproc              

たとえば[、ビルトインであり、それ[[がキーワードであることに注意してください。これらはよく知られている演算子であるため、これら2つを使用して以下の違いを説明します。

キーワードは、解析の非常に早い段階でBashによってスキャンされ、理解されます。これにより、たとえば次のことが可能になります。

string_with_spaces='some spaces here'
if [[ -n $string_with_spaces ]]; then
    echo "The string is non-empty"
fi

これは正常に機能し、Bashは喜んで出力します

The string is non-empty

引用しなかったことに注意してください$string_with_spaces。一方、次のとおりです。

string_with_spaces='some spaces here'
if [ -n $string_with_spaces ]; then
    echo "The string is non-empty"
fi

Bashは幸せではないことを示しています。

bash: [: too many arguments

ビルトインではなくキーワードで機能するのはなぜですか?Bashはコードを解析するときに、[[どれがキーワードであるかを確認し、それが特別であることを非常に早く理解するためです。そのため、クロージングを探し]]、特別な方法で内部を扱います。組み込み(またはコマンド)は、引数で呼び出される実際のコマンドとして扱われます。この最後の例では、bash [は引数(1行に1つ表示)を指定してコマンドを実行する必要があることを理解しています。

-n
some
spaces
here
]

変数の展開、引用の削除、パス名の展開、単語の分割が発生するためです。コマンド[はシェルに組み込まれていることが判明したため、これらの引数を実行すると、エラーが発生するため、苦情が発生します。

実際には、この区別により洗練された動作が可能になり、ビルトイン(またはコマンド)では不可能になることがわかります。

それでも実際には、どのようにして組み込みキーワードとキーワードを区別することができますか?これは実行する楽しい実験です。

$ a='['
$ $a -d . ]
$ echo $?
0

Bashは、行を解析するときに$a -d . ]、特別なもの(エイリアス、リダイレクト、キーワードなど)がないため、変数の展開のみを実行します。変数の展開後、次のように表示されます。

[ -d . ]

そのため[、引数-d.および)を使用してコマンド(組み込み)を実行します]。これはもちろんtrueです(これは、.はディレクトリです)。

ほら見て:

$ a='[['
$ $a -d . ]]
bash: [[: command not found

ああ。これは、Bashがこの行を見ると、特別なものは何も見えないため、すべての変数を展開し、最終的に次のように見えるためです。

[[ -d . ]]

現時点では、エイリアスの展開とキーワードスキャンは長い間実行されており、今後実行されないため、Bashは次のコマンドを見つけようとします。 [[は見つからず、文句を言います。

同じ線に沿って:

$ '[' -d . ]
$ echo $?
0
$ '[[' -d . ]]
bash: [[: command not found

そして

$ \[ -d . ]
$ echo $?
0
$ \[[ -d . ]]
bash: [[: command not found

エイリアス拡張もかなり特別なものです。次のことを少なくとも1回は実行しました。

$ alias ll='ls -l'
$ ll
.... <list of files in long format> ....
$ \ll
bash: ll: command not found
$ 'll'
bash: ll: command not found

理由は同じです。エイリアスの展開は、変数の展開と引用符の削除のかなり前に行われます。


キーワードとエイリアス

エイリアスをキーワードに定義するとどうなると思いますか?

$ alias mytest='[['
$ mytest -d . ]]
$ echo $?
0

ああ、うまくいく!エイリアスを使用してキーワードのエイリアスを作成できます!知っておくといい。


結論:ビルトインは実際にはコマンドのように動作します。これらは、変数を直接展開し、単語の分割とグロビングを行う引数で実行されるアクションに対応します。それはちょうどで外部コマンドのどこかに持っているような本当にだ/bin/usr/bin私が言うときことなど、変数の展開後に与えられた引数で呼び出されたノートそれだけで外部コマンドを持っているようなものだ、本当に、私は唯一の引数に関して意味、単語の分割、グロブ、変数の展開など。ビルトインはシェルの内部状態を変更できます!

一方、キーワードは非常に早期にスキャンおよび理解され、洗練されたシェル動作が可能になります。シェルは単語の分割やパス名の展開などを禁止できます。

ここで、ビルトインとキーワードのリストを見て、一部がキーワードである必要がある理由を見つけてください。


!キーワードです。関数でその振る舞いを模倣することが可能であるようです:

not() {
    if "$@"; then
        return false
    else
        return true
    fi
}

しかし、これは次のような構造を禁止します:

$ ! ! true
$ echo $?
0

または

$ ! { true; }
echo $?
1

同じtime:リダイレクトを使用して複雑な複合コマンドとパイプラインの時間を調整できるように、キーワードを使用する方がより強力です。

$ time grep '^#' ~/.bashrc | { i=0; while read -r; do printf '%4d %s\n' "$((++i))" "$REPLY"; done; } > bashrc_numbered 2>/dev/null

場合はtime単なるコマンド(でも組み込み)は、それが唯一の引数を参照する場所grep^#および/home/gniourf/.bashrc、時間これをした後、その出力は、パイプラインの残りの部分を通過するでしょう。しかし、キーワードを使用すると、Bashはすべてを処理できます。timeリダイレクトを含む完全なパイプラインが可能です!time単なるコマンドである場合、次のことはできません。

$ time { printf 'hello '; echo world; }

それを試してみてください:

$ \time { printf 'hello '; echo world; }
bash: syntax error near unexpected token `}'

修正してみてください(?):

$ \time { printf 'hello '; echo world;
time: cannot run {: No such file or directory

絶望的。


キーワードとエイリアス

$ alias mytime=time
$ alias myls=ls
$ mytime myls

何が起こると思いますか?


本当に、組み込みは、一方でそれは、シェルに組み込まれていることを除いて、コマンドのようなものですキーワードは、洗練された動作が可能になりますものです!シェルの文法の一部であると言えます。


2
@JohnyTexに同意すると、これはStackサイトで見た最も包括的で適切な回答の1つです。ありがとうございました。おそらく関係のない質問:好奇心のために、=\「、を使用してman、コマンドの前に「一時的にエイリアスを無効にする」機能のドキュメントを見つけようとしています。aproposそしてhelp、私は運がありませんでした。この情報を見つけるためにどこに行くのでしょうか?ほとんどの場合、将来的には他に何が入っているのかを見ることができます。なぜなら、参照元が不足していると思うからです。
nc。

@nc:明示的に文書化されていることはありません。それが機能する理由は、この回答で説明されています。最も近いものは、シェル操作のセクションのリファレンスマニュアルにあります。エイリアスの展開は、ステップ2で非常に早い段階で行われます(この回答で強調しようとしたことです)。引用符の削除、パラメーターの展開、グロブなどは後で実行されます。そうするために無効にするエイリアスを、あなたがする引用符のいくつかの種類を使用することができ禁じ別名としてトークンを理解すること、例えば、シェルから\ll"ll"または'll'
gniourf_gniourf

1
私は実際に注文したものを手に入れました。[[降伏を引用しているので\[[、エイリアスとして解析されません。今のところ?私が迷子になったのは、バックスラッシュがBashの引用であることに気づかず、エイリアスとキーワードの下でそれを調べてみたところ、完全に迷子になりました...すべてうまくいきました。引用セクションの下:>引用符で囲まれていないバックスラッシュ()はエスケープ文字です。
nc。

1
そのため、(2)はOpリストのTokenizationとして考えることができ、OpenTestKeywordTokenと\[[は対照的にLiteralQuoteToken型の単一のトークンであり、oroperのコンパイル/正しい構文のためにCloseCloseKeyKeyTokenTokenを[[閉じる必要があり]]ます。後で、(4)でLiteralQuoteTokenが[[実行するbulitin /コマンドの名前として評価され、(6)でBashは[[組み込みコマンドまたはコマンドがないためにバーフします。いいね 私は時間の経過とともに正確な詳細を忘れるかもしれませんが、現時点では、Bashの実行方法ははるかに明確です。ありがとうございました。
nc。

9

man bashそれらを呼び出しますSHELL BUILTIN COMMANDS。したがって、「shell builtin」はgrep、などの通常のコマンドに似ていますが、別のファイルに含まれるのではなく、bash自体に組み込まれます。これにより、外部コマンドよりも効率的に実行できます。

また、キーワードは「Bashにハードコードされていますが、組み込みとは異なり、キーワードはそれ自体がコマンドではなく、コマンド構成のサブユニットです」。これは、キーワードには機能だけがなく、何かを行うにはコマンドが必要であることを意味すると解釈しています。(リンクから、他の例がありforwhiledo、そして!、多くのであり、私の答えはあなたの他の質問へ。)


1
興味深い事実:[[ is a shell keywordしかし[ is a shell builtin。理由はわかりません。
Sparhawk

1
過去に[別のコマンドとして存在していたため、歴史的な理由とPOSIX標準が古いBourneシェルに可能な限り厳密に準拠する可能性があります。[[規格では指定されていないため、開発者はそれをキーワードまたは組み込みとして含めることができます。
セルギーコロディアズニー

1

Ubuntuに付属のコマンドラインマニュアルにはキーワードの定義はありませんが、オンラインマニュアル(補足説明を参照)およびPOSIX Shell Command Languageの標準仕様では、これらを「予約語」と呼び、両方ともそれらのリストを提供しています。POSIX標準から:

この認識は、どの文字も引用されておらず、単語が次のように使用されている場合にのみ発生します。

  • コマンドの最初の単語

  • case、for、またはin以外の予約語の1つに続く最初の語

  • caseコマンドの3番目の単語(この場合にのみ有効)

  • forコマンドの3番目の単語(この場合はinおよびdoのみが有効です)

ここで重要なのは、キーワード/予約語には特別な意味があるということです。これらはシェル構文を容易にし、ループ、複合コマンド、分岐(if / case)ステートメントなどの特定のコードブロックを通知する役割を果たすためです。自分で-何もしない、とあなたはなどのキーワードなどを入力した場合、実際にはforuntilcase-シェルは、そうでない場合は、完全なステートメントを期待します-構文エラー:

$ for
bash: syntax error near unexpected token `newline'
$  

ソースコードレベルでは、bashの予約語はparese.yで定義されていますが、組み込みには専用のディレクトリ全体があります。

サイドノート

GNUインデックスは[予約語として表示されますが、実際の組み込みコマンドです。[[対照的に、予約語です。

参照:キーワード、予約語、組み込みの違い?

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.