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=*
のところで、var
にyour.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
にも\
が渡され、そのまま\
が出力される。
本に載っている例がこれしかないので、イマイチ汎用性に欠けるというか、この違いの重要性がイマイチわからない。大切な例があれば今後更新予定。
参考文献
- この記事を書くにあたり参考にした本。載っているコードのサンプルがあまりよくないので、自分でスクリプトを書く際にはいろいろ苦労しそうだが、逆に現場で出会うシェルスクリプトを読み解く際にリファレンスとして使うにはとても良いと思われる。網羅性が高いので。