Shell scriptにおける, '', "", \, ``, $()まとめ


Shell scriptで扱うstring(文字列)はやっかいである。環境や言語にもよるが、基本的には' 'や" "を使って文字列を表現するわけであるが、中には特殊文字と呼ばれるものがあり、扱いが難しい。いつでも参照できるように自分用メモとしてまとめておく。

1. シングルクォート ' '

シングルクォートを使用する際は、細かいことは気にせず全て「文字通りの意味になる」と覚えておけばよい。

例1

x=hello world
> Command 'world' not found, but can be installed with:
> sudo snap install world

これはシングルクォートの例というわけでないが...
xにhello worldを入れたいから
x=hello world
とやってみたけど、worldというコマンドが見つからないと言われる。ここの仕様の確認ができていないのだが、恐らく区切り文字であるspaceを使用しているので、
x=helloという代入とworldというコマンドを実行しようとしているように見える。
ということはだ、以下では最後の実行でhelloと表示されるはずだが、

x=hello ls
> your.txt files.txt in.txt pwd.txt
echo $x
>

となるので、どうやらこの場合はlsのみ実行されているっぽい。この辺りの仕様に詳しい方は情報提供願いたい。

例2

echo $HOME
> /home/ubuntu
echo '$HOME'
> $HOME

echo $HOMEは、ホームディレクトリのパスを表示する。
ホームディレクトリについてはこちら

$マークは、シェル変数を参照する際に使用する。
これが
echo '$HOME'
とすると、$意味が消えて入力した通り、$HOMEと表示される。

例3

echo *
> your.txt files.txt in.txt pwd.txt
echo '*'
> *

パス名展開については別記事で説明予定であるが、
echo *とすると*でパス名展開が行われ、カレントディレクトリのフォルダやファイルが表示される。
echo '*'とすると、*の意味が消えるため、*がそのまま表示される。

例4

I'm ubuntu.と表示することを目標にした実行例である。

echo 'I'm ubuntu.'
> (セカンダリプロンプトが表示されてしまう)
echo 'I\'m ubuntu.'
> (セカンダリプロンプトが表示されてしまう)
echo 'I'\''m ubuntu.'
> I'm ubuntu.

echo 'I'm ubuntu.'の場合、セカンダリプロンプトが出てきてしまう。echo 'I'のところでシングルクォートが閉じてしまい、最後の'で再びシングルクォートが出てくるが、これが閉じられていないからである。

echo 'I\'m ubuntu.'の場合、\'によって'をエスケープしようとしているが、そもそも' 'の中では\すらも特殊な意味が打ち消されてしまうので、結局echo 'I\'のところでシングルクォートが閉じてしまう。

echo 'I'\''m ubuntu.'の場合、所望の出力が得られる。
まずecho 'I'で一度シングルクォートを閉じる。そのあと、\'とすることで、「シングルクォートの開始」ではなく「アポストロフィ記号」を表せる。直後に再び「シングルクォートを開始」することで、m ubuntu.をつなげる。

2. ダブルクォート " "

シングルクォートと同じく、ダブルクォートで挟まれると「文字通りの意味になる」のだが、$, `, \に関しては例外

例1

var='hello      world'

echo $var
> hello world

echo '$var'
> $var

echo "$var"
> hello      world

var="hello      world"
echo "$var"
> hello      world

例を見るだけで納得できると思う。
echo $varについて補足しておくと、
$varの中のスペースはシェルによって単なる区切り文字と解釈され、スペースx1の出力となってしまう。

例2

var='*'

echo "$var"
> *

echo '$var'
> $var

echo $var
> your.txt files.txt in.txt pwd.txt


var="*"

echo "$var"
> *

echo '$var'
> $var

echo $var
> your.txt files.txt in.txt pwd.txt


var=*

echo "$var"
> *

echo '$var'
> $var

echo $var
> your.txt files.txt in.txt pwd.txt

これも例を見ればわかるのではないかと思う。
最後の例では
var=*のところで、varyour.txt files.txt in.txt pwd.txtが代入され、以降の出力では全てyour.txt files.txt in.txt pwd.txtが出てくるのかと思ったが、パス名展開は出力の時になって初めて有効になるのだなぁと思った。

例3

var="I'm ubuntu."

echo $var
> I'm ubuntu.

シングルクォートを使用した際は面倒だったが、ダブルクォートを使う場合は'を使うところでダブルクォートが閉じられてしまうこともないし、ダブルクォートの中のシングルクォートがアポストロフィ記号になるから特に面倒なことはしなくてもよい。

3. バックスラッシュ \

シングルクォートの「次の一文字だけ」版といった感じ。

例1

echo $HOME
> /home/ubuntu

echo \$HOME
> $HOME

「次の一文字だけ」特殊な意味が打ち消されるので、\$HOMEとすれば\の次の$が変数参照の意味を失い、文字通りの意味になる。

例2

次は改行の例。シェルスクリプトではよく使うので紹介。

echo \
$HOME
> /home/ubuntu

\(改行)は、コマンドが長すぎて一行で書くと可読性が落ちる際に使用する。
この\(改行)は、見た目的には改行しているんだけど、コマンド上は何もしていないに等しい。だから、単純にecho \の続きを入力することになる。
ターミナルで\(改行)を行うと、まだコマンド入力が終了していないため、セカンダリプロンプトが表示される。

4. コマンド置換 ` `

コマンドの出力を別のコマンドの入力として再利用したい際に使用する。

例1

pwd
> /home/ubuntu/workspace

basename "`pwd`"
> workspace

"`pwd`"/home/ubuntu/workspaceと置換され、basenameの引数になっている。
念のため以下の例も載せておく。' 'についてはもうくどいというか、そうなることはこれまでの例から当たり前なのだが、
慣れないうちは、「あれ、' 'だとどうなるんだっけ」ってなりますもんね。。。ならないか。

pwd
> /home/ubuntu/workspace

basename `pwd`
> workspace

basename '`pwd`'
> `pwd`

basename '`pwd`'についてはもうなんというか、「これで~いいの~自分を好きになってえぇ~」という意思すら感じてきますが、本当にありのままが出力されます。
basename `pwd`については、/home/ubuntu/workspaceの中に特殊文字が含まれていないため上手くいきます。

例2

echo `cal 2022`
>
2022 January February March Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa 1 1 2 3 4 5 1 2 3 4 5 2 3 4 5 6 7 8 6 7 8 9 10 11 12 6 7 8 9 10 11 12 9 10 11 12 13 14 15 13 14 15 16 17 18 19 13 14 15 16 17 18 19 16 17 18 19 20 21 22 20 21 22 23 24 25 26 20 21 22 23 24 25 26 23 24 25 26 27 28 29 27 28 27 28 29 30 31 30 31 April May June Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa 1 2 1 2 3 4 5 6 7 1 2 3 4 3 4 5 6 7 8 9 8 9 10 11 12 13 14 5 6 7 8 9 10 11 10 11 12 13 14 15 16 15 16 17 18 19 20 21 12 13 14 15 16 17 18 17 18 19 20 21 22 23 22 23 24 25 26 27 28 19 20 21 22 23 24 25 24 25 26 27 28 29 30 29 30 31 26 27 28 29 30 July August September Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa 1 2 1 2 3 4 5 6 1 2 3 3 4 5 6 7 8 9 7 8 9 10 11 12 13 4 5 6 7 8 9 10 10 11 12 13 14 15 16 14 15 16 17 18 19 20 11 12 13 14 15 16 17 17 18 19 20 21 22 23 21 22 23 24 25 26 27 18 19 20 21 22 23 24 24 25 26 27 28 29 30 28 29 30 31 25 26 27 28 29 30 31 October November December Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa 1 1 2 3 4 5 1 2 3 2 3 4 5 6 7 8 6 7 8 9 10 11 12 4 5 6 7 8 9 10 9 10 11 12 13 14 15 13 14 15 16 17 18 19 11 12 13 14 15 16 17 16 17 18 19 20 21 22 20 21 22 23 24 25 26 18 19 20 21 22 23 24 23 24 25 26 27 28 29 27 28 29 30 25 26 27 28 29 30 31 30 31

なんと見やすいカレンダーでしょう。これで予定管理はばっちりですね。
こうなってしまう原因は、
cal 2022とすると単語分割が行われ、せっかくみやすいように配置された複数スペースや改行が一つのスペースに置き換わってしまうからです。
これの対策は以下のようにしましょう。

echo "`cal 2022`"
>
                            2022
      January               February               March
Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa
                   1         1  2  3  4  5         1  2  3  4  5
 2  3  4  5  6  7  8   6  7  8  9 10 11 12   6  7  8  9 10 11 12
 9 10 11 12 13 14 15  13 14 15 16 17 18 19  13 14 15 16 17 18 19
16 17 18 19 20 21 22  20 21 22 23 24 25 26  20 21 22 23 24 25 26
23 24 25 26 27 28 29  27 28                 27 28 29 30 31
30 31

       April                  May                   June
Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa
                1  2   1  2  3  4  5  6  7            1  2  3  4
 3  4  5  6  7  8  9   8  9 10 11 12 13 14   5  6  7  8  9 10 11
10 11 12 13 14 15 16  15 16 17 18 19 20 21  12 13 14 15 16 17 18
17 18 19 20 21 22 23  22 23 24 25 26 27 28  19 20 21 22 23 24 25
24 25 26 27 28 29 30  29 30 31              26 27 28 29 30


        July                 August              September
Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa
                1  2      1  2  3  4  5  6               1  2  3
 3  4  5  6  7  8  9   7  8  9 10 11 12 13   4  5  6  7  8  9 10
10 11 12 13 14 15 16  14 15 16 17 18 19 20  11 12 13 14 15 16 17
17 18 19 20 21 22 23  21 22 23 24 25 26 27  18 19 20 21 22 23 24
24 25 26 27 28 29 30  28 29 30 31           25 26 27 28 29 30
31

      October               November              December
Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa
                   1         1  2  3  4  5               1  2  3
 2  3  4  5  6  7  8   6  7  8  9 10 11 12   4  5  6  7  8  9 10
 9 10 11 12 13 14 15  13 14 15 16 17 18 19  11 12 13 14 15 16 17
16 17 18 19 20 21 22  20 21 22 23 24 25 26  18 19 20 21 22 23 24
23 24 25 26 27 28 29  27 28 29 30           25 26 27 28 29 30 31
30 31

だいぶマシなカレンダーが出てきました。

5. コマンド置換 $()

これは` `と基本的には同じだが、違う点が一点。$()の場合は/$, /`
, \\が特別に解釈されることがない。

例1

echo $(cal 2022)
>
2022 January February March Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa 1 1 2 3 4 5 1 2 3 4 5 2 3 4 5 6 7 8 6 7 8 9 10 11 12 6 7 8 9 10 11 12 9 10 11 12 13 14 15 13 14 15 16 17 18 19 13 14 15 16 17 18 19 16 17 18 19 20 21 22 20 21 22 23 24 25 26 20 21 22 23 24 25 26 23 24 25 26 27 28 29 27 28 27 28 29 30 31 30 31 April May June Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa 1 2 1 2 3 4 5 6 7 1 2 3 4 3 4 5 6 7 8 9 8 9 10 11 12 13 14 5 6 7 8 9 10 11 10 11 12 13 14 15 16 15 16 17 18 19 20 21 12 13 14 15 16 17 18 17 18 19 20 21 22 23 22 23 24 25 26 27 28 19 20 21 22 23 24 25 24 25 26 27 28 29 30 29 30 31 26 27 28 29 30 July August September Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa 1 2 1 2 3 4 5 6 1 2 3 3 4 5 6 7 8 9 7 8 9 10 11 12 13 4 5 6 7 8 9 10 10 11 12 13 14 15 16 14 15 16 17 18 19 20 11 12 13 14 15 16 17 17 18 19 20 21 22 23 21 22 23 24 25 26 27 18 19 20 21 22 23 24 24 25 26 27 28 29 30 28 29 30 31 25 26 27 28 29 30 31 October November December Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa 1 1 2 3 4 5 1 2 3 2 3 4 5 6 7 8 6 7 8 9 10 11 12 4 5 6 7 8 9 10 9 10 11 12 13 14 15 13 14 15 16 17 18 19 11 12 13 14 15 16 17 16 17 18 19 20 21 22 20 21 22 23 24 25 26 18 19 20 21 22 23 24 23 24 25 26 27 28 29 27 28 29 30 25 26 27 28 29 30 31 30 31

またどこかで見たミヤスイカレンダーが出てきた。改良してみよう。

echo "$(cal 2022)"
>
                            2022
      January               February               March
Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa
                   1         1  2  3  4  5         1  2  3  4  5
 2  3  4  5  6  7  8   6  7  8  9 10 11 12   6  7  8  9 10 11 12
 9 10 11 12 13 14 15  13 14 15 16 17 18 19  13 14 15 16 17 18 19
16 17 18 19 20 21 22  20 21 22 23 24 25 26  20 21 22 23 24 25 26
23 24 25 26 27 28 29  27 28                 27 28 29 30 31
30 31

       April                  May                   June
Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa
                1  2   1  2  3  4  5  6  7            1  2  3  4
 3  4  5  6  7  8  9   8  9 10 11 12 13 14   5  6  7  8  9 10 11
10 11 12 13 14 15 16  15 16 17 18 19 20 21  12 13 14 15 16 17 18
17 18 19 20 21 22 23  22 23 24 25 26 27 28  19 20 21 22 23 24 25
24 25 26 27 28 29 30  29 30 31              26 27 28 29 30


        July                 August              September
Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa
                1  2      1  2  3  4  5  6               1  2  3
 3  4  5  6  7  8  9   7  8  9 10 11 12 13   4  5  6  7  8  9 10
10 11 12 13 14 15 16  14 15 16 17 18 19 20  11 12 13 14 15 16 17
17 18 19 20 21 22 23  21 22 23 24 25 26 27  18 19 20 21 22 23 24
24 25 26 27 28 29 30  28 29 30 31           25 26 27 28 29 30
31

      October               November              December
Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa
                   1         1  2  3  4  5               1  2  3
 2  3  4  5  6  7  8   6  7  8  9 10 11 12   4  5  6  7  8  9 10
 9 10 11 12 13 14 15  13 14 15 16 17 18 19  11 12 13 14 15 16 17
16 17 18 19 20 21 22  20 21 22 23 24 25 26  18 19 20 21 22 23 24
23 24 25 26 27 28 29  27 28 29 30           25 26 27 28 29 30 31
30 31

見やすくなった。` `と同じパターン。

例2

$()のネスティングの例

pwd
> /home/ubuntu/workspace

basename "$(pwd)"
> workspace

dir=$(basename "$(pwd)")

echo "$dir"
> workspace

dir=$(basename "$(pwd)")のところは、
dir="$(basename "$(pwd)")"としなくてもよいのかと思ってしまうが、こうしなくても代入時はパス名展開や単語分割は行われないから大丈夫だそうだ。もちろん後者のようにしてもいい。

dir="$(basename "$(pwd)")"

echo "$dir"
> workspace

例3

basename $(basename '\\')
> \\

basename `basename '\\'`
> \

最後に` `との違いの例。
$()では\$, \`, \\などが特別に扱われない。他方で、` `を使用するとこれらが特別扱いされる。
具体的には、
`basename '\\'`のところで、basenameには\\ではなく\が渡される。なので、外側のbasenameにも\が渡され、そのまま\が出力される。
本に載っている例がこれしかないので、イマイチ汎用性に欠けるというか、この違いの重要性がイマイチわからない。大切な例があれば今後更新予定。

参考文献

  1. この記事を書くにあたり参考にした本。載っているコードのサンプルがあまりよくないので、自分でスクリプトを書く際にはいろいろ苦労しそうだが、逆に現場で出会うシェルスクリプトを読み解く際にリファレンスとして使うにはとても良いと思われる。網羅性が高いので。