Bashがコマンドを実行する際、入力した文字列がそのまま実行されるわけではありません。実行前に「展開(Expansion)」というプロセスを経て、文字列が書き換えられます。
この記事では、Bashにおける展開の種類とその優先順位について解説します。
目次
- 展開とは
- 展開の種類
- ブレース展開
- チルダ展開
- パラメータ展開(変数展開)
- コマンド置換
- 算術展開
- プロセス置換
- 単語分割
- パス名展開(ワイルドカード)
- クォートによる展開抑止
- 展開の順序
- 展開とコマンド実行の順序
- まとめ
展開とは
展開とは、入力された文字列を特定のルールに基づいて変換・置換するプロセスのことです。
例えば、ls *.txt と入力したとき、シェルは *.txt を実際のファイル名リストに置き換えてから ls コマンドに渡します。この「置き換え」の作業すべてを展開と呼びます。
展開の種類
Bashには8種類の展開があります。
1. ブレース展開
{} 内のカンマ区切りの文字列や範囲を元に、複数の文字列を生成します。
# Bash
echo log.{1,2,3}
# 結果: log.1 log.2 log.3
ブレース展開については「Bashのブレース展開(Brace Expansion)をマスターして入力を爆速にする」で詳しく紹介しています。
2. チルダ展開
~ をホームディレクトリの絶対パスに置き換えます。
# Bash
echo ~/Documents # 結果: /home/user/Documents
その他にも、他のユーザのホームディレクトリや、1つ前にいたディレクトリに置き換える機能もあります。
チルダ展開については「Bashチルダ展開(Tilde Expansion)による高速ディレクトリ操作」で詳しく紹介しています。
3. パラメータ展開(変数展開)
$ に続くパラメータ(変数)を、その値に置き換えます。
# Bash
name="Alice"
echo "Hello, ${name}"
# 結果: Hello, Alice
値を単純に参照するだけでなく、様々な文字列操作によって置き換えることも可能です。
パラメータ展開については「Bashのパラメータ展開(変数展開)をマスターしよう」で詳しく紹介しています。
4. コマンド置換
$(command) の形式で、コマンドの実行結果(標準出力)に置き換えます。
# Bash
echo "今日は$(date)"
# 結果: 今日は2026年 2月 15日 ...
コマンド置換の結果は、別のコマンドの引数に使ったり、変数に代入することが可能です。
コマンド置換については「Bashのコマンド置換(Command Substitution)徹底解説」で詳しく紹介しています。
5. 算術展開
$((expression)) の形式で、数式の計算結果に置き換えます。
# Bash
echo $((1 + 2 * 3))
# 結果: 7
算術展開を使って2進数を使ったビット演算を行ったり、ループの処理状況を表示するといったことも可能です。
算術展開については、「Bashの算術展開(Arithmetic Expansion)を使いこなそう」で詳しく紹介しています。
6. プロセス置換
コマンドの出力をファイルのように扱えるようにします。
# Bash
diff <(ls dir1) <(ls dir2)
# dir1とdir2のファイル一覧を比較
プロセス置換を使うと、引数にファイルしか渡せないコマンドに対して、別のコマンドの処理結果を渡すことが可能になります。
プロセス置換については、「Bashのプロセス置換の使い方:一時ファイルとおさらばする技術」で詳しく紹介しています。
7. 単語分割
パラメータ展開、コマンド置換、算術展開の結果に空白が含まれる場合、それらを個別の引数として分割します。
# Bash
fruits='apple orange banana'
printf ' <%s>' $fruits
# 結果: <apple> <orange> <banana>
※ダブルクォートで囲むと、この分割を抑制できます。
カンマ(,)など空白以外の文字で単語を分割し、文字列に含まれる複数の要素をループするといったことも可能です。
単語分割については、「Bashの単語分割(Word Splitting)を使いこなそう」で詳しく紹介しています。
8. パス名展開(ワイルドカード)
* や ? などのワイルドカードを含むパターンを、一致するファイル名のリストに置き換えます。
# Bash
ls *.jpg # 結果: image1.jpg photo.jpg など
パターンマッチングの設定を変えることで、隠しファイル(ドットファイル)を表示したり、ディレクトリを再帰的に検索するようにすることも可能です。
パス名展開については「Bashのパス名展開(Pathname Expansion)でワイルドカードを使いこなそう」で詳しく紹介しています。
クォートによる展開抑止
展開をさせたくない場合は、クォート(引用符)を使用します。
| 記号 | 名称 | 挙動 |
' ' | シングルクォート | すべての展開を無効化する(最強の抑止)。 |
" " | ダブルクォート | パラメータ展開、コマンド置換、算術展開以外を抑止する。 |
通常はダブルクォートで変数を囲い、意図しない展開を防ぐのがベストプラクティスです。
# Bash
# ダブルクォートで単語分割を抑止
file='my file.txt'
printf ' <%s>' "$file"
# 結果: <my file.txt>
展開の順序
Bashは以下の順序で展開を行います。
- ブレース展開
- チルダ展開、パラメータ展開、算術展開、プロセス置換、コマンド置換(これらは同等の優先度で出現順に左から行われる)
- 単語分割
- パス名展開
- クォート除外(最終的にクォート記号自体が取り除かれる)
この順番を知ることで、「なぜブレース展開内で変数が使えないのか」といった謎が解けます。
# Bash
# 変数展開してからブレース展開したいという意図だが失敗
extensions='txt,csv'
echo {$extensions}
# 結果: {txt,csv}
展開とコマンド実行の順序
シェルがコマンドを実行するまでの大きな流れは以下の通りです。
- 解析: コマンドラインを読み込み、トークンに分ける。
- 展開: 上記のルールに従って文字列を置換する。
- リダイレクト:
<や>の処理を行う。 - 実行: 展開後の文字列を引数として、実際のコマンドを起動する。
重要: コマンド側(lsやechoなど)は、展開が終わった後の「完成した文字列」しか受け取りません。
まとめ
Bashの展開を理解することは、シェルスクリプトのバグを防ぎ、複雑なワンライナーを使いこなすための第一歩です。特に「展開の順序」と「クォートの影響」を意識するだけで、シェルの挙動がぐっと予測しやすくなります。
