〜2014年7月上旬〜
-printfでサイズを表示するようにして、sedなりで変更するという方法が一般的だと思うが、
findだけで可能なのかやってみた。
準備。
% mkdir tmp % repeat 9 echo foo > tmp/$[++i].txt % touch tmp/0.txt % ls -l tmp 合計 36 -rw-r--r-- 1 eban eban 0 2014-07-01 23:23:59 0.txt -rw-r--r-- 1 eban eban 4 2014-07-01 23:23:52 1.txt -rw-r--r-- 1 eban eban 4 2014-07-01 23:23:52 2.txt -rw-r--r-- 1 eban eban 4 2014-07-01 23:23:52 3.txt -rw-r--r-- 1 eban eban 4 2014-07-01 23:23:52 4.txt -rw-r--r-- 1 eban eban 4 2014-07-01 23:23:52 5.txt -rw-r--r-- 1 eban eban 4 2014-07-01 23:23:52 6.txt -rw-r--r-- 1 eban eban 4 2014-07-01 23:23:52 7.txt -rw-r--r-- 1 eban eban 4 2014-07-01 23:23:52 8.txt -rw-r--r-- 1 eban eban 4 2014-07-01 23:23:52 9.txt
0.txtだけがサイズ0。頭に@をつけるだけなら
% find tmp -size 0 -printf @ -print @tmp/0.txt
でいいわけだが、これだと他のファイルが表示されない。
そこで-trueだ。なんの意味があるんだと思っていたが、こんな使い途が。
% find tmp -type f \( -size 0 -printf @ -or -true \) -print tmp/7.txt tmp/9.txt tmp/8.txt tmp/4.txt tmp/2.txt tmp/6.txt tmp/3.txt tmp/1.txt @tmp/0.txt tmp/5.txt
-or -trueで強制的に真にする。
複数のpaneへ同時入力する設定。
これめちゃめちゃ便利なんだけど、トグル動作させたい。
showw -v synchronize-panesで現在の状態がわかるので、
bind e if 'test `tmux showw -v synchronize-panes` = on' 'setw synchronize-panes off' 'setw synchronize-panes on'
とすればPREFIX eでいけるかと思ったら、なんとon/offを指定しなければ最初からトグルだった。
bind e setw synchronize-panes
でokだ。
220通ほど。平和が戻った。
62進数は
短縮URLでも使われていて、マッピングさえなんとかすればいける。
一方bashでも0-9,a-z,A-Zで62進数も扱える。
man bashによると@,_と合わせて64進数まであり。
62進でe9aを10進に変換するなら簡単。ただし、短縮URLの例ではマッピングが違うので変換が必要。
a-z,A-Z,0-9という順番なので
% bash -c 'echo $[62#`tr a-zA-Z0-9 0-9a-zA-Z <<< e9a`]' 19158
となる。むしろ逆変換のほうが欲しいが、さすがにそれはbashにはない。
zshにはあるが大文字小文字を無視するので36進数まで。
base62のつもりだったが、テーブルの大きさ次第に気づき、Nになった。
#! /bin/bash tbl=({0..9} {a..z} {A..Z} + /) N=${#tbl[@]} z=${tbl[0]} base(){ local i=$1 local b= while ((i > 0)); do b=${tbl[i % N]}$b i=$[i / N] done result=${b:-${tbl[0]}} } for ((i=0; i < $1; i++)); do base $i a="$z$z$z"${result} b=${a%????} echo "${a#$b} $i" done
0の場合を見落としていて、とってつけたような対応になっている。
tblを差し替えればいくらでも。連続する文字は{..}が使えるので楽。
40バイトという記録があるが、
TZ=right/UTC xargs -i date -d@{} +%F\ %T
これって
sed s/^/@/|TZ=right/UTC date -f- +%F\ %T
でもよさげだなあと試してみるも通らない。
サイズは変わらないので通っても通らなくてもいいんだが、
3番目でなぜか数秒ずれるものがある。
どういうことだと元の解でもやってみると、なんとだめだった。
Rubyのもだめだ。
2010年以降が全部ずれてる。
うるう秒の設定ってなんか変わったんだっけ?
昨日作った64進と10進で1000万行ほどデータを作り、
奇数行の64進だけ抜き出したキーをファイルにしてgrepする。
遅いのでperlで書き直した。
% time perl base.pl 10000000 > data.txt % awk 'NR%2{print $1}' data.txt | sort > key.txt % wc -l data.txt key.txt 10000000 data.txt 5000000 key.txt 15000000 合計 % head data.txt key.txt ==> data.txt <== 0001 1 0002 2 0003 3 0004 4 0005 5 0006 6 0007 7 0008 8 0009 9 000a 10 ==> key.txt <== 0++/ 0++1 0++3 0++5 0++7 0++9 0++B 0++D 0++F 0++H
やりたいのはkey.txtの内容でgrepすることだ。
forやwhil readで1個なり数個なりでgrepするのはものすごく時間がかかる。
かといってgrep -f key.txt data.txtは入力を受け付けなくなるぐらい重くなる。
それに+を使っちゃったのでエスケープ処理も必要になる。
じゃあfgrep -f key.txt data.txtならいいかというと、
実は2番目のフィールドの余計なものにマッチする。
% time fgrep -f key.txt data.txt | wc -l 9297500 fgrep -f key.txt data.txt 9.59s user 0.33s system 99% cpu 9.968 total
key.txtが500万行ある割には結構速い。10秒ほどでできてしまう。
% time fgrep -f <(sed 's/$/:/' key.txt) <(sed 's/ /: /' data.txt) | sed 's/://' | wc -l 5000000 fgrep -f <(sed 's/$/:/' key.txt) <(sed 's/ /: /' data.txt) 8.22s user 0.30s system 77% cpu 11.045 total
まあ、こんな小細工が必要になる。
それとは別にawkを使う手もある。
% time awk 'NR==FNR{map[$1];next}$1 in map' key.txt data.txt | wc -l 5000000 awk 'NR==FNR{map[$1];next}$1 in map' key.txt data.txt 6.96s user 0.24s system 99% cpu 7.204 total
これはkey.txtを全部連想配列のmapに入れてdata.txtの$1だけを見て判断している。
共通フィールドを抜き出すときによく使われる。
NR==FNRで最初のファイルのときだけ真になるのでkey.txtだけmapへ入れられる。
awkは参照するだけでエントリーが作られるのでmap[$1]=1とかしなくてもいい。
存在確認はinを使うのが正しい。つづく。
どういうことかというとkey.txtとdata.txtを合わせてsortする。
欲しいのはダブったところなので、uniq -dで取り出せばいい。
ただそのままだと2番目のフィールドが邪魔なので-w4をつけてkeyだけを対象にする。
-wの場合は最初に現われたものが残るので
0001 1 0001
という具合に並べておけばいい感じにgrepと同じ結果になる。
以上まとめると
% time sort -r key.txt data.txt | uniq -d -w4 > out sort -r key.txt data.txt 5.18s user 0.42s system 237% cpu 2.360 total
とすればいい。しかも結構速い。
これだとawkのようにkey.txtの内容をメモリに全部取り込むこともないので、
たぶんもっと巨大なデータになっても対応できるはず。
一応チェック。
% grep '[02468]$' out % wc -l out 5000000 out
さらにつづく。実はまだ方法はある。
joinというと2つのファイルを共通フィールドで結合するという目的使われる。
フィールドに差異があっても共通な部分しか表示されない。
ってことはそれってgrepだよね。
% time join key.txt <(sort data.txt) > out2 join key.txt <(sort data.txt) > out2 1.78s user 0.13s system 68% cpu 2.782 total
これも結構速い。アルゴリズム的にはマージソートと同じようなもんだからか。
全部読み込んでから処理するわけではないのでメモリも気にする必要はない。
そもそもdot by dotで画像は変換済みなのでebook-convertを使う必要もなく、
epubへ変換できればいいんだよなあ。
htmlとか画像数だけ作ればいいだけだし自前で作るか。
convertの-resizeにはいろんなgeometry表現で指定できるが、
後ろにつく記号の意味をすぐ忘れるので備忘録。
まずはなにもつけない場合:
% convert -size 100x100 xc: -resize 150x200 info: xc: XC 150x150 150x150+0+0 16-bit DirectClass 0.010u 0:00.009 % convert -size 100x100 xc: -resize 200x150 info: xc: XC 150x150 150x150+0+0 16-bit DirectClass 0.010u 0:00.070
これは指定したgeometryに最大限収まる大きさになる。
150x200とか200x150になるわけではなく、縦横比は保存される。
強制的に200x150にするには!を使う。
% convert -size 100x100 xc: -resize 150x200\! info: xc: XC 200x150 150x200+0+0 16-bit DirectClass 0.020u 0:00.000
注意しないといけないのはwidthかheightのどちらかを省略した場合:
% convert -size 100x100 xc: -resize 200x info: xc: XC 200x200 200x200+0+0 16-bit DirectClass 0.020u 0:00.010 % convert -size 100x100 xc: -resize x200 info: xc: XC 200x200 200x200+0+0 16-bit DirectClass 0.010u 0:00.000
のように縦横比は保存されるが、はみ出てしまうかもしれない。