コマンド置換
コマンドの実行結果を、そのまま変数や別のコマンドの一部として使う仕組みがコマンド置換です。$(コマンド) と書くと、中のコマンドが先に実行され、その標準出力に置き換わります。例えば today=$(date +%F) とすれば、今日の日付を変数に取り込めます。古い書き方のバッククオート `...` も同じ意味ですが、入れ子にしづらいため、今は $(...) が推奨されます。
コマンド置換は、あるコマンドの実行結果をそのまま別の場所で値として使う仕組みだ。
スクリプトを書いていると、こんな欲が出てくる。今日の日付をファイル名に入れたい、今いるディレクトリのパスを変数に取り込みたい、といった具合だ。固定の値を手で書いていては、実行のたびに変わる情報には追いつけない。
コマンド置換を使えると、固定の値を手で書く代わりに、その時々で変わる動的な値をスクリプトに自然に組み込めるようになり、自動化の幅が一気に広がる。
🔄 $(コマンド) は出力をその場に置き換える
コマンド置換は $(コマンド) という形で書く。$( と ) で囲まれた中のコマンドが先に実行され、その標準出力(画面に出るはずだった文字列)が、そっくりその場所に置き換わる。
たとえば today=$(date +%F) と書くと、まず date +%F が実行されて 2026-06-27 のような日付文字列が得られ、それが変数 today に代入される。続けて echo "今日は $today です" とすれば「今日は 2026-06-27 です」と表示される。
date +%F の +%F は、日付を「年-月-日」の形式で出すための指定だ。
同じ要領で dir=$(pwd) とすれば現在地のパスを変数に取り込めるし、count=$(ls | wc -l) のように他のコマンドと組み合わせて、ファイル数を数えて変数に入れる、といった使い方もできる。
コマンド置換は変数への代入だけでなく、別のコマンドの引数の一部としても直接使える。たとえば echo "カーネルは $(uname -r) です" のように文中に埋め込めるし、mkdir "backup_$(date +%Y%m%d)" とすれば「backup_20260627」のような日付入りのディレクトリ名を一発で作れる。
「コマンドの出力をその場の文字列に変える」と捉えると、応用の見当が付きやすくなる。
🕰 古いバッククオートは避ける
コマンド置換には、古くから使われてきたバッククオート `コマンド` という書き方もある。`date +%F` のように逆向きの引用符(バッククオート)で囲む形で、結果は $(date +%F) とまったく同じだ。
古い教材やスクリプトではこちらをよく見かけるので、読めるようにはしておこう。ただし新しく書くときは $(...) を使うのが推奨されている。
理由のひとつが入れ子のしやすさだ。$( ) は $(echo $(date +%F)) のように素直に重ねて書けるが、バッククオートは入れ子にしようとするとバックスラッシュでエスケープが必要になり、読みにくく間違いやすくなる。
もうひとつ、バッククオートは画面上でシングルクオートと見分けづらく、誤読を招きやすいという問題もある。これらの理由から、現在では $(...) に統一しておくのが無難だ。
🧩 取り込んだ結果が崩れる場面
コマンド置換で気を付けたいのが出力の改行の扱いだ。$( ) は、取り込んだ出力の末尾にある改行を取り除いてくれる。そのため date のように1行だけ出すコマンドなら、余計な改行が付かずきれいに値が入る。
問題は複数行を出すコマンドのときだ。その結果を変数に入れて、値をダブルクオートで囲まずに使うと、シェルが空白や改行で値を分割してしまい、行のまとまりが崩れることがある。
複数行の結果を1つの塊として扱いたいときは、echo "$result" のように必ずダブルクオートで囲むのが安全だ。囲まずに echo $result と書くと、改行が空白に変換されて全部が1行に詰められてしまうので、出力の見た目が変わってしまう。前の章で習った「変数はダブルクオートで囲む」が、ここでもそのまま効いてくる。
もうひとつ知っておきたいのが、$( ) の中は「別のシェル(サブシェル)」として実行されるという点だ。これが意外な落とし穴になる。
コマンド置換の内側で変数に代入したり cd でディレクトリを移動したりしても、その効果は $( ) の外側の本体には残らない。たとえば dir=$(cd /tmp; pwd) と書いても、本体のカレントディレクトリは /tmp には変わらず、あくまで dir という変数に /tmp という文字列が入るだけだ。
「中で起きたことは値だけが外に出てくる」と捉えると、この挙動に戸惑わずに済む。
また、まだ値の入っていない(存在しない)コマンドや空の出力を $( ) で受けると、変数が空文字になる。空のまま後続の処理に渡すと条件式が崩れたりファイル名が欠けたりするので、結果が空でないかを if [ -n "$result" ] のように確かめてから使うと、より堅牢なスクリプトになる。
🛠 どんな場面でこれが効くのか
コマンド置換は、実務のスクリプトでほぼ必ず登場する基本テクニックだ。代表的なのは、ログファイルやバックアップに日時を埋め込む用途で、logfile="app_$(date +%Y%m%d_%H%M%S).log" のように書けば、実行のたびに重ならないファイル名を自動で作れる。
ほかにも、host=$(hostname) でホスト名を取り込んで通知メッセージに入れたり、pid=$(pgrep -f myapp) で特定プロセスの番号を取得して後続の処理に渡したり、といった形で活躍する。
「この値はその時々で変わるから手では書けない」という場面に出会ったら、それを出力してくれるコマンドを $( ) で包んで埋め込めないかを考える。これがコマンド置換を使いこなす発想の出発点だ。
コマンド置換は for ループのリスト部分とも好相性で、for f in $(ls *.log); のように書けば、コマンドが出力した一覧をそのまま繰り返しの対象にできる(ただしファイル名に空白が含まれる場合は for f in *.log; のようにワイルドカードを直接使うほうが安全だ)。
なお、足し算などの計算結果を埋め込みたい場合は、コマンド置換ではなく算術展開 $(( )) を使う。today=$(date +%F) のようにコマンドの出力を取り込むのが $( )、sum=$(( a + b )) のように式を計算するのが $(( )) と、丸カッコの数で役割が分かれている点も合わせて覚えておくと混同しない。