背景
退屈なことは大体shell
でなんとかなります。特に普段からコマンドラインに生息している人は、バーっと書いてそのまま動けばこんなに楽なことはありません。
ところで、shell
ですることと言えば、grep
してsort
してuniq
みたいなことをここにあるファイル全部についてやりたい、とか、ファイルを連番でコピーしてsed
でちょっとずつパラメータを変えていきたいとか、そういうことでしょう。つまり、for
文を書く機会がそれなりにあるはずです(前者はgrep 'hoge' piyo/*.fuga
の方が楽ですが)。
では皆さんはbash
でfor
文を書くときどうしていますか? 私はseq
コマンドを使っていました。
for i in `seq -w 1 10`; do
echo ${i}
done
01
02
03
04
05
06
07
08
09
10
-w
オプションを使うと幅が調整されるので連番ファイルの時に地味に便利です。
ですが、例えばseq
ではアルファベット順に何かをすることが難しいです。
for a in `seq A D`; do
echo ${a}
done
A
B
C
D
# となってほしいが、ならない
これは、例えば以下のようにすれば動くんですが、
for a in A B C D; do
echo ${a}
done
A
B
C
D
# 今度は動く
これはプログラマとしてはあまり嬉しくないです。このコードは「怠惰」ではありません。A
からZ
まで回したいときはどうするんですか? プログラマが根性で問題を解決してはいけません。
といってseq
で対応するASCIIコードを吐いてprintf
コマンドを使う、というようなことをすると化け物みたいなコードが生まれます。bash
ワンライナーは可読性という言葉からかなり遠いところにある概念ですが、それでもASCIIコードが生で登場するワンライナーは一段階ステージが上でしょう。
解決策
というわけで本題です。以下が動きます。
for a in {A..D}; do
echo ${a}
done
A
B
C
D
上で言っていた方法は全てお払い箱です。可読性は十分、そして簡単。これで全部なんとかなります。
これは想像以上に強力な機能で、例えばABCD
と回した後に1234
と回したい時などもそれぞれ書けばできますし、
# {}間にスペースが必要
for a in {A..D} {1..4}; do
echo ${a}
done
A
B
C
D
1
2
3
4
またA1 A2 A3 A4 B1 B2 ...
と回したい時も、2重ループなんぞ必要ありません。
# {}間にスペースを入れず、詰めて書く
for a in {A..D}{1..4}; do
echo ${a}
done
A1
A2
A3
A4
B1
B2
B3
B4
C1
C2
C3
C4
D1
D2
D3
D4
え、ファイルのIDはアンダースコアで区切ってある? 中々見やすい名前をつけていますね。では一気に回しましょう。変数なんか1個でいいですよ。
for a in data_{A..D}_{1..4}.dat; do
echo ${a}
done
data_A_1.dat
data_A_2.dat
data_A_3.dat
data_A_4.dat
data_B_1.dat
data_B_2.dat
data_B_3.dat
data_B_4.dat
data_C_1.dat
data_C_2.dat
data_C_3.dat
data_C_4.dat
data_D_1.dat
data_D_2.dat
data_D_3.dat
data_D_4.dat
ほう、data_A_01.dat
のようになっているので数字の頭に0を付けたいと。幅が統一され、見目麗しくなる素晴らしい命名規約だと思います。今後もぜひ続けて下さい。同じだけ簡単にループで処理できるので。
for a in data_{A..D}_{01..10}.dat; do
echo ${a}
done
data_A_01.dat
data_A_02.dat
data_A_03.dat
data_A_04.dat
data_A_05.dat
# ... (中略) ...
data_D_06.dat
data_D_07.dat
data_D_08.dat
data_D_09.dat
data_D_10.dat
最高ですね。
補遺
もちろん、刻み幅も変えられます。
for i in {01..10..2}; do
echo ${i}
done
01
03
05
07
09
アルファベットでもお構いなしです。
for a in {A..G..2}; do
echo ${a}
done
A
C
E
G