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

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

この記事でできるようになること

  • `$(コマンド)` でコマンドの実行結果を文章に埋め込める
  • `VAR=$(コマンド)` で実行結果を変数に保存できる
  • `"$(...)"` と囲む理由を説明できる。バッククォートとの違いもわかる

前提知識(先に読むと理解しやすい記事)

この記事で学べること

言葉の整理

  • 変数:値を入れておく名前付きの箱です。名前を書けば、中身を取り出せます。
  • コマンド置換:コマンドの実行結果を、その場の文字として埋め込む仕組みです。「コマンド展開」「Capturing Output」と呼ばれることもあります。
  • クォート:引用符のことです。"..." をダブルクォート、'...' をシングルクォートと呼びます。

なお、この記事で使う date / pwd / whoami / ls は、すべて表示だけを行うコマンドです。ファイルを消したり書きかえたりはしません。何度打ち間違えても、あなたのパソコンは壊れません。

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

  • コマンドの結果を 変数に入れる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コマンド名 だと解釈します。そのため command not found になります。

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

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

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

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

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

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

バッククォートは、古い記事を読むときに理解できれば十分です。自分で書くときは $(...) に統一しましょう。

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

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

$ echo "$(basename $(pwd))"
myproject
リナ: $(...) の中に、また $(...) がありますね。これはどう動くのでしょうか。
ライニー先輩: 内側から外側へ 順番に処理されるよ。まず $(pwd) が現在のパスファイルやディレクトリの場所を表す文字列。になる。たとえば /home/user/myproject だね。
ライニー先輩: それを basename が受け取って、末尾の myproject だけを取り出すんだ。basename はパスから最後の名前だけを取り出すコマンドだよ。

内側から外側への流れ

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

バッククォートの場合、内側を \` と書き直す必要があります。この書き直しをエスケープと呼びます。手間がかかります。

$(...) なら そのまま入れ子にできます。これが大きな強みです。

5. クォートで囲む理由

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

5-1. リナの失敗:クォートを忘れて改行が消える

$ files=$(ls)
$ echo $files        # クォートなし
Documents Downloads report.txt
リナ: ファイル一覧が横一列に並びました。改行が消えています。変数の中身まで壊れてしまったんでしょうか。
ライニー先輩: 壊れてないよ。変数の中には、ちゃんと改行入りで入っている。
リナ: えっ、中身は無事なんですか。それは意外です。
ライニー先輩: そう。壊れているのは表示のほうなんだ。クォートを付けないと、$(...) の結果はスペースや改行の位置でバラバラの単語に分けられる。これを単語分割と呼ぶよ。
ライニー先輩: echo はその単語をスペース区切りで並べ直す。だから横一列に見えるんだ。
リナ: 中身ではなく渡し方の問題だったんですね。納得しました。
$ 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 という変数に入れて、文章の中で表示しよう。

ヒント 1(方向づけ)を見る

まずユーザー名を調べるコマンドを実行します。その結果を箱に入れ、あとから文章の一部として取り出します。

ヒント 2(コマンド名)を見る

ユーザー名は whoami でわかります。箱に入れる形は 変数名=$(コマンド) です。表示は echo です。

答えを見る
$ me=$(whoami)
$ echo "私は $me です"
私は lina です

表示される名前は、お使いの環境によって変わります。

課題 2: 「現在のディレクトリファイルをまとめて整理する入れ物。Windows や macOS の「フォルダ」と同じもの。は ◯◯ です」という文章を、pwd の結果を埋め込んで表示しよう。

ヒント 1(方向づけ)を見る

今回は変数を使いません。文章の中に、コマンドの結果を直接はめ込みます。

ヒント 2(コマンド名)を見る

現在地は pwd でわかります。文章全体をダブルクォートで囲み、$(pwd) を差し込みます。

答えを見る
$ echo "現在のディレクトリは $(pwd) です"
現在のディレクトリは /home/lina/myproject です

表示されるパスは、お使いの環境によって変わります。

課題 3: 今いるディレクトリの 名前だけ を表示しよう。パス全体ではなく、末尾の 1 つだけです。

ヒント 1(方向づけ)を見る

2 段構えです。まず現在地のパス全体を取り出します。次にそのパスから、末尾の名前だけを取り出します。

ヒント 2(コマンド名)を見る

パス全体は pwd です。末尾だけを取り出すのは basename です。$(...) を入れ子にします。

答えを見る
$ echo "$(basename "$(pwd)")"
myproject

pwd が取得したパスの末尾だけを basename が取り出します。内側の $(pwd) から先に実行される点を確かめてください。

8. 振り返り

リナ: 整理します。$(コマンド) は、その場所をコマンドの実行結果に置きかえる書き方ですね。
ライニー先輩: そのとおり。変数に入れたいときは VAR=$(コマンド) の形にする。
リナ: そして結果を使うときは "$(...)" と囲む。囲まないと単語分割で崩れてしまうんですね。
ライニー先輩: 完璧だね。バッククォートは読めれば十分。書くときは $(...) に統一しよう。

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

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

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

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

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

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

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

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

今日の 3 行まとめ

  1. $(コマンド) は、その場所をコマンドの実行結果に置きかえる
  2. 変数に入れるときは VAR=$(コマンド)= の前後にスペースを入れない
  3. 結果を使うときは "$(...)" と囲む。囲まないと空白や改行で分割される

次に読む

この記事を共有

次の一手