回答:
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
何が起こると思いますか?
本当に、組み込みは、一方でそれは、シェルに組み込まれていることを除いて、コマンドのようなものですキーワードは、洗練された動作が可能になりますものです!シェルの文法の一部であると言えます。
=\
「、を使用してman
、コマンドの前に「一時的にエイリアスを無効にする」機能のドキュメントを見つけようとしています。apropos
そしてhelp
、私は運がありませんでした。この情報を見つけるためにどこに行くのでしょうか?ほとんどの場合、将来的には他に何が入っているのかを見ることができます。なぜなら、参照元が不足していると思うからです。
\ll
、"ll"
または'll'
。
[[
降伏を引用しているので\[[
、エイリアスとして解析されません。今のところ?私が迷子になったのは、バックスラッシュがBashの引用であることに気づかず、エイリアスとキーワードの下でそれを調べてみたところ、完全に迷子になりました...すべてうまくいきました。引用セクションの下:>引用符で囲まれていないバックスラッシュ()はエスケープ文字です。
\[[
は対照的にLiteralQuoteToken型の単一のトークンであり、oroperのコンパイル/正しい構文のためにCloseCloseKeyKeyTokenTokenを[[
閉じる必要があり]]
ます。後で、(4)でLiteralQuoteTokenが[[
実行するbulitin /コマンドの名前として評価され、(6)でBashは[[
組み込みコマンドまたはコマンドがないためにバーフします。いいね 私は時間の経過とともに正確な詳細を忘れるかもしれませんが、現時点では、Bashの実行方法ははるかに明確です。ありがとうございました。
man bash
それらを呼び出しますSHELL BUILTIN COMMANDS
。したがって、「shell builtin」はgrep
、などの通常のコマンドに似ていますが、別のファイルに含まれるのではなく、bash自体に組み込まれます。これにより、外部コマンドよりも効率的に実行できます。
また、キーワードは「Bashにハードコードされていますが、組み込みとは異なり、キーワードはそれ自体がコマンドではなく、コマンド構成のサブユニットです」。これは、キーワードには機能だけがなく、何かを行うにはコマンドが必要であることを意味すると解釈しています。(リンクから、他の例がありfor
、while
、do
、そして!
、多くのであり、私の答えはあなたの他の質問へ。)
[[ is a shell keyword
しかし[ is a shell builtin
。理由はわかりません。
[
別のコマンドとして存在していたため、歴史的な理由とPOSIX標準が古いBourneシェルに可能な限り厳密に準拠する可能性があります。[[
規格では指定されていないため、開発者はそれをキーワードまたは組み込みとして含めることができます。
Ubuntuに付属のコマンドラインマニュアルにはキーワードの定義はありませんが、オンラインマニュアル(補足説明を参照)およびPOSIX Shell Command Languageの標準仕様では、これらを「予約語」と呼び、両方ともそれらのリストを提供しています。POSIX標準から:
この認識は、どの文字も引用されておらず、単語が次のように使用されている場合にのみ発生します。
コマンドの最初の単語
case、for、またはin以外の予約語の1つに続く最初の語
caseコマンドの3番目の単語(この場合にのみ有効)
forコマンドの3番目の単語(この場合はinおよびdoのみが有効です)
ここで重要なのは、キーワード/予約語には特別な意味があるということです。これらはシェル構文を容易にし、ループ、複合コマンド、分岐(if / case)ステートメントなどの特定のコードブロックを通知する役割を果たすためです。自分で-何もしない、とあなたはなどのキーワードなどを入力した場合、実際にはfor
、until
、case
-シェルは、そうでない場合は、完全なステートメントを期待します-構文エラー:
$ for
bash: syntax error near unexpected token `newline'
$
ソースコードレベルでは、bashの予約語はparese.yで定義されていますが、組み込みには専用のディレクトリ全体があります。
GNUインデックスは[
予約語として表示されますが、実際の組み込みコマンドです。[[
対照的に、予約語です。