コマンド置換 (command substitution) 入門 — $(...) でコマンド結果を変数に取り込む

コマンド置換 (command substitution) 入門 — $(...) でコマンド結果を変数に取り込む

この記事で学べること

  • コマンド置換 という考え方が分かる
  • $(コマンド)実行結果を変数に取り込む 方法が分かる
  • 古い書き方の バッククォート ... との違いが分かる
  • なぜ "$(...)"クォートで囲む のかが分かる

結論(先に覚えるべき型)

  • コマンドの結果を 変数に入れるVAR=$(コマンド)
  • 結果を 文章に埋め込むecho "今日は $(date)"
  • 迷ったら "$(...)" とダブルクォートで囲む

1. コマンド置換とは何か?

結論: コマンド置換はコマンドの実行結果を、その場の文字列として埋め込む仕組み。$(...) で書く。

リナ: 先輩、date を実行すると今日の日付が出ますよね。あの結果を別のコマンドの中で使いたいんですけど、どうすればいいですか?
ライニー先輩: それがまさに コマンド置換(command substitution) の出番だよ。$(コマンド) と書くと、その部分が コマンドの実行結果に置き換わる んだ。
リナ: 置き換わる...? どういうことですか?
ライニー先輩: 実際に見てみよう。$(date) と書くと、シェルは先に date を実行して、その出力で $(date) を丸ごと差し替えてからコマンド全体を実行するんだ。
$ echo "今日は $(date) です"
今日は 2026年  6月  5日 金曜日 12:00:00 JST です

置き換えのイメージ

echo "今日は $(date) です"
            ↓ date を先に実行
echo "今日は 2026年 6月 5日 ... です"

$(...) の中身が 実行結果という名の文字列 に化けてから、外側のコマンドが動く。

2. 結果を変数に取り込む

結論: VAR=$(コマンド) でコマンドの出力を変数に保存できる。= の前後にスペースを入れないのが鉄則。

2-1. 基本形

$ today=$(date +%Y-%m-%d)
$ echo "$today"
2026-06-05
リナ: today という変数に日付が入りましたね! これは便利。
ライニー先輩: そう。一度変数に入れておけば、後で何度でも使い回せる。ログのファイル名にしたり、メッセージに埋め込んだりね。
$ logfile="backup-$today.log"
$ echo "$logfile"
backup-2026-06-05.log

2-2. = の前後にスペースを入れない

変数代入は = の前後にスペースを入れてはいけない

today = $(date)    # NG: today というコマンドを探しに行ってエラー
today=$(date)      # OK

スペースを入れると、シェルは todayコマンド名 だと解釈してしまう。

3. バッククォートとの違い

結論: 古い書き方の `...` でも同じ動作。しかし入れ子や可読性で不利なため、今は $(...) を使う。

リナ: ネットで調べると、`date` みたいにバッククォートで囲んでいる記事もありました。あれは何ですか?
ライニー先輩: それは 古いコマンド置換の書き方。動作は $(date) とほぼ同じだよ。ただ今は $(...) を使うのが推奨なんだ。
リナ: どうして新しい方がいいんですか?
# 古い書き方(バッククォート)
$ echo "今日は `date` です"

# 新しい書き方(推奨)
$ echo "今日は $(date) です"

$(...) が推奨される理由

観点 バッククォート ... $(...)
入れ子 書きにくい(要エスケープ) そのまま書ける
見やすさ 縦棒と紛らわしい 括弧で明確
引用符の扱い クセがある 素直

バッククォートは「読めればOK」。自分で書くときは $(...) に統一 しよう。

4. 入れ子(ネスト)で組み合わせる

結論: $(...) は中にさらに $(...) を書ける。内側から順に実行され、結果が外側へ渡る。

$ echo "$(basename $(pwd))"
myproject
リナ: $(...) の中にまた $(...) がありますね。これはどう動くんですか?
ライニー先輩: 内側から外側へ 順番に処理されるよ。まず $(pwd) が現在のパス(例: /home/user/myproject)になって、それを basename が受け取って末尾の myproject だけ取り出すんだ。

内側から外側への流れ

basename $(pwd)
         ↓ pwd を実行
basename /home/user/myproject
         ↓ basename を実行
myproject

バッククォートだと内側を \` とエスケープする必要があり面倒。$(...) なら そのまま入れ子にできる のが強み。

5. クォートで囲む理由

結論: "$(...)" とダブルクォートで囲むと、結果に空白や改行があっても 1 つの値として安全に扱える。

5-1. クォートしないと起きる事故

$ files=$(ls)
$ echo $files        # クォートなし
リナ: ファイル一覧が横一列に並んで表示されました。改行が消えてます...
ライニー先輩: それが クォートなしの落とし穴$(...) の結果はスペースや改行で バラバラの単語に分割 されてしまうんだ(単語分割という)。改行や空白をそのまま保ちたいときは、ダブルクォートで囲む。
$ echo "$files"      # クォートあり
Documents
Downloads
report.txt

5-2. 迷ったら囲む

コマンド置換の結果は 基本的に "$(...)" とダブルクォートで囲む

  • ファイル名にスペースが含まれていても壊れない
  • 改行が保たれる
  • 空の結果でも引数が消えてエラーになるのを防げる

「とりあえず囲んでおく」で事故の大半を防げる。

6. よくある初心者のつまずき

結論: 末尾の改行は自動で消える、$() の中でも変数は使える、の 2 点を押さえると混乱しにくい。

6-1. 末尾の改行は取り除かれる

$ count=$(ls | wc -l)
$ echo "ファイル数は $count 個"
ファイル数は 3 個

コマンド置換は 出力末尾の改行を自動的に取り除く。だから上のように数値をそのまま文章へ埋め込める。

6-2. $() の中でも変数や引数が使える

$ dir=/etc
$ echo "$dir のファイル数: $(ls "$dir" | wc -l)"
/etc のファイル数: 220

$(...) の中は 普通のコマンドラインと同じ。変数も、パイプも、別のコマンド置換も自由に書ける。

6-3. 結果が空でも壊れないようにする

$ result="$(grep "存在しない語" file.txt)"
$ echo "[$result]"
[]

クォートで囲んでいれば、検索結果が空でも [] と表示されるだけで済む。クォートを外すと引数そのものが消え、思わぬ動作の原因になる。

7. ミニ課題:実際にやってみよう

結論: 変数代入・文章への埋め込み・入れ子の 3 問で、コマンド置換の基本を手で確かめる。

リナ: 知識は入りました! 手を動かして試したいです。
ライニー先輩: いいね、3問用意したよ。ターミナルでやってみて。

課題1: 現在のユーザー名を me という変数に入れて表示しよう(whoami を使う)。

ヒントを見る

変数名=$(コマンド) の形。表示は echo "$変数名"

解答例
$ me=$(whoami)
$ echo "私は $me です"

課題2: 「現在のディレクトリは ◯◯ です」という文章を、pwd の結果を埋め込んで表示しよう。

ヒントを見る

echo "... $(pwd) ..." の形で、ダブルクォートで囲む。

解答例
$ echo "現在のディレクトリは $(pwd) です"

課題3: 今いるディレクトリの 名前だけ(パス全体ではなく末尾)を表示しよう(入れ子を使う)。

ヒントを見る

basename$(pwd) を渡す。$(basename "$(pwd)") の形。

解答例
$ echo "$(basename "$(pwd)")"

pwd で取得したパスの末尾だけを basename が取り出す。

8. コピペ用テンプレート

結論: 代入・埋め込み・ファイル名生成・カウントのよく使う型をまとめて手元に置いておく。

よく使う型をまとめておく

# 結果を変数に入れる
VAR=$(コマンド)

# 文章に埋め込む(必ずダブルクォート)
echo "結果は $(コマンド) です"

# 日付入りのファイル名を作る
logfile="app-$(date +%Y%m%d).log"

# 行数を数えて変数に
count=$(ls | wc -l)

# 入れ子で組み合わせる
name="$(basename "$(pwd)")"

次に読む