■ブレース展開と1文字を対象にした正規表現の連続リスト形式への変換 bashは5.0.3(1)、awkはgawkで4.2.1, API: 2.0 (GNU MPFR 4.0.2, GNU MP 6.1.2) $ dpkg -l | awk '/^ii/&&$2 ~ /awk|bash$/{print $2,$3}' bash 5.0-4 gawk 1:4.2.1+dfsg-1 mawk 1.3.3-17+b3 $ echo $BASH_VERSION 5.0.3(1)-release $ bash --version GNU bash, バージョン 5.0.3(1)-release (x86_64-pc-linux-gnu) Copyright (C) 2019 Free Software Foundation, Inc. ライセンス GPLv3+: GNU GPL バージョン 3 またはそれ以降 <http://gnu.org/licenses/gpl.html> This is free software; you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. $ awk --version GNU Awk 4.2.1, API: 2.0 (GNU MPFR 4.0.2, GNU MP 6.1.2) Copyright (C) 1989, 1991-2018 Free Software Foundation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.
■ブレース展開はその速さよりも、以下のような感覚的な書き方が出来る点が良い。 ※「速さ」の点での向き、不向きは後述の余談で。 $ time printf "%s," {0..9} {a..z} {A..Z};echo 0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z, real 0m0.000s user 0m0.000s sys 0m0.000s ■現実にはブレース展開して作成した後、欠番が出ることもあるが、 正規表現の連続リスト形式ではなかなか気づくことが出来ない。 $ touch {0..9} {a..z} {A..Z} $ ls [0-9A-Za-z] 0 2 4 6 8 A C E G I K M O Q S U W Y a c e g i k m o q s u w y 1 3 5 7 9 B D F H J L N P R T V X Z b d f h j l n p r t v x z $ rm Z b $ ls {0..9} {A..Z} {a..z} > /dev/null ls: 'Z' にアクセスできません: そのようなファイルやディレクトリはありません ls: 'b' にアクセスできません: そのようなファイルやディレクトリはありません $ ls [0-9A-Za-z] 0 2 4 6 8 A C E G I K M O Q S U W Y c e g i k m o q s u w y 1 3 5 7 9 B D F H J L N P R T V X a d f h j l n p r t v x z ■存在を予測した正しいブレース展開と正規表現の対は以下のようになる。 $ ls {0..9} {A..Y} a {c..z} >/dev/null $ ls [0-9A-Yac-z] 0 2 4 6 8 A C E G I K M O Q S U W Y c e g i k m o q s u w y 1 3 5 7 9 B D F H J L N P R T V X a d f h j l n p r t v x z ■これを機械的にやることを考える。 かなり力技な気もするけど、先頭行の挿入と最終行の置換でコマンドを作成。 $ echo {0..8} | wc -c 18 $ echo {A..Z} | wc -c 52 $ (echo {0..9};echo {1..8};echo {2..7};echo {3..6};echo {4..5}) | \ awk 'BEGIN{print $0} \ {for(n=1;n<17;n+=2) \ {print substr($0,n);print substr($0,1,(length($0)-(n+1)))} \ }END{print substr($0,17)}' | awk '(NF>1){print " -e \047s/"$0"/["$1"-"$NF"]/g\047 \134" | "sort -uV"}' > seq-list.sh $ (echo {A..Z};echo {B..Y};echo {C..X};echo {D..W};echo {E..V};echo {F..U};echo {G..T};echo {H..S};echo {I..R};echo {J..Q}; \ echo {K..P};echo {L..O;echo {M..N}) | \ awk 'BEGIN{print $0} \ {for(n=1;n<49;n+=2) \ {print substr($0,n);print substr($0,1,(length($0)-(n+1)))} \ }END{print substr($0,49)}' | awk '(NF>1){print " -e \047s/"$0"/["$1"-"$NF"]/g\047 \134" | "sort -uV"}' >> seq-list.sh $ (echo {a..z};echo {b..y};echo {c..x};echo {d..w};echo {e..v};echo {f..u};echo {g..t};echo {h..s};echo {i..r};echo {j..q}; \ echo {k..p};echo {l..o;echo {m..n}) | \ awk 'BEGIN{print $0} \ {for(n=1;n<49;n+=2) \ {print substr($0,n);print substr($0,1,(length($0)-(n+1)))} \ }END{print substr($0,49)}' | awk '(NF>1){print " -e \047s/"$0"/["$1"-"$NF"]/g\047 \134" | "sort -uV"}' >> seq-list.sh $ sed -i -e '1s%.*%#!/bin/bash\nsed \\\n&%' seq-list.sh $ sed -i -e '$ s/ \\//g' seq-list.sh $ chmod +x seq-list.sh ■試してみる。 $ ls [0-9A-Za-z] | tr '\n' ' ' | ./seq-list.sh;echo [0-9] [A-Y] a [c-z] $ rm 6 $ ls [0-9A-Za-z] | tr '\n' ' ' | ./seq-list.sh;echo [0-5] [7-9] [A-Y] a [c-z] $ rm A S $ ls [0-9A-Za-z] | tr '\n' ' ' | ./seq-list.sh;echo [0-5] [7-9] [B-R] [T-Y] a [c-z] $ rm Y $ ls [0-9A-Za-z] | tr '\n' ' ' | ./seq-list.sh;echo [0-5] [7-9] [B-R] [T-X] a [c-z] $ rm M $ ls [0-9A-Za-z] | tr '\n' ' ' | ./seq-list.sh;echo [0-5] [7-9] [B-L] [N-R] [T-X] a [c-z] $ rm H $ ls [0-9A-Za-z] | tr '\n' ' ' | ./seq-list.sh;echo [0-5] [7-9] [B-G] [I-L] [N-R] [T-X] a [c-z] ■「test」で始まるファイルを作成、正規表現の連続リスト形式への変換で試してみる。 最後の1文字を対象にするように抽出して変換、再度ファイル名に書き戻す。 $ touch test{0..9};touch test{A..Z};touch test{a..z} $ ls test* | awk '{print substr($0,length($0))}' | tr '\n' ' ' | ./seq-list.sh | awk '{for(a=1;a<=NF;a++){print "test"$a}}' test[0-9] test[A-Z] test[a-z] $ rm testQ $ ls test* | awk '{print substr($0,length($0))}' | tr '\n' ' ' | ./seq-list.sh | awk '{for(a=1;a<=NF;a++){print "test"$a}}' test[0-9] test[A-P] test[R-Z] test[a-z] ■最後の1文字以外を変数にすると、変数はfor文に与えるなどしてループ処理が出来る。 $ rm B $ filename=test;ls ${filename}* | \ awk '{print substr($0,length($0))}' | tr '\n' ' ' | ./seq-list.sh | awk -v file="${filename}" '{for(a=1;a<=NF;a++){print file$a}}' test[0-9] test[A-P] test[R-Z] test[a-z] ■以下は余談。 seqはそのままでは複数の指定には向かない。 短く書けるブレース展開は便利ではあるけれど、 例えば/8のIPアドレスのリストを出すとしたら、ブレース展開よりもawkの方が速い。 $ echo $(( (2**8) **3)) 16777216 $ time printf "%s\n" 10.{0..255}.{0..255}.{0..255} | wc -l 16777216 real 2m22.792s user 1m56.130s sys 1m40.191s $ echo 1 > zero;time awk '{for(a=0;a<256;a++){for(b=0;b<256;b++){for(c=0;c<256;c++){print "10."a"."b"."c}}}}' zero | wc -l 16777216 real 0m8.995s user 0m8.989s sys 0m0.822s ■mawkは制限が多くて使っていないので、余談として。 $ mawk -W version mawk 1.3.3 Nov 1996, Copyright (C) Michael D. Brennan compiled limits: max NF 32767 sprintf buffer 2040 $ time mawk '{for(a=0;a<256;a++){for(b=0;b<256;b++){for(c=0;c<256;c++){print "10."a"."b"."c}}}}' zero | wc -l 16777216 real 0m11.907s user 0m11.794s sys 0m0.787s ■シンプルに16進数の一覧を出すなら、seqやawkよりもブレース展開の方が速い。 $ time printf "0x%x\n" {0..255} > /dev/null real 0m0.001s user 0m0.001s sys 0m0.000s $ time printf "0x%x\n" $(seq 0 255) > /dev/null real 0m0.010s user 0m0.001s sys 0m0.009s $ time awk '{for(a=0;a<256;a++){printf "0x%x\n",a}}' zero > /dev/null real 0m0.010s user 0m0.001s sys 0m0.009s