search
LoginSignup
6

More than 3 years have passed since last update.

posted at

bashのbrace expansionでfor文を使いこなせ!

背景

退屈なことは大体shellでなんとかなります。特に普段からコマンドラインに生息している人は、バーっと書いてそのまま動けばこんなに楽なことはありません。

ところで、shellですることと言えば、grepしてsortしてuniqみたいなことをここにあるファイル全部についてやりたい、とか、ファイルを連番でコピーしてsedでちょっとずつパラメータを変えていきたいとか、そういうことでしょう。つまり、for文を書く機会がそれなりにあるはずです(前者はgrep 'hoge' piyo/*.fugaの方が楽ですが)。

では皆さんはbashfor文を書くときどうしていますか? 私は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

参考

Bash Reference Manual -- 3.5.1 Brace Expansion

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
6