スクリプトを書く

INPUT · スライド

`for` で繰り返す

01 / 08

同じことを、たくさんの相手に

前の回で作った見張り役は、1つのファイルしか見られなかったね。

~ $ ./mihari.sh aka.txt~ $ ./mihari.sh ao.txt~ $ ./mihari.sh ki.txt

3つなら打てるけれど、30 個あったら無理でしょう。

そこで繰り返しを覚えるんだ。

for F in *.txt; do  ./mihari.sh "$F"done

これだけで、.txt が何個あっても全部見てくれるよ。

これでプログラムの3要素がそろうんだ。

1. 順に実行する    1本目2. 条件で分かれる  3本目3. 繰り返す        今日

この3つがあれば、たいていのことが書けるようになるよ。JavaScript や Python でも同じ3つを覚えたでしょう。書き方が違うだけなんだ。

そして実は、あなたは似たものをもう使っているんだ。

ls *.txt | xargs wc -l

章4でやった xargs だね。あれも「たくさんの相手に同じことをする」道具だよ。今日はその書ける版を覚えるんだ。

02 / 08

for の形

基本の形はこれだよ。

for A in aa bb cc; do  echo [$A]done

動かすとこうなるね。

[aa][bb][cc]

読み方はこうだよ。

for A in 並んでいるもの  ↓ 1つずつ取り出して A に入れるdo  中身をやるdone  次の1つへ

A は自分で決める名前だよ。F でも IRO でも何でもいいんだ。ふつうは中身が分かる名前にするね。

for F in *.txt      F は file の Ffor N in 1 2 3      N は number の N

閉じるのは done だよ。iffi と同じ役目だね。

if   →  fifor  →  done

1行に詰めることもできるよ。

for A in aa bb; do echo $A; done

; で区切るんだ。短いときは1行でいいけれど、中身が2つ以上になったら縦に書くほうが読みやすいね。

03 / 08

ファイル名を回す

いちばん使うのはこれだよ。

for F in *.txt; do  echo "--- $F ---"  wc -l < "$F"done

*.txt先に広がって、その1つずつが F に入るんだ。

for F in *.txt  ↓ グロブが広がる(章10)for F in aka.txt ao.txt ki.txt

章10でやった展開の順が、そのまま効いているでしょう。シェルが先に書きかえるんだったね。

1つ気をつけることがあるよ。当てはまるものが無いとき、こうなるんだ。

~ $ for F in nai*.txt; do echo [$F]; done[nai*.txt]

パターンがそのまま1回回るでしょう。0 回ではないんだ。

だから中で確かめるのが作法だよ。

for F in nai*.txt; do  [ -f "$F" ] || continue  echo "$F"done

continue は「この回を飛ばして次へ」という意味だよ。無いものは飛ばすという書き方だね。

そして "$F" と囲むのも忘れないでね。空白入りの名前があると、囲まないと割れてしまうんだ。章10と前の回でやったとおりだよ。

04 / 08

数を回す・ファイルの中身を回す

ファイル名以外も回せるよ。

数を回す

for N in 1 2 3; do echo $N; donefor N in $(seq 1 10); do echo $N; done

seq は数を並べるコマンドだったね(章2で使ったよ)。10 回やるというときはこの形だよ。

ファイルの中身を回す

for IRO in $(cat iro.list); do  echo "$IRO no ban"done

iro.list の中身が1行ずつ入るでしょう。やることの一覧を、ファイルに書いておけるようになったんだ。

これは強力だよ。

iro.list を書きかえるだけで、やることが変わる

スクリプトを直さずに動きを変えられるね。手順と、対象を分けるという考え方だよ。

ただし、これには弱点があるんだ。

1行に空白が入っていると、割れてしまう

章10でやった単語分割だね。空白を含む行を扱いたいときは while read を使うよ。それは次の回でやるね。

引数を回すのも大事だよ。

for F in "$@"; do  echo [$F]done

"$@" と囲めば、引数の切れ目が保たれるんだったね。前の回でやったとおりだよ。

05 / 08

xargs との使い分け

章4でやった xargs を思い出してね。

ls *.txt | xargs wc -l

同じことが for でも書けるよ。

for F in *.txt; do wc -l "$F"; done

どちらを使えばいいんだろうね。目安を書いておくよ。

やりたいこと向いているもの
1つのコマンドに渡すだけxargs
途中で条件を見たいfor
何行もやりたいfor
すごく数が多いxargs(速い)

xargs はまとめて渡すので速いんだ。

xargs   wc -l a.txt b.txt c.txt      1回で済むfor     wc -l a.txt / wc -l b.txt    3回動かす

1万個のファイルだと、この差が大きく出るよ。

でも for は中で自由に書けるでしょう。

for F in *.txt; do  [ -s "$F" ] || continue  echo "--- $F ---"  head -1 "$F"done

条件も、何行の手順も書けるんだ。xargs ではこれはできないね。

だから覚え方はこうだよ。

単純で速さがほしい   →  xargs中で考えたい         →  for

道具が2つあるのは、目的が違うからなんだ。

06 / 08

飛ばす・やめる

繰り返しの中で使う道具が2つあるよ。

continue — この回を飛ばして次へ

for A in aa bb cc; do  [ "$A" = bb ] && continue  echo $Adone
aacc          ← bb は飛ばされた

break — 繰り返しをやめる

for A in aa bb cc; do  [ "$A" = bb ] && break  echo $Adone
aa          ← bb で止まった

使いどころはこうだよ。

continue   合わないものを飛ばす(ふるい)break      見つかったらやめる(探しもの)

continue はよく使うね。前のスライドで書いた形がそれだよ。

for F in *.txt; do  [ -f "$F" ] || continue     無いものは飛ばす  [ -s "$F" ] || continue     空も飛ばす  本題done

飛ばす条件を上に並べて、本題を下に書くでしょう。前の回でやった「早めに返す」と同じ形だね。入れ子にしないので読みやすいんだ。

入れ子もできるよ。

for A in aa bb; do  for B in 1 2; do    echo $A$B  donedone

aa1 aa2 bb1 bb2 と出るよ。内側が全部回ってから、外側が次へ進むんだ。ただし深くすると読みにくいので、2段までにしておくのがいいね。

07 / 08

まとめてリダイレクトできる

for の出力は、まとめて1つのファイルに入れられるよ。

for F in *.txt; do  echo "--- $F ---"  wc -l < "$F"done > matome.txt

done のうしろに > を書くでしょう。繰り返し全体の出口を向けたんだ。

中で書くとどうなるか比べてみてね。

for F in *.txt; do  wc -l < "$F" > matome.txt      ← 毎回上書き!done

これだと最後の1つだけ残るんだ。> は毎回まっさらにするからね。

中で足すなら >> だよ。

  wc -l < "$F" >> matome.txt     足していく

3通りを並べておくね。

書き方結果
done > f全部まとまる(開くのは1回)
中で >> f全部まとまる(毎回開く)
中で > f最後だけ残る

done > f がいちばんきれいだよ。ファイルを1回だけ開くので速いし、書き忘れの事故も無いからね。

章4でやった >>> の区別が、繰り返しと組み合わさると結果が全然変わるでしょう。ここは実際に手を動かして確かめるところだよ。

08 / 08

さあ、打ってみよう

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

for A in aa bb cc; do … done      並べたものfor F in *.txt; do … done         ファイル名for N in $(seq 1 5); do … done    数for L in $(cat list); do … done   ファイルの中身for F in "$@"; do … done          引数continue                          この回を飛ばすbreak                             繰り返しをやめるdone > matome.txt                 まとめて出す

作るときのコツを1つ渡しておくね。

> 先に echo で回して、何が来るか見る

for F in *.txt; do echo "$F"; done

本題の前に、名前だけ出してみるんだ。章10でやった「危ない操作の前に echo」と同じ考え方だよ。

for F in *.txt; do echo rm "$F"; done     消す予定を見るfor F in *.txt; do rm "$F"; done          本当に消す

繰り返しはまちがえると全部にやってしまうので、確かめが特に大事なんだ。1つのファイルを消すまちがいと、100 個を消すまちがいでは、取り返しがつかないでしょう。

次の回は while だよ。

for     数が決まっているときwhile   条件が続くあいだ

使い分けも見えてくるからね。では回してみよう。