単語の展開

コマンドを構成する各単語は、そのコマンドが実行されるときに展開されます。展開とは単語に含まれるパラメータやパターンを処理して具体的な文字列値に置き換えることです。展開には以下の七種類があります。

  1. チルダ展開
  2. パラメータ展開
  3. コマンド置換
  4. 数式展開
  5. ブレース展開
  6. 単語分割
  7. パス名展開

これらの展開は上に挙げた順序で行われます。特に最初の四つ (チルダ展開・パラメータ展開・コマンド置換・数式展開) を四種展開といいます。

チルダ展開

チルダ展開は、~ で始まる単語を特定のパス名に置き換える展開です。単語の先頭にある ~ から最初の / まで (/ がない場合は単語全体) が指定されたパス名に変換されます。ただし、置き換えられる部分が一文字でもクォートされている場合は、チルダ展開は行われません。

展開される内容は、置き換えられる部分の書式によって以下のように決まります。

~
単なる ~ は、HOME 変数の値に置き換えられます。
~ユーザ名
~ の後にユーザ名が書かれている場合は、そのユーザのホームディレクトリのパス名に置き換えられます。
~+
~+ は、PWD 変数の値に置き換えられます。
~-
~- は、OLDPWD 変数の値に置き換えられます。
~+n
~-n
この n は 0 以上の整数でなければなりません。この形式のチルダ展開は、+n または -n で指定されるディレクトリスタック内のパスの一つに展開されます。(dirs 組込みコマンド参照)

変数代入の値に対してチルダ展開が行われる際、値がコロンで区切ってある場合は、コロンで区切ってある各部分をそれぞれ単語とみなしてチルダ展開します。

チルダ展開に失敗した場合 (指定されたパス名が何らかの原因で得られなかった場合) の動作は POSIX では規定されていませんが、yash では何事もなかったかのように処理を続行します (置き換えられるはずだった部分はそのまま残され、エラーメッセージなどは出ません)。

POSIX 準拠モードでは ~~ユーザ名 の形式の展開のみが有効です。

パラメータ展開

パラメータ展開は、単語の一部をパラメータの値に置き換える展開です。

よく使われる単純なパラメータ展開の形式は ${パラメータ名} です。これはパラメータ名で指定されたパラメータの値に展開されます。さらに、以下の場合にはパラメータ名を囲む括弧を省略して $パラメータ名 のように書くこともできます。

パラメータ名として特殊パラメータでも位置パラメータでも変数名でもないものを指定した場合は、構文エラーになります。(Yash 以外のシェルでは構文エラーではなく展開エラーになるものもあります)

シェルに nounset オプションが設定されている場合、パラメータ名に存在しない変数を指定すると展開エラーになります。Nounset オプションが設定されていない場合は、存在しない変数は空文字列に展開されます。

より複雑なパラメータ展開の形式では、パラメータの値を加工することができます。パラメータ展開の一般形は以下の通りです。

パラメータ展開
${ 前置詞 パラメータ名 インデックス 加工指定 }

ここでは便宜上パラメータ名インデックスの周りに空白を入れましたが、実際には空白を入れてはいけません。パラメータ名以外の部分はいずれも省略可能です。

前置詞

前置詞としてパラメータ名の直前に記号 # を置くことができます。この場合、このパラメータ展開はいま展開しようとしている値の文字数を表す整数に展開されます。展開しようとしているのが配列変数の場合、各要素がそれぞれ文字数を表す整数に置き換えられます。

パラメータ名

パラメータ名には、特殊パラメータ位置パラメータ・変数を指定することができます。この場合、パラメータ展開は指定されたパラメータの値に展開されます。指定したパラメータ名配列変数の場合、配列の各要素が特殊パラメータ @ の場合と同様に単語分割されます (インデックス [*] が指定された場合を除く)。

パラメータ名としてパラメータ展開・コマンド置換数式展開を指定することもできます。これは特に展開の入れ子と言います。この場合、パラメータ展開は内側の展開の展開結果に展開されます。なお、内側のパラメータ展開の括弧 { } は省略できません。また展開の入れ子は POSIX 準拠モードでは使えません。

インデックス

インデックスは展開する値の一部を抜き出すのに使います。インデックスは以下の書式をしています。

インデックス
[単語1]
[単語1,単語2]

ここでの単語1および単語2は通常のトークンと同様に解釈されますが、,] で強制的に区切られます。また空白やタブはトークンの区切りとはみなしません。

インデックスは、以下のように解釈されます。

  1. まず、インデックスに含まれる単語1単語2に対してパラメータ展開・コマンド置換数式展開を行います。
  2. インデックス[単語1] の書式をしていて、単語1の上記展開結果が *@# のいずれかの場合は、インデックスの解釈は終了です。
  3. 単語1単語2の上記展開結果を数式とみなして、数式展開と同様に計算します。計算の結果得られる整数がインデックスとなります。数式展開の結果が整数でない場合は展開エラーです。単語2がない形式でインデックスを指定している場合は、単語2単語1と同じ整数を指定しているものとみなされます。

パラメータ名配列変数の場合または特殊パラメータ * または @ の場合、インデックスは配列の要素または位置パラメータの一部を指定しているものとみなされます。例えばインデックスが [2,4] ならば、配列の 2 番目から 4 番目の要素 (または 2 個目から 4 個目の位置パラメータ) が選択されます。パラメータ名が上記以外の場合は、パラメータの値の一部を指定しているものとみなされます。例えばインデックスが [2,4] ならば、パラメータの値の 2 文字目から 4 文字目までが選択されます。インデックスで選択された配列の要素またはパラメータの値の一部のみが、パラメータ展開の結果として展開結果に残ります。インデックスによる選択について以下の規則が適用されます。

インデックス[単語1] の書式をしていて、単語1の展開結果が *@# のいずれかだった場合は、パラメータは以下のように処理されます。

*
パラメータ名が配列変数の場合、配列の全要素を連結し一つの文字列にします。パラメータ名が特殊パラメータ * または @ の場合、全ての位置パラメータを連結し一つの文字列にします。それ以外の場合はインデックス [1,-1] と同様です。(連結の仕方は特殊パラメータ * の位置パラメータの連結の仕方と同じです)
@
インデックス [1,-1] と同様です。
#
パラメータ名が配列変数の場合、このパラメータ展開は配列の要素数を表す整数に展開されます。パラメータ名が特殊パラメータ * または @ の場合、このパラメータ展開は位置パラメータの個数を表す整数に展開されます。それ以外の場合、このパラメータ展開はいま展開しようとしている値の文字数を表す整数に展開されます。

パラメータ展開にインデックスが指定されていない場合は、インデックスとして [@] が指定されたものとみなされます。インデックスPOSIX 準拠モードでは一切使えません。

加工指定

加工指定はパラメータの値を加工します。加工された後の値がパラメータ展開の結果として展開されます。加工指定には以下の形式があります。

-単語
パラメータ名が存在しない変数を指示している場合は、このパラメータ展開は単語に展開されます。(Nounset オプションが有効な時でもエラーになりません)
+単語
パラメータ名が存在する変数を指示している場合は、このパラメータ展開は単語に展開されます。(Nounset オプションが有効な時でもエラーになりません)
=単語
パラメータ名が存在しない変数を指示している場合は、単語の展開結果がその変数に代入された後、このパラメータ展開はその値に展開されます。変数以外のものに対して代入しようとすると展開エラーになります。(Nounset オプションが有効な時でもエラーになりません)
?単語
パラメータ名が存在しない変数を指示している場合は、エラーメッセージとして単語を標準エラーに出力します。(単語がない場合はデフォルトのエラーメッセージが出ます)
:-単語
:+単語
:=単語
:?単語
これらは上記の -+=?単語の組み合わせの加工指定と同様ですが、単語を使用する条件が異なります。先頭に : が付かないものでは変数が存在するかどうかで判定されますが、: が付くものでは変数が存在し、その値が空文字列でないかどうかで判定されます。
#単語
単語パターンとして見たとき、それがいま展開しようとしている値の先頭部分にマッチするならば、そのマッチする部分を削除します。結果として、このパラメータ展開はマッチしなかった残りの部分に展開されます。マッチの仕方が複数通りある場合はできるだけ短くマッチさせます。
##単語
この加工指定は #単語 と同様ですが、マッチの仕方が複数通りある場合はできるだけ長くマッチさせる点が異なります。
%単語
この加工指定は #単語 と同様ですが、値の先頭部分ではなく末尾部分にマッチさせる点が異なります。
%%単語
この加工指定は %単語 と同様ですが、マッチの仕方が複数通りある場合はできるだけ長くマッチさせる点が異なります。
/単語1/単語2
単語1をパターンとして見たとき、それがいま展開しようとしている値の一部にマッチするならば、そのマッチする部分を単語2に置き換えます。結果として、このパラメータ展開はマッチした部分を単語2に置き換えた値に展開されます。マッチする箇所が複数ある場合は、最初の箇所が選ばれます。マッチの仕方が複数通りある場合はできるだけ長くマッチさせます。
この加工指定は POSIX 準拠モードでは使えません。
/#単語1/単語2
この加工指定は /単語1/単語2 と同様ですが、いま展開しようとしている値の先頭部分にしかマッチしない点が異なります。
/%単語1/単語2
この加工指定は /単語1/単語2 と同様ですが、いま展開しようとしている値の末尾部分にしかマッチしない点が異なります。
//単語1/単語2
この加工指定は /単語1/単語2 と同様ですが、マッチする箇所が複数ある場合は最初の箇所だけではなく全ての箇所を単語2に置き換える点が異なります。
:/単語1/単語2
この加工指定は /単語1/単語2 と同様ですが、いま展開しようとしている値全体にマッチする場合しか対象としない点が異なります。

いずれの形式においても、加工指定に含まれる単語は (それが使用されるときのみ) 四種展開されます。

展開しようとしているパラメータ名が配列変数または特殊パラメータ * または @ の場合、加工指定は配列の各要素または各位置パラメータに対してそれぞれ作用します。

コマンド置換

コマンド置換は、指定されたコマンドを実行してその出力をコマンドラインに展開します。コマンド置換の書式は以下の通りです。

コマンド置換
$(コマンド)
`コマンド`

コマンド置換では、コマンドサブシェルで実行されます。このときコマンドの標準出力がパイプを通じてシェルに送られます。結果として、コマンド置換はコマンドの出力結果に置き換えられます。ただし、コマンドの出力の末尾にある改行は除きます。

$() で囲んだコマンド置換のコマンドは、コマンド置換の入れ子やリダイレクトなどを考慮して予め解析されます。従って、$() の間には基本的に通常通りコマンドを書くことができます。ただし、数式展開との混同を避けるため、中のコマンド( で始まる場合はコマンドの最初に空白を挿し挟んでください。

` で囲むコマンド置換では、コマンド置換の入れ子などは考慮せずに、コマンドの中に最初に (バックスラッシュでクォートしていない) ` が現れたところでコマンド置換の終わりとみなされます。` で囲んだコマンド置換の中に ` で囲んだコマンド置換を書く場合は、内側の ` をバックスラッシュでクォートする必要があります。その他、コマンドの一部として ` を入れたいときは、(それがコマンド内部で一重または二重引用符でクォートされていても) バックスラッシュでクォートする必要があります。

$() で囲んだコマンド置換の中のコマンドは、そのコマンド置換を含むコマンドを解析する時に一緒に解析されます (POSIX 準拠モードを除く)。` で囲んだコマンド置換の中のコマンドは、POSIX 準拠モードであるかどうかに関わらず、そのコマンド置換が実行される時に毎回解析されます。

数式展開

数式展開は、文字列を数式として解釈して、その計算結果を表す数値に展開します。数式展開の書式は以下の通りです。

数式展開
$(())

数式展開では、まずに対してパラメータ展開コマンド置換・(入れ子の) 数式展開が行われます。その結果得られた文字列を以下のように数式として解釈し、その計算結果を表す数値に展開されます。

Yash では、数式の中で整数 (C 言語の long 型) と浮動小数点数 (C 言語の double 型) を扱うことができます。ただし POSIX 準拠モードでは浮動小数点数は使えません。整数同士の演算の結果は整数に、浮動小数点数を含む演算の結果は浮動小数点数になります。

数式では C 言語と (ほぼ) 同様に以下の演算子が使えます。

  1. ( )
  2. ++-- (後置演算子)
  3. ++--+-~! (前値演算子)
  4. */%
  5. +-
  6. <<>>
  7. <<=>>=
  8. ==!=
  9. &
  10. ^
  11. |
  12. &&
  13. ||
  14. ? : (三項演算子)
  15. =*=/=%=+=-=<<=>>=&=^=|=

原子式としては整数リテラル・浮動小数点数リテラル・変数が使用できます。数リテラルの書式は C 言語に準じます。0 で始まる整数リテラルは八進数、0x で始まる整数リテラルは十六進数とみなされます。浮動小数点数リテラルでは指数表記も使えます (例えば 1.23×1061.23e+6)。変数は、その値が数値でない場合はエラーになります。

ブレース展開

ブレース展開は、ブレース ({ }) で囲んだ部分をいくつかの単語に分割します。ブレース展開は braceexpand オプションが有効な時のみ行われます。ブレース展開には二種類の形式があります。

一つ目の形式は、ブレースで囲んだ部分を一つ以上のカンマ (,) で区切ったものです。区切られたそれぞれの部分がブレース展開の前後の部分と結合されて、それぞれ単語として展開されます。例えば a{1,2,3}ba1ba2ba3b という三つの単語に展開されます。

二つ目の形式は {始点..終点} または {始点..終点..差分} です。始点終点差分は全て整数である必要があります。この形式のブレース展開では、始点から終点までの各整数がブレース展開の前後の部分と結合されて、それぞれ単語として展開されます。差分は整数の間隔を指定します。例えば a{1..3}ba1ba2ba3b という三つの単語に展開され、a{1..7..2}ba1ba3ba5ba7b という四つの単語に展開されます。始点終点より大きい場合は整数は降順に展開されます。

複数のブレース展開を組み合わせたり、入れ子にしたりすることもできます。ブレースをブレース展開としてでなく通常の文字として扱うには、ブレースをクォートしてください。またカンマを区切りとしてでなく通常の文字として扱うには、カンマをクォートしてください。

ブレース展開では展開エラーは発生しません。ブレース展開が正しくできない場合は、単にそれはブレース展開ではなかったものとして、そのまま残されます。

単語分割

単語分割は、展開の結果をいくつかの単語に分割します。

単語分割で分割の対象となるのは、パラメータ展開コマンド置換数式展開で展開された結果の部分だけです。また、二重引用符によるクォートの中で展開された部分は、(特殊パラメータ @ の展開を除いて) 分割の対象となりません。

単語分割は IFS 変数の値に従って行われます。IFS 変数が存在しない場合は、空白文字・タブ・改行の三文字が IFS 変数の値として使われます。

IFS 変数の値に含まれている文字を IFS 文字といいます。IFS 文字のうち空白文字またはタブまたは改行であるものを IFS 空白類といいます。IFS 空白類以外の IFS 文字を IFS 非空白類といいます。

分割は以下の規則に従って行われます。

注: IFS 変数の値が空文字列の場合は、単語は一切分割されません。

パス名展開

パス名展開は、単語をパターンとみなしてファイルを検索し、パターンにマッチする実在のファイルへのパス名に展開します。パス名展開は noglob オプションが有効な時は行われません。

パス名展開においてパターンがマッチするには、検索の対象となるディレクトリの読み込み権限が必要です。検索しようとしたディレクトリがシェルにとって読み込み可能でなければ、シェルはそのディレクトリは空であるとみなします。

以下のオプションがパス名展開の結果に影響します。(これらのオプションは最初はすべて無効になっています。)

Nullglob
マッチするファイルがないとき、通常 (このオプションが無効のとき) はパターンはそのまま残りますが、このオプションが有効な時はパターンは削除され何も残りません。
Nocaseglob
このオプションが有効な時は、大文字と小文字を区別せずにマッチングを行います。
Dotglob
通常 (このオプションが無効のとき) は、*? などのワイルドカードやブラケット記法で始まるパターンはピリオドで始まるファイル名にマッチしません。しかしこのオプションが有効な時はこのような制約は解除されます。
Markdirs
このオプションが有効な時は、マッチしたファイルの種類がディレクトリの場合に展開されるパス名の最後に / が付きます。
Extendedglob
このオプションが有効な時は、パス名展開における拡張機能 (後述) が使えるようになります。

パス名展開ではエラーは発生しません。マッチするファイルがない場合またはパターンが不正な場合は、展開は行われずパターンはそのまま残ります (nullglob オプションが有効な時を除く)。

ファイルの検索とパターンマッチングは / で区切られたパス名の構成要素ごとに行われます。ワイルドカードやブラケット記法を全く含まない構成要素はパターンとはみなされず、検索とマッチングは行われません。従って、nocaseglob オプションが有効な時、/*/foo/*/fo[o] の展開結果が異なる可能性があります (前者では foo の部分がパターンとはみなされないので、例えば /bar/FOO というファイルがあってもマッチしません。)。

パス名展開の拡張機能

Extendedglob オプションが有効な時は、以下の特殊なパターンが使えるようになります。

**
指定されたディレクトリツリーに対し再帰的に検索を行います。すなわち、指定されたディレクトリと、そのサブディレクトリ、さらにそのサブディレクトリなどに対し検索を行います。ただし名前がピリオドで始まるディレクトリは検索の対象になりません。例えば dir/**/file というパターンは、dir/file や dir/foo/file や dir/a/b/c/file など、dir ディレクトリの中にある全ての file ファイルへのパスに展開されます。
この特殊なパターンは、foo/bar/** のようにパターン全体の最後にある場合には効果がありません。
.**
** パターンと同様ですが、名前がピリオドで始まるディレクトリも含めて検索する点が異なります。
***
** パターンと同様ですが、検索の中でディレクトリへのシンボリックリンクが見つかった場合、そのディレクトリの中も検索の対象に含める点が異なります。
.***
*** パターンと同様ですが、名前がピリオドで始まるディレクトリも含めて検索する点が異なります。