シェルはコマンドを一行ずつ読み込んで解釈し、実行します。一行に複数のコマンドがある場合は、それら全てを解釈してから実行します。一つのコマンドが複数行にまたがっている場合は、そのコマンドを解釈し終えるのに必要なだけ後続の行が読み込まれます。コマンドを正しく解釈できない場合は、文法エラーとなり、コマンドは実行されません。
非対話モードで文法エラーが発生した時は、シェルはコマンドの読み込みを中止するため、それ以降のコマンドは一切読み込まれません。
コマンドは、いくつかのトークンによって構成されます。トークンとは、シェルの文法における一つ一つの単語のことを言います。トークンは原則として空白 (空白文字またはタブ文字) によって区切られます。ただしコマンド置換などに含まれる空白はトークンの区切りとは見なしません。
以下の記号は、シェルの文法において特別な意味を持っています。これらの記号も多くの場合他の通常のトークンの区切りとなります。
;
&
|
<
>
(
)
以下の記号はトークンの区切りにはなりませんが、文法上特別な意味を持っています。
$
`
\
"
'
*
?
[
#
~
=
%
以下のトークンは特定の場面において予約語と見なされます。予約語は複合コマンドなどを構成する一部となります。
!
{
}
case
do
done
elif
else
esac
fi
for
if
in
then
until
while
これらのトークンは以下の場面において予約語となります。
case
, for
, in
を除く) の直後のトークンのとき
in
in
または do
トークンが #
で始まる場合、その #
から行末まではコメントと見なされます。コマンドの解釈においてコメントは完全に無視されます。
空白や上記の区切り記号・予約語などを通常の文字と同じように扱うには、適切な引用符でクォートする必要があります。引用符として、バックスラッシュ・一重引用符・二重引用符の三種類が使えます。
バックスラッシュ (\
) は直後の一文字をクォートします。バックスラッシュそのものは単語から取り除かれます。
例外として、バックスラッシュの直後に改行がある場合、それは改行をクォートしているのではなく、行の連結と見なされます。バックスラッシュと改行が削除され、バックスラッシュがあった行とその次の行が元々一つの行であったかのように扱われます。
二つの一重引用符 ('
) で囲んだ部分では、全ての文字は通常の文字と同じように扱われます。改行を一重引用符で囲むこともできます。ただし、一重引用符を一重引用符で囲むことはできません。
二つの二重引用符 ("
) で囲んだ部分も一重引用符で囲んだ部分と同様にクォートされますが、いくつか例外があります。二重引用符で囲んだ部分では、パラメータ展開・コマンド置換・数式展開が通常通り解釈されます。またバックスラッシュは $
, `
, "
, \
の直前にある場合および行の連結を行う場合にのみ引用符として扱われ、それ以外のバックスラッシュは通常の文字と同様に扱われます。
コマンドを構成する各トークンは、それが予め登録されたエイリアスの名前に一致するかどうか調べられます。一致するものがあれば、そのトークンはそのエイリアスの内容に置き換えられて、その後コマンドの解析が続けられます。これをエイリアス置換といいます。
エイリアスの名前に引用符を含めることはできないので、引用符を含むトークンはエイリアス置換されません。また、予約語もエイリアス置換されません。
エイリアスには通常のエイリアスとグローバルエイリアスの二種類があります。通常のエイリアスは、コマンドの最初のトークンにのみ一致します。グローバルエイリアスはコマンド内の全てのトークンが一致の対象です。グローバルエイリアスは POSIX 規格にはない拡張機能です。
エイリアス置換の結果がさらに別のエイリアスに一致して置換される場合もあります。しかし、同じエイリアスに再び一致することはありません。
最初のトークンが予約語でないコマンドは、単純コマンドです。単純コマンドはコマンドの検索と実行のしかたに従って実行されます。
単純コマンドの初めのトークンが 名前=値 の形式になっている場合は、それは変数代入と見なされます。ただしここでの名前は、一文字以上のアルファベット・数字または下線 (_
) で、かつ最初が数字でないものです。変数代入ではない最初のトークンはコマンドの名前と解釈されます。それ以降のトークンは (たとえ変数代入の形式をしていたとしても) コマンドの引数と解釈されます。
全てのトークンが変数代入の形式ならば、変数の代入だけが行われ、コマンドは実行されません。
パイプラインは、一つ以上のコマンドを記号 |
で繋いだものです。
パイプラインの実行は、パイプラインに含まれる各コマンドをそれぞれ独立したサブシェルで同時に実行することで行われます。この時、各コマンドの標準出力は次のコマンドの標準入力にパイプで受け渡されます。最初のコマンドの標準入力と最後のコマンドの標準出力は元のままです。パイプライン内のコマンドの実行が終わると、パイプラインの実行が終わります。最後のコマンドの終了ステータスがパイプラインの終了ステータスになります。
パイプラインの先頭には、記号 !
を付けることができます。この場合、パイプラインの終了ステータスが逆転します。つまり、最後のコマンドの終了ステータスが 0 のときはパイプラインの終了ステータスは 1 になり、それ以外の場合は 0 になります。
注: 最後のコマンドの終了ステータスがパイプラインの終了ステータスになるため、パイプラインの実行が終了するのは少なくとも最後のコマンドの実行が終了した後です。しかしそのとき他のコマンドの実行が終了しているとは限りません。また、最後のコマンドの実行が終了したらすぐにパイプラインの実行が終了するとも限りません。(シェルは、他のコマンドの実行が終わるまで待つ場合があります)
注: POSIX 規格では、パイプライン内の各コマンドはサブシェルではなく現在のコマンド実行環境で実行してもよいことになっています。
And/or リストは一つ以上のパイプラインを記号 &&
または ||
で繋いだものです。
And/or リストの実行は、and/or リストに含まれる各パイプラインを条件付きで実行することで行われます。最初のパイプラインは常に実行されます。それ以降のパイプラインの実行は、前のパイプラインの終了ステータスによります。
&&
で繋がれている場合、前のパイプラインの終了ステータスが 0 ならば後のパイプラインが実行されます。
||
で繋がれている場合、前のパイプラインの終了ステータスが 0 でなければ後のパイプラインが実行されます。
最後に実行したパイプラインの終了ステータスが and/or リストの終了ステータスになります。
複合コマンドは、より複雑なプログラムの制御を行う手段を提供します。
グルーピングを使うと、複数のコマンドを一つのコマンドとして扱うことができます。
{ コマンド...; }
(コマンド...)
{
と }
は予約語なので、他のコマンドのトークンとくっつけて書いてはいけません。一方 (
と )
は特殊な区切り記号と見なされるので、他のトークンとくっつけて書くことができます。
通常のグルーピング構文 ({
と }
で囲む) では、コマンドは (他のコマンドと同様に) 現在のコマンド実行環境で実行されます。サブシェルのグルーピング構文 ((
と )
で囲む) では、括弧内のコマンドは新たなサブシェルで実行されます。
If 文は条件分岐を行います。分岐の複雑さに応じていくつか構文のバリエーションがあります。
if コマンド...; then コマンド...; fi
- Else がある場合
if コマンド...; then コマンド...; else コマンド...; fi
- Elif がある場合
if コマンド...; then コマンド...; elif コマンド...; then コマンド...; fi
- Elif と else がある場合
if コマンド...; then コマンド...; elif コマンド...; then コマンド...; else コマンド...; fi
どの構文の場合でも、if
と then
の間にあるコマンドがまず実行されます。コマンドの終了ステータスが 0 ならば、条件が真であると見なされて then
と fi
(あるいは else
や elif
) の間にあるコマンドが実行され、if 文の実行はそれで終了します。終了ステータスが 0 でなければ、条件が偽であると見なされます。ここで else
も elif
もなければ、if 文の実行はこれで終わりです。else
がある場合は、else
と fi
の間のコマンドが実行されます。elif
がある場合は、elif
と then の間にあるコマンドが実行され、その終了ステータスが 0 であるかどうか判定されます。その後は先程と同様に条件分岐を行います。
elif
と then
は一つの if 文内に複数あっても構いません。
If 文の終了ステータスは、どれかの条件が真であった場合はそれに対応して実行された then
の後のコマンドの終了ステータスです。どの条件も偽であった場合は、else
の後のコマンドの終了ステータスです (else
がない場合は 0)。