〜2015年4月中旬〜
awk万能。
% awk '{getline col2 < "col2.txt"; print $1 "\t" col2}' col1.txt
$2へ読み込むという方法もある。
% awk '{getline $2 < "col2.txt"; print}' OFS='\t' col1.txt
さらにゴルフするとprintも省略できる。
% awk 'getline $2 < "col2.txt"' OFS=\\t col1.txt
headをsedやawkで。
% sed -n '1,10p' hightemp.txt % sed '1,10!d' hightemp.txt % awk 'NR==1,NR==10' hightemp.txt % awk 'NR<=10' hightemp.txt
こんな感じが自然だが、元のファイルが非常に大きい場合は無駄に最後まで読んでしまう。
そこで発想を変えて、10行表示したら終了すると考える。
% sed '10q' hightemp.txt % awk '1;NR==10{exit}' hightemp.txt
こうすれば瞬時に終了する。
ruby-talk MLがspam bombにまたやられてしまった。
で、SpamAssassinはどう判断してたのか確認してみると、ちゃんとspamと判断していた。
X-Spam-Flag: YES X-Spam-Status: Yes, score=6.3 required=5.0 tests=FSL_HELO_NON_FQDN_1, HELO_NO_DOMAIN,RDNS_NONE,SUBJ_ALL_CAPS autolearn=disabled version=3.3.1
うーむ。From詐称でメンバーからのアドレスだとSpamAssassinの結果が無視されるのかと、
Mailman/Handlers/SpamAssassin.pyを読んでみたら、
無視じゃなくてMEMBER_BONUSだけ引かれてることがわかった。
MailmanではYESは見ずにscoreを自前で評価しているんだな。
MEMBER_BONUSのデフォルト値が2なのでscoreは4.3となり5よりも小さくなってしまう。
このあたりのぎりぎりの攻防ですり抜けてしまったようだ。
とりあえずmm_cfg.pyでMEMBER_BONUSを0にして様子見。
今年の祝日が知りたくなったのでicalendarをいじってみた。
% curl -s https://www.google.com/calendar/ical/ja.japanese%23holiday%40group.v.calendar.google.com/public/basic.ics |\ awk 'sub(/^(SUMMARY|DTSTART.*DATE):/, "")' |\ awk -F'\r' '$0~strftime("%Y")&&getline $2' | sort 20150101 元日 20150112 成人の日 20150211 建国記念の日 20150321 春分の日 20150429 昭和の日 20150503 憲法記念日 20150504 みどりの日 20150505 こどもの日 20150506 憲法記念日 振替休日 20150720 海の日 20150921 敬老の日 20150922 国民の休日 20150923 秋分の日 20151012 体育の日 20151103 文化の日 20151123 勤労感謝の日 20151223 天皇誕生日
改行コードがCR+LFなのでCRを消すために-F'\r'としている。
lessで見ると行の途中にあるCRしか目立たないので肝心の行末を処理してなかった。
というわけで、
awk 'sub(/^(SUMMARY|DTSTART.*DATE):/, "")' |\ awk -F'\r' '$0~strftime("%Y")&&getline $2' | sort
の部分は
awk 'sub(/^(SUMMARY|DTSTART.*DATE):/, "")' RS='[\r\n]+' |\ awk '$0~strftime("%Y")&&getline $2' | sort
と修正。これでLFでもCRLFでもok。dos2unixを通してもいいが。
with_indexを使わない方法を考えていたら、こんなのを思い付いた。
% ruby -e 't=true;p [*1..11].select{t=!t}' [2, 4, 6, 8, 10]
まあ、こんどはtが気に入らないという話になるわけだが。
twitterでputs("hoge")は"hoge"と"\n"と分割されてwrite(2)されるという指摘があった。
これはputsの挙動を考えると納得する。
putsは最後に"\n"がないときにだけ"\n"を追加する。
これをそのまま実装すると"hoge"を出力してから最後の"\n"の有無を判断し出力となる。
つまりわざわざ"hoge"+"\n"にしてから出力するような処理にはならない。
出力先がttyならunbufferedなのでwriteが2回呼ばれることになり、
ファイルならbufferされるのでたぶん1回で済む。
% strace -e write -o out.log ruby -e 'puts "hoge"'; cat out.log hoge write(1, "hoge", 4) = 4 write(1, "\n", 1) = 1 write(4, "!", 1) = 1 +++ exited with 0 +++ % strace -e write -o out.log ruby -e 'STDERR.puts "hoge"'; cat out.log hoge write(2, "hoge", 4) = 4 write(2, "\n", 1) = 1 write(4, "!", 1) = 1 +++ exited with 0 +++ % strace -e write -o out.log ruby -e 'puts "hoge"' > /dev/null; cat out.log write(1, "hoge\n", 5) = 5 write(4, "!", 1) = 1 +++ exited with 0 +++ % strace -e write -o out.log ruby -e 'puts "hoge\n"'; cat out.log hoge write(1, "hoge\n", 5) = 5 write(4, "!", 1) = 1 +++ exited with 0 +++
この"!"はいったい?
例によってUstで参加。とりあえず準備1だけ。今回一番難しい問題と言われている。
% time zcat access_log.nasa.gz|ruby -EASCII-8BIT -F' ' -ane 'a=$F[3][1..-1].split(/[:\/]/);print a[2],{"Jul"=>"07","Aug"=>"08"}[a[1]],a[0]," ",a[3],a[4],a[5]," ",$_' >access-ruby.log zcat access_log.nasa.gz 3.89s user 0.10s system 4% cpu 1:37.52 total ruby -EASCII-8BIT -F' ' -ane > access-ruby.log 96.16s user 1.24s system 99% cpu 1:37.54 total
TimeとかDateオブジェクトにしてstrftimeするとものすごく遅くなるので、JulとAugしかない点を利用する。
いまいち。
perlでもやってみようと思ったら、-F' 'の挙動がよくわからない。
-Fpattern specifies the pattern to split on for -a. The pattern may be surrounded by "//", "", or '', otherwise it will be put in single quotes. You can't use literal whitespace in the pattern.
使えないと言われてもねえ。
% echo 'a b' | perl -F' ' -e 'print join(":",@F)' a: :b:
空文字でsplitされるようで。本当に使えないようだ。\sじゃ意味が違うし。
同じことをawkで。
% time zcat access_log.nasa.gz|awk -F' ' '{split(substr($4,2,21),a,/[:\/]/);h["Jul"]="07";h["Aug"]="08";print a[3]h[a[2]]a[1],a[4]a[5]a[6],$0}' >access-awk.log zcat access_log.nasa.gz 3.75s user 0.08s system 22% cpu 16.681 total awk -F' ' > access-awk.log 14.53s user 0.68s system 91% cpu 16.683 total
実はgawkのほうが断然速い。この場合LANG=Cはあまり関係ない。mawkにするともっと速くなる。
% time zcat access_log.nasa.gz|mawk -F' ' '{split(substr($4,2,21),a,/[:\/]/);h["Jul"]="07";h["Aug"]="08";print a[3]h[a[2]]a[1],a[4]a[5]a[6],$0}' >access-awk.log zcat access_log.nasa.gz 2.05s user 0.05s system 38% cpu 5.477 total mawk -F' ' > access-awk.log 4.81s user 0.66s system 66% cpu 8.211 total
つづく。
まだ引っ張るわけで。今回はsedで。
% time zcat access_log.nasa.gz | sed -r 's,(.*\[(..)/(...)/(....):(..):(..):(..).*),\4\3\2 \5\6\7 \1,;s/^(....)Jul/\107/;s/^(....)Aug/\108/' > access-sed.log zcat access_log.nasa.gz 4.42s user 0.08s system 6% cpu 1:09.81 total sed -r > access-sed.log 67.95s user 0.85s system 97% cpu 1:10.40 total
意外にawkよりも遅いというか、実は大抵GNU sedはGNU awkよりも遅い。
-rを使わなくても書けるが、速度に大した差はないのでわかりやすい例を挙げた。
\107と書くと107番目になるわけではなく、
実は1から9までの1桁しか有効ではないので、特に問題はない。
つまり()を10個以上書いても参照しようがない。
実は昨日のsed scriptはほとんどperl scriptと言ってもいいぐらい似ている。
そのままではPerlだと\107が8進数を意味するのでまずい。
\108は8が8進数ではないので\10と8つまり\b8という意味なる。
これを回避する${1}07,${1}08を使えばいい。
% time zcat access_log.nasa.gz | perl -pe 's,(.*\[(..)/(...)/(....):(..):(..):(..).*),\4\3\2 \5\6\7 \1,;s/^(....)Jul/${1}07/;s/^(....)Aug/${1}08/' > access-perl3.log zcat access_log.nasa.gz 2.87s user 0.07s system 15% cpu 19.598 total perl -pe > access-perl3.log 18.89s user 0.72s system 88% cpu 22.246 total
結構速い。sedでs///するならperlでs///したほうがまし。ほとんど同じだし。