〜2015年1月下旬〜
awkの$3は3つあることを確認している。
つまりsedでも3個あることを確認すればいい。
フィールドが3個ということは区切りは2個なので/ .* /でok。
% seq 100|xargs -n3|sed '/ .* /s/[^ ]*$/Fizz/'|xargs -n5|sed '/ .* .* .* /s/[0-9]*$/Buzz/'|xargs -n1
ちょっと不恰好だけど、正規表現で条件を書けばいける。
while read lineで読んでいるときに最終行に改行がないとループを抜けてしまう。
だが、read自身はちゃんとlineへ反映させているのであった。
% bash -c 'echo -n foo | while read line; do echo $line; done' % bash -c 'echo -n foo | { while read line; do echo $line; done; echo $line; }' foo
つまりwhileの条件でreadが偽だったら$lineの中身を見ればいい。たとえば
% bash -c 'echo -n foo | { while read line || [ -n "$line" ]; do echo $line; done; }' foo
みたいに。
これはdiffでもいいけどcommとかjoinを使うと簡単。
% seq 250 | sed 128d | sort > nums % seq 250 | sort | comm -23 - nums 128
1つだけじゃなく何個でもok。
% seq 250 | sed '50d;128d;200d' | sort > nums % seq 250 | sort | comm -23 - nums 128 200 50 % seq 250 | sort | join -v1 - nums 128 200 50
commやjoinはあらかじめsortしておく必要がある。
commは共通部分を抜き出すのに使うが、複数並んでいても1個しか相手にされない。
一方joinは並んでいると全部共通部分とみなす。これは何を意味するかというとgrepの代わりに使える。
% printf '1\n2\n2\n2\n3\n' | comm -12 - <(echo 2) 2 % printf '1\n2\n2\n2\n3\n' | join - <(echo 2) 2 2 2
grepの代わりになって何がうれしいんだという話ではあるが。
よく考えてみたらcommを使わなくてもgrepでいけるのであった。
% seq 250 | sed 128d > nums % seq 250 | grep -xv -f nums 128 % seq 250 | sed '50d;128d;200d' > nums % seq 250 | grep -xv -f nums 50 128 200
-xは強制的に行全体でPATTERNの一致処理を行うという意味。^と$がつく感じ。
この場合は-wでもいいが、空白とか含まれる場合は-xが必須となる。
sortする必要もないし、こっちのほうがいいかも。
例の問題はgrepで250ぐらいならなんともないが、
10万ぐらいになると8GBあってもメモリが足りなくなる。
% seq 100000 | sed 5000d > nums100K % time seq 100000 | grep -xv -f nums100K zsh: broken pipe seq 100000 | zsh: killed grep -xv -f nums100K seq 100000 0.00s user 0.00s system 0% cpu 19:12.16 total
/var/log/syslogを見るとOOM Killerに殺されていた。
Jan 26 23:07:41 m kernel: [ 1572.506071] Out of memory: Kill process 7702 (grep) score 922 or sacrifice child Jan 26 23:07:41 m kernel: [ 1572.506073] Killed process 7702 (grep) total-vm:15207580kB, anon-rss:7427400kB, file-rss:0kB
こういうときはどうするかというとfgrepを使う。
% time seq 100000 | fgrep -xv -f nums100K 5000 seq 100000 0.00s user 0.00s system 1% cpu 0.191 total fgrep -xv -f nums100K 0.06s user 0.00s system 33% cpu 0.194 total
まじかっていうほどあっさり通る。正規表現がいかにメモリを食うかがわかる例。
5000万ぐらいになるとfgrepでも結構苦しくなる。
% seq 50000000 | sed 5000d > nums50M % time seq 50000000 | fgrep -xv -f nums50M 5000 seq 50000000 0.99s user 0.25s system 1% cpu 1:55.80 total fgrep -xv -f nums50M 113.18s user 2.62s system 99% cpu 1:56.03 total
2分近くかかっている。少しでも速く実行したいならjoinやcommを使う。
% time seq 50000000 | sort | join -v1 - <(sort nums50M) 5000 seq 50000000 0.77s user 0.13s system 7% cpu 12.189 total sort 15.41s user 1.81s system 35% cpu 48.196 total join -v1 - <(sort nums50M) 8.13s user 0.28s system 17% cpu 48.195 total % time seq 50000000 | sort | comm -23 - <(sort nums50M) 5000 seq 50000000 0.81s user 0.14s system 9% cpu 9.953 total sort 15.62s user 1.96s system 41% cpu 42.238 total comm -23 - <(sort nums50M) 4.22s user 0.28s system 10% cpu 42.237 total
2倍以上速い。実はほとんどソートしてる時間だったりする。
あらかじめソートしといていいなら10秒かからない。
% seq 50000000 | sort > seq50Ms % seq 50000000 | sed 5000d | sort > nums50Ms % time join -v1 seq50Ms nums50Ms 5000 join -v1 seq50Ms nums50Ms 6.95s user 0.10s system 99% cpu 7.060 total % time comm -23 seq50Ms nums50Ms 5000 comm -23 seq50Ms nums50Ms 3.52s user 0.11s system 99% cpu 3.633 total
grepの代わりになって実はうれしいのである。
PDFを画像にするときの定番にconvertがある。
-densityで指定しても低いままだなあとずっと思っていたが、
これも順番がものすごく大事だとわかった。
たとえば
% convert foo.pdf -density 300 foo.jpg
とかやりがちだが、これだと全然意味がない。
% convert -density 300 foo.pdf foo.jpg
のようにfoo.pdfの前に-densityを置く。-verboseをつけるとよくわかる。
% convert -verbose '残照.pdf[1]' -density 300 /tmp/foo.jpg "gs" -q -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 "-sDEVICE=pngalpha" -dTextAlphaBits=4 -dGraphicsAlphaBits=4 "-r72x72" -dFirstPage=2 -dLastPage=2 "-sOutputFile=/tmp/magick-_Uc3SdUw-%08d" "-f/tmp/magick-qPsh2kGe" "-f/tmp/magick-fujmhwsW" /tmp/magick-_Uc3SdUw-00000001 PNG 298x496 298x496+0+0 8-bit DirectClass 4.22KB 0.010u 0:00.000 残照.pdf[1]=>残照.pdf[1] PDF 298x496 298x496+0+0 16-bit DirectClass 0.000u 0:00.000 残照.pdf[1]=>/tmp/foo.jpg[1] PDF 298x496 298x496+0+0 16-bit DirectClass 0.000u 0:00.000
内部的にはgsが変換してるが、-r72x72となってる。つまり-density 72ということだ。
これはconvertのデフォルトの値と思われる。
ファイル名の後に-density 300を指定しても後の祭りということになる。
% convert -verbose -density 300 '残照.pdf[1]' /tmp/foo.jpg "gs" -q -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 "-sDEVICE=pngalpha" -dTextAlphaBits=4 -dGraphicsAlphaBits=4 "-r300x300" -dFirstPage=2 -dLastPage=2 "-sOutputFile=/tmp/magick-YslpFx3t-%08d" "-f/tmp/magick-XPhLyekZ" "-f/tmp/magick-mVgnGZAu" /tmp/magick-YslpFx3t-00000001 PNG 1240x2066 1240x2066+0+0 8-bit DirectClass 17.3KB 0.060u 0:00.060 残照.pdf[1]=>残照.pdf[1] PDF 1240x2066 1240x2066+0+0 16-bit DirectClass 0.000u 0:00.000 残照.pdf[1]=>/tmp/foo.jpg[1] PDF 1240x2066 1240x2066+0+0 16-bit DirectClass 16.4KB 0.040u 0:00.039
これで-r300x300になった。
GHOSTが話題だが このページにあるglibcを使ってるプロセスを探す方法はつっこまずにはいられない。
$ lsof | grep libc | awk '{print $1}' | sort | uniq
コメントでawkだのsort -uを使えば短くなるとかあるのはまあお約束として、問題はlibcで検索してるところ。
% lsof |grep -o 'libc.*' | sort -u libc-2.19.so libc.mo libcairo-gobject.so.2.11301.0 libcairo.so.2.11301.0 libcanberra-0.30/libcanberra-pulse.so libcanberra-gtk-module.so libcanberra-gtk.so.0.1.9 libcanberra-gtk3-module.so libcanberra-gtk3.so.0.1.9 libcanberra.so.0.2.5 libcap-ng.so.0.0.0 libcap.so.2.24 libcgmanager.so.0.0.0 libcom_err.so.2.1 libcpufreq.so.0.0.0 libcpugraph.so libcroco-0.6.so.3.0.1 libcrypt-2.19.so libcrypto.so.1.0.0 libcups.so.2 libcurl.so.4.3.0
libcで始まるライブラリはいっぱいあるわけで。
というよりもすべてのプロセスはlibcに依存していると言っていいわけで、
libcで検索すること自体ナンセンス。
lsofは引数で指定したファイルをopenしているプロセスを表示してくれるので、
たとえばlibcrypto.soを使ってるのを探したければ、こんな感じでok。
% lsof /lib/x86_64-linux-gnu/libcrypto.so.1.0.0 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME python 2958 eban mem REG 8,2 1961344 43253810 /lib/x86_64-linux-gnu/libcrypto.so.1.0.0 python 2967 eban mem REG 8,2 1961344 43253810 /lib/x86_64-linux-gnu/libcrypto.so.1.0.0 python 2979 eban mem REG 8,2 1961344 43253810 /lib/x86_64-linux-gnu/libcrypto.so.1.0.0 indicator 2990 eban mem REG 8,2 1961344 43253810 /lib/x86_64-linux-gnu/libcrypto.so.1.0.0 ssh 3435 eban mem REG 8,2 1961344 43253810 /lib/x86_64-linux-gnu/libcrypto.so.1.0.0 ssh 3438 eban mem REG 8,2 1961344 43253810 /lib/x86_64-linux-gnu/libcrypto.so.1.0.0 ssh 3611 eban mem REG 8,2 1961344 43253810 /lib/x86_64-linux-gnu/libcrypto.so.1.0.0 ssh 3622 eban mem REG 8,2 1961344 43253810 /lib/x86_64-linux-gnu/libcrypto.so.1.0.0 ssh 3636 eban mem REG 8,2 1961344 43253810 /lib/x86_64-linux-gnu/libcrypto.so.1.0.0 ruby 3748 eban mem REG 8,2 1961344 43253810 /lib/x86_64-linux-gnu/libcrypto.so.1.0.0 x11vnc 22351 eban mem REG 8,2 1961344 43253810 /lib/x86_64-linux-gnu/libcrypto.so.1.0.0 w3m 25870 eban mem REG 8,2 1961344 43253810 /lib/x86_64-linux-gnu/libcrypto.so.1.0.0
xargsの-L1と-n1って1行につき1個書いてあるときは特に違いはないと思っていたが、罠があった。
-L max-lines Use at most max-lines nonblank input lines per command line. Trailing blanks cause an input line to be logically continued on the next input line. Implies -x.
つまり後ろに空白があると次の行へ継続しているとみなされる。
% printf "a \nb\n" | cat -A a $ b$ % printf "a \nb\n" | xargs -L1 -t /bin/echo a b a b % printf "a \nb\n" | xargs -n1 -t /bin/echo a a /bin/echo b b
適当に引数リストを作ってたりすると理解不能なバグになりそう。
xargsで並列実行させる-Pオプションはお手軽でいいが、これにも罠がある。
単に-P4とつけても意味がない。
% seq 10 | xargs -t -P4 /bin/echo 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
一気に実行されてしまう。ここでは-n1が必要になる。
% seq 10 | xargs -t -P4 -n1 /bin/echo 1 /bin/echo 2 /bin/echo 3 /bin/echo 4 1 /bin/echo 5 2 4 /bin/echo 6 /bin/echo 7 3 /bin/echo 8 5 /bin/echo 9 7 6 /bin/echo 10 8 10 9
結果は同じ場合が多いので気づきにくい。