日記(シェル芸で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ビットに戻してからパックしているので、データ読み出すときにバイトオーダはたぶん気にしなくてよいだろう。たぶん。

ね?簡単でしょ?

・・・簡単じゃねーよ。すぐ終わったけど。

寝る。

ノート   このエントリーをはてなブックマークに追加 
 

prev:日記(実験データ解析にGlueLang投入から慌ててwhileを実装) next:日記(シェル芸フル動員のデータ集計)

やり散らかし一覧

記事いろいろ