シェルには特別な意味を持つ特殊文字(メタキャラクタともいう)があります。
シェルはコマンドラインに特殊文字を見つけると、コマンドを実行する前にそれを解釈し展開などを行ってからコマンドを実行します。
例えばワイルドカード文字(*や?など)を見つけると、シェルはファイル名に展開し、$を見つけるとその後を変数名と解釈して変数の値に置き換えます。
しかし、場合によっては特殊文字を通常の文字として扱ってもらいたいときがあります。
このようなときにクォーテーションが必要になります。クォーテーションには次の3つの方法があります。
- バックスラッシュ(\)を特殊文字の直前に置く
- シングルクォート(')で囲む
- ダブルクォート(")で囲む
なお、ここでクォーテーションとは特殊文字の特別な意味を奪って通常文字として扱うことを意味しています。一般には、特別な意味を奪うことを「エスケープする」と言いうことの方が多いです。
シェルの特殊文字には次のようなものがあります。
; & ( ) | ^ < > ? * [ ] $ ' " ` { } 改行 タブ スペース
つまりこれらを通常文字として使いたい場合はクォーテーションが必要になります。
バックスラッシュ(\)でエスケープする
特殊文字の直前にバックスラッシュ(\)を置くと特殊文字の特別な意味は失われ、シェルはその文字を通常文字として扱います。
このときのバックスラッシュを「エスケープキャラクタ」と呼びます。
例えば空白を含む「my file」というファイル名を次のように指定してもうまくいきません。
$ cat my file
空白はコマンドや引数を区切るための文字なので、これはcatコマンドに「my」と「file」という2つの引数を指定していることになります。
正しくは次のように記述します。
$ cat my\ file
こうすると空白の区切り文字としての意味は失われ、単なる空白文字として解釈されます。そのためcatには「my file」という1つの引数として渡されます。
バックスラッシュ(\)自身も直後の文字をエスケープするという特別な働きがありますので、「\」を通常文字と扱いたい場合はエスケープするために直前に「\」を置きます。
$ echo foo\\bar foo\bar
なおコマンドラインで改行文字の直前に「\」を置くことによって改行文字を無視させることができますが、これは「\」をエスケープキャラクタとして使用するのとは異なります。
なぜなら改行文字の特別な意味を失わせて単なる改行文字として扱うわけではなく、「\」の直後の改行文字を無いものとして扱うからです。
シングルクォーテーション(')でエスケープする
シングルクォーテーションで囲むことによって、ほぼすべての特殊文字を通常文字として扱うことができます。
シングルクォートでエスケープできないのはシングルクォーテーションだけです。シングルクォートでシングルクォートを囲むことが不可能といった方が正確かもしれません。
例えば次のように記述してもシングルクォートでシングルクォート自身を囲めません。
$ echo 'hoo's bar' >
2番目のシングルクォートは1番目のシングルクォートの終わりであり、3番目のシングルクォートは新しいシングルシングルクォートの開始とみなされます。
そのためシングルクォートの後に改行すると、シングルクォートが閉じられていないのでプロンプト(>)が表示されます。
シングルクォーテーションをエスケープしたい場合は、バックスラッシュ(\)かダブルクォーテーション(")を使います。
$ echo hoo\'s bar hoo's bar $ echo "hoo's bar" hoo's bar
ダブルクォーテーション(")でエスケープする
ダブルクォーテーションも、ほとんどの特殊文字をエスケープして通常文字として扱うことができます。
ダブルクォーテーションでエスケープできないのは次の3つの特殊文字だけです。
- $
- バッククォート(`)
- バックスラッシュ(\)
ただし、ダブルクォート内のバックスラッシュ(\)は、の直後に「$」、「`」、「\」、「"」の文字がある場合に限ってエスケープキャラクタとして働きます。これ以外の場合、バックスラッシュは通常文字として扱われます。
反対の見方をすると、上記の文字はダブルクォートの中では必ず特殊文字として扱われるということです。
つまりこれは次のことを意味します。ダブルクォートを使う理由は主にこれらにあります。
- 変数の参照は、変数の値に展開される
- バッククォート(`)で囲まれたコマンドは、その結果に置換される
少し分かりづらくなってきたのでそれぞれを順に見ていきましょう。
ダブルクォーテーション(")の中での「$」や「`」の使用
ダブルクォーテーションの中で、$によるシェル変数の参照はその変数の値に展開されます。またバッククォート(`)によるコマンド置換も使えます。
次の例では両方を使っています。
$ TODAY=Today $ echo "$TODAY is `date -I`" Today is 2022-02-05
ダブルクォートの中の「$TODAY」はその値に展開され、「`date -I`」はそのコマンドの実行結果に置き換えられます。
ダブルクォーテーション(")の中での「\」の使用
ダブルクォートの中のバックスラッシュ(\)は、直後に「$」、「`」、「\」、「"」の文字がある場合だけエスケープキャラクタとして働きます。
先程の例で試してみましょう。
$ echo "\$TODAY is \`date -I\`" $TODAY is `date -I`
この例で「$」や「`」はバックスラッシュでエスケープされています。そのため変数展開やコマンド置換は行われません。
もう一つ例を見てみましょう。
$ echo "\"\\\" is backslash" "\" is backslash
ダブルクォートの中に「"」を入れたい場合、ダブルクォートの終了とみなされないように「\"」のようにエスケープする必要があります。
また最初の部分を1文字分ずつに分解すると「\"」「\\」「\"」となります。
この部分を「\"\\"」のように記述してしまうと「\"」「\\」「"」と認識されてしまいます。そのため「"」「\」「"」の文字それぞれの前に「\」を置いてエスケープする必要があります。
しかし、この場合はもちろんシングルクォートを使った方が簡単です。
$ echo '"\" is backslash' "\" is backslash
クォートが必要なとき
上述したように特殊文字を通常文字として扱いときにエスケープが必要でしたが、この他にもクォーテーションが必要な場合があります。
例えば、次のようにtestコマンドを書くのは当然ですが正しくありません。これはtestコマンドの引数が2つだからです。
test = john
しかし次のようにヌル値との比較は正しい書き方です。
test "" = "foo"
では次のような書き方は正しいでしょうか?
test $BAR = "foo"
これは変数BARに値が入っていれば動作しますが、値がヌル値だとエラーになります。
シェル変数は値を設定しないとヌル値に初期化されます。次のような書き方はどちらもシェル変数をヌル値に初期化します。
$ BAR= $ BAR=""
変数がヌル値だと1行目の記述は、シェルが変数を展開した後は2行目のようになります。
test $BAR = "foo" test = "foo"
これを避けるには変数はダブルクォートで囲む必要があります。
test "$BAR" = "foo"
この場合、変数がヌル値でも変数が展開された後は次のようになりますので正しく動作します。
test "" = "foo"
この間違いはよくありますので、覚えておきましょう。
文字列の連結を使って複数のクォーテーションを混在させる
変数展開やコマンド置換を使いたい場合はダブルクォートを使用する必要があります。それ以外の場合はシングルクォートを使うのが一般には読みやすいと思います。
ここで次のような例を考えてみましょう。
$ HOGE=hoge $ echo "\$HOGEの値は$HOGEです。" $HOGEの値はhogeです。
この例では変数展開が必要なのでダブルクォートを使っています。
しかし文字列は並べて書けば連結されますので、同じようなことは次の書き方でもできます。
$ echo '$HOGE'"の値は$HOGEです。" $HOGEの値はhogeです。
シングルクォートで囲まれた部分は変数展開されませんのでそのまま「$HOGE」となります。
ダブルクォートで囲まれた部分は変数展開されるので「の値はhogeです。」になります。
したがって最終的にこれらの文字列が連結されてechoコマンドに渡されます。
この例ではそれほど便利ではありませんが、複数のクォーテーションを連結させた方がわかりやすときもありますので覚えておきましょう。
まとめ
特殊文字はコマンドを実行する前にシェルが解釈するということを忘れないようにしましょう。
コマンドがエラーになった原因や意図した通りに結果が表示されない原因が、シェルが特殊文字を置き換えてしまったということがよくあります。
特にsedやawkはコマンドの引数として特殊文字をよく使いますので、これらのコマンドへの引数をシングルクォートで囲むことがよく行われます。