日記(シェル芸で1バイトより小さいデータを扱うことを強要されて死ぬ)
Thu Feb 12 21:58:12 JST 2015 (modified: Fri Sep 29 21:38:45 JST 2017)
views: 2009, keywords:コマンド,CLI,寝る,研究,シェル芸,バイナリシェル芸,遊んでいるわけではない この記事は最終更新日が7年以上前のものです。
バイナリのファイルを縮めるという苦行をせざるを得なかったので、日記にしたためて自分を供養。シェル芸勉強会のような感じで。
問題
次のようなバイナリの1バイトごとに「-1」、「0」、「1」、「2」という数字がchar型で入っています。
-1, 0, 1, 2の4種類しかデータがないので、1バイト(8ビット)ではなくて2ビットで十分なので、1バイトを2ビットにしてデータを詰めてください。-1を00、0を01、1を10、2を11で表現します。
uedambp:dat ueda$ xxd -ps policy.dat | head
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffff01ffff0000ff010001
ffff010001ffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffff01010100010101000000ff01ff00000000ffff01
010101000000ffffffff01010100ffff000000ffffffffffffffffffff00
ffffffffffffffff0000ff000100ffff0000ffff0000ffffffffffffffff
ffffffffffffffffffff00ffff0000ffff0001010000ffff010100010100
ffffff0101ff01010101ff01010101010001010101ffffff000101010101
うーん。プログラム書くほどでもないよなあ・・・。(いや、普通書くけど。)
解答
ということでワンライナーで解答します。Macで作りました。coreutilsを使っています。
とりあえずどんなデータがあるか確認。
uedambp:dat ueda$ xxd -ps policy.dat | gfold -b2 | uniq | LANG=C sort -u
00
01
02
ff
整数に変換して(1を足して)また確認。
uedambp:dat ueda$ xxd -ps policy.dat | gfold -b2 |
sed -e 's/ff/0/' -e 's/00/1/' -e 's/01/2/' -e 's/02/3/' | uniq | LANG=C sort -u
0
1
2
3
こいつをよんれつに並べ・・・(xargs -n 4でもよいが遅い。)
uedambp:dat ueda$ xxd -ps policy.dat | gfold -b2 |
sed -e 's/ff/0/' -e 's/00/1/' -e 's/01/2/' -e 's/02/3/' |
awk 'NR%4==0{print $1}NR%4!=0{printf $1" "}' | head
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
2ビットずつシフトさせながら足し・・・
uedambp:dat ueda$ xxd -ps policy.dat | gfold -b2 |
sed -e 's/ff/0/' -e 's/00/1/' -e 's/01/2/' -e 's/02/3/' |
awk 'NR%4==0{print $1}NR%4!=0{printf $1" "}' | awk '{print $1 + $2*4 + $3*16 + $4*64}' | less
...
0
0
0
106
106
133
84
129
106
5
160
...
16進数に再び変換。awkのprintfで"%x"でなく"%02x"としないと桁がずれるので注意。(というかちょっと嵌った。)
uedambp:dat ueda$ xxd -ps policy.dat | gfold -b2 |
sed -e 's/ff/0/' -e 's/00/1/' -e 's/01/2/' -e 's/02/3/' |
awk 'NR%4==0{print $1}NR%4!=0{printf $1" "}' |
awk '{print $1 + $2*4 + $3*16 + $4*64}' | gawk '{printf("%02x",$1)}' | head -c 200
000000000000000000000000000000000000000000000000000000805098600200000000000000006a6a
8554816a05a006150000040050645050000000001014a4059a0628aaa89a2a90aa1a900615a056000000
00000000000000000000000000000000uedambp:dat ueda$
最後にxxdで戻す。
uedambp:dat ueda$ xxd -ps policy.dat | gfold -b2 |
sed -e 's/ff/0/' -e 's/00/1/' -e 's/01/2/' -e 's/02/3/' |
awk 'NR%4==0{print $1}NR%4!=0{printf $1" "}' |
awk '{print $1 + $2*4 + $3*16 + $4*64}' | gawk '{printf("%02x",$1)}' |
xxd -r -ps > policy.bin
・・・たぶんこれでOKか・・・。8ビットに戻してからパックしているので、データ読み出すときにバイトオーダはたぶん気にしなくてよいだろう。たぶん。
ね?簡単でしょ?
・・・簡単じゃねーよ。すぐ終わったけど。
寝る。