シェルの環境

INPUT · スライド

広がる順番(`~` `$` `*`)

01 / 07

⏎ を押してから動くまで

この章で3回言ってきたことを、いよいよ1本にまとめるよ。

> シェルは、コマンドを動かす前に文字を書きかえている

その書きかえには順番があるんだ。だいたいこの流れだよ。

1. ~ を家の場所にする              チルダ展開2. $名前 を中身にする              変数展開3. $(...) をその出力にする         コマンド置換4. $((...)) を計算結果にする       算術展開5. 空白で区切る                    単語分割6. * ? [] をファイル名にする       グロブ7. 引用符を外す                    クォート除去8. できあがった列をコマンドに渡す

多く見えるけれど、覚えるのは2 → 5 → 6 の順だけだよ。ここに前の回の事故の理由が全部入っているんだ。

変数を広げたあとに、空白で区切る    →  だから "$F" と囲む区切ったあとに、* を広げる          →  だから変数の中の * も広がる

順番が分かると、結果を予想できるようになるよ。予想できれば、危ない操作の前に止まれるでしょう。それがこの回のねらいだよ。

02 / 07

~ は先頭だけ

まずは軽いものから。~ は家の場所に広がるね。

~ $ echo ~/home/manabiya~ $ echo ~/memo.txt/home/manabiya/memo.txt

でも、いつでも広がるわけではないよ。

~ $ echo a~a~            ← 広がらない

単語の先頭にあるときだけ広がるんだ。だから途中に出てくる ~ は、ただの文字だよ。

これは知っておくと助かるよ。バックアップファイルの名前に ~ を使う道具があるからね。

memo.txt~     エディタが作る控え(ただの名前)

そして ~ は他の使い方もあるんだ。

~        自分の家~manabiya  manabiya さんの家

知らない名前を書くと、広がらずにそのまま残るよ。

~ $ echo ~x~x

~$HOME と同じものだと思ってよいけれど、厳密には少し違うということだね。

echo "~"       →  ~(囲むと広がらない)echo "$HOME"   →  /home/manabiya(囲んでも広がる)

スクリプトでは $HOME を使うほうが確実だよ。囲んでも効くからね。

03 / 07

$(...) でコマンドの出力を埋めこむ

これは強力だよ。

~ $ echo "ima wa $(whoami) desu"ima wa manabiya desu

$(...) の中のコマンドを先に動かして、その出力をそこに埋めこむんだ。これをコマンド置換と呼ぶよ。

echo "ima wa $(whoami) desu"  ↓ whoami を動かす → manabiyaecho "ima wa manabiya desu"

中ではパイプも使えるよ。

~ $ N=$(ls *.txt | wc -l)~ $ echo $N3

コマンドの結果を変数にしまえるようになったね。これで章11のスクリプトが書けるようになるんだ。

古い書き方も見かけるよ。

N=`ls | wc -l`     バッククォート(古い形)N=$(ls | wc -l)    こちらが今の形

意味は同じだけれど、$( ) を使ってね。理由は入れ子にできるからだよ。

echo $(( $(ls *.txt | grep -c .) * 2 ))

バッククォートだと、入れ子にするのに \ の重ねが必要でひどく読みにくいんだ。新しい形のほうが素直だね。

04 / 07

$((...)) で計算する

シェルは足し算もできるよ。

~ $ echo $((2+3))5~ $ N=4~ $ echo $((N*N))16

$(( )) の中では、変数に $ を付けなくてもよいんだ。計算だと分かっているからね。

注意が1つあるよ。整数だけなんだ。

~ $ echo $((10/3))3            ← 3.33 ではない

小数が要るときは、他の道具に頼るよ。

awk 'BEGIN{print 10/3}'

章5でやった awk だね。シェルにできないことは、できる道具に渡すという考え方だよ。

使える記号を並べておくね。

記号意味
+ - * /四則(/ は切り捨て)
%あまり
> < ==比べる(1 か 0 が返る)

コマンド置換と組み合わせると、こんなことができるよ。

~ $ echo $(( $(ls *.txt | grep -c .) * 2 ))6

数を数えて、それを計算するでしょう。小さな道具を重ねているだけなのに、けっこうなことができるんだ。

05 / 07

変数の中の * も広がる

この回のいちばん大事な実験だよ。

~ $ P='*.txt'~ $ echo $Pa1.txt a2.txt b1.txt      ← 広がってしまった!~ $ echo "$P"*.txt

シングルで囲んで代入したのに、使うときに広がったね。なぜか分かるかな。順番を思い出してみよう。

echo $P  ↓ 2. 変数展開       →  echo *.txt  ↓ 6. グロブ         →  echo a1.txt a2.txt b1.txt

変数を広げたあとに、グロブが走るからだよ。代入のときの引用符は、代入のときしか効かないんだ。

囲めば止まるよ。

echo "$P"  ↓ 引用符の中はグロブしない*.txt

ここで前の回の作法がもう一度出てくるでしょう。

迷ったら "$変数" と囲む

囲むと止まるのは2つあるよ。

5. 単語分割(空白で切られる)6. グロブ(* が広がる)

どちらも「置きかえたあとに起きる」ものだね。だから囲みは置きかえた後始末をしてくれる、と考えると覚えやすいよ。

06 / 07

この環境では使えないもの

本で見かけるのに、この環境では使えないものがあるよ。

~ $ echo {a,b}{a,b}            ← 広がらない~ $ echo {1..3}{1..3}           ← 広がらない

ブレース展開という機能で、bash にはあるけれど、この環境のシェル(busybox ash)には無いんだ。

bash なら:  echo {a,b}    →  a b            mkdir dir{1,2,3}  →  dir1 dir2 dir3 を一度に

便利なので、本物のパソコンで bash を使うときに試してみてね。

ここで大事なことを言うよ。

> シェルにも種類がある

sh    最小限。どこにでもあるash   busybox の sh(この環境)bash  Linux でいちばん多いzsh   Mac の既定

書き方の土台は同じだけれど、便利機能の有無が違うんだ。

だからスクリプトを書くときは、こう考えるとよいよ。

どこでも動かしたい     →  sh の機能だけで書く自分の環境だけでよい   →  bash の便利機能を使ってよい

この教材で身につくのは前者の土台だよ。いちばん狭い場所で動くものを書けるなら、広い場所でも困らないでしょう。

07 / 07

さあ、打ってみよう

この回で使う形をまとめておくね。

echo ~                     家の場所(先頭だけ)echo $((2+3))              計算する(整数だけ)echo $(whoami)             コマンドの出力を埋めこむN=$(ls | wc -l)            結果を変数にしまうecho ?1.txt                1文字だけの当てはめecho [ab]1.txt             どれか1文字P='*.txt'; echo $P         広がってしまうP='*.txt'; echo "$P"       止まる

確かめ方の道具を1つ渡しておくね。echo を頭に付けて、先に見るんだ。

~ $ echo rm $Prm a1.txt a2.txt b1.txt      ← 3つ消えると分かる

打つ前に結果が見えるでしょう。取り消せない操作の前は、必ずこれをやってね。プロも rm -r の前にはやるんだ。

そして順番の3つだけ、もう一度。

2. $名前 を中身にする5. 空白で区切る6. * を広げる

この並びを知っていれば、「なぜこうなった?」の8割は自分で説明できるようになるよ。では打ってみよう。