2022-12-03(土)「第62回シェル芸勉強会」が開催された。まずは上田会長へ、問題作成と勉強会運営に感謝。参加者の皆様もお疲れ様。東京と福岡の会場は無しだが、大阪と長崎ではサテライト会場が設けられた。
勉強会募集サイト
勉強会開催アナウンス
大阪サテライト会場アナウンス
長崎サテライト会場アナウンス
勉強会開催報告など
Togetterまとめ
シェル芸勉強会リンク集
【後ほど記載】
Youtubeライブ配信の録画
大阪サテライトレポート
参加レポート
TLプレゼン資料
勉強会の内容について
毎度のことながら、問題作成と解説配信ワンオペの上田会長に感謝。 今回の問題はいろんな形式のデータファイルをいじる問題。ファイルフォーマットの仕様を調べることが必要になった。
問題と解答
問題に使うデータファイルは、下記のGitリポジトリから取得。
$ git clone https://github.com/ryuichiueda/ShellGeiData.git $ cd ShellGeiData/vol.62
問題や解答例は、TogetterまとめやYoutubeライブ配信の録画を参照いただきたい。以下、私なりの解説を掲載する。
Q1
PowerPoint(pptx)のファイル内に含まれる文字データからunko
という文字列を作り出す問題。最初はunko
という文字列を探すと題意を間違って解釈していたが、unko
という文字を作り出せば良い。
最初はstrings
コマンド使って文字列を探したが、文字を削り過ぎてunko
が見つからなかった。以下、上田会長の解答を解説する。tr -dc
を使って、指定した文字(u、n、k、o)以外を削除し、grep -o
で文字列unkoを切り出す。ウ○○
という文字を見つける別解などは動画を参照のこと。
$ cat message.pptx | tr -dc 'unko' nonnoknnukonooonnuuononnuukoouokukn(略)uouououououououououououououououoounuoooooooonno $ cat message.pptx | tr -dc unko | grep -o unko unko
Q2
ファイルの種別を調べるにはfile
コマンドを使うのが常套手段。画像や動画ファイルの場合は、ImageMagickのidentify
コマンドを使うとより詳細な内容が確認可能。file
コマンドを使う方法は動画を参照のこと。
今回はそれ以外の方法で、PNG画像ファイルの仕様を調べてファイルの中身を確認する方法でやってみた。
PNGファイルの場合は、ファイル先頭8バイト分のバイナリが16進数で89504e470d0a1a0a
である。
#PNGファイルの場合 $ xxd -p -l 8 img1.png 89504e470d0a1a0a #PNGファイル以外の場合 $ xxd -p -l 8 img2.png 8e63944f83458393
grep
の終了ステータスを利用して、「PNG」か「PNGじゃない」かを出力するワンライナーはこう書ける。
$ xxd -l 8 -p img1.png | grep '^89504e470d0a1a0a$' > /dev/null && echo PNG || echo PNGじゃない PNG $ xxd -l 8 -p img2.png | grep '^89504e470d0a1a0a$' > /dev/null && echo PNG || echo PNGじゃない PNGじゃない
bash
のforループで、全てのファイルに対して処理する解答がこちら。
$ ls *.png | while read f; do xxd -l 8 -p $f | grep '^89504e470d0a1a0a$' > /dev/null && echo $f PNG || echo $f PNGじゃない; done img1.png PNG img2.png PNGじゃない img3.png PNG img4.png PNGじゃない
Q3
PGM画像ファイルのフォーマットを調べる必要がある問題。解説の詳細は動画を参照のこと。
以下会長の解答を解説する。PGMファイルは先頭3行がASCIIデータで、4行目以降に画像データが含まれる。まずはImageMagickのconvert
コマンドでPNGからPGMへ変換して先頭3行を確認する。第二引数でPGM:-
のように指定すると標準出力へ出力する。
$ convert program.png PGM:- | head -n 3 P5 【←マジックナンバー(バイナリフォーマット)】 120 133 【←画像の解像度(幅、高さ)】 255 【←色の深度(最大255)】
4行目以下のデータをfile
コマンドで確認すると、ELF形式の実行ファイルである可能性が高い。
$ convert program.png PGM:- | tail -n +4 | xxd 00000000: 7f45 4c46 0201 0100 0000 0000 0000 0000 .ELF............ 00000010: 0300 3e00 0100 0000 6010 0000 0000 0000 ..>.....`....... (略) $ convert program.png PGM:- | tail -n +4 | file - /dev/stdin: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=27036e54dcac6372cd6cffc9c84332e3f4ca5f9e, for GNU/Linux 3.2.0, not stripped
4行目以下をファイルに保存して実行すると、僕の環境ではライブラリバージョン不一致のエラーが出た。スタティックリンクした実行バイナリなら実行出来ていたと思われる。
$ convert program.png PGM:- | tail -n +4 > out; chmod +x out; ./out ./out: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.34' not found (required by ./out)
Q4
今度はQ3のようなプログラムを埋め込んだ画像を作る問題。詳細は動画を参照のこと。テキストデータであるスクリプト言語なら、テキストエディタを使って作成可能である。以下手順を記載する。画像の解像度(幅と高さ)とプログラムのバイト数を一致させるのが面倒か?
# PGM形式でデータ部にシェルスクリプトを入れた画像ファイルを作成。 $ vim test.pgm *********** test.pgm *********** P5 10 5 255 #!/bin/bash echo 'Hello World!!!!!!!!!!!!!!!!!!!!' *********** test.pgm *********** # 画像データ部のデータ容量確認 $ cat test.pgm | sed 1,3d #!/bin/bash echo 'Hello World!!!!!!!!!!!!!!!!!!!' $ cat test.pgm | sed 1,3d | wc -c 50 (幅*高さと一致) # PNG形式へフォーマット変換 $ convert test.pgm test.png # fileコマンドでファイルの確認 $ file test.p* test.pgm: Netpbm image data, size = 10 x 5, rawbits, greymap test.png: PNG image data, 10 x 5, 8-bit grayscale, non-interlaced # ImageMagickのidentifyコマンドでファイルの確認 $ identify test.p* test.pgm PGM 10x5 10x5+0+0 8-bit Grayscale Gray 62B 0.000u 0:00.000 test.png PNG 10x5 10x5+0+0 8-bit Gray 256c 256B 0.000u 0:00.000 # PNG形式からPGM形式へフォーマット変換 $ convert test.png PGM:- P5 10 5 255 #!/bin/bash echo 'Hello World!!!!!!!!!!!!!!!!!!!' # ヘッダー部を取り除いて`bash`でシェルスクリプトとして実行 $ convert test.png PGM:- | sed 1,3d | bash Hello World!!!!!!!!!!!!!!!!!!!
Q5
PowerPoint形式のファイルの中から、記載されたテキストを抽出する問題。PowerPoint形式のファイルは、複数のXMLファイルがZIPで圧縮されて一つのファイルに結合されている。
unzip
の-l
オプションで、ZIPファイル内のファイルリストを表示する。
$ unzip -l message.pptx Archive: message.pptx Length Date Time Name --------- ---------- ----- ---- 3247 1980-01-01 00:00 [Content_Types].xml 738 1980-01-01 00:00 _rels/.rels 3497 1980-01-01 00:00 ppt/presentation.xml 3512 1980-01-01 00:00 ppt/slides/slide1.xml (省略) 182 1980-01-01 00:00 ppt/tableStyles.xml 1113 1980-01-01 00:00 docProps/app.xml 681 1980-01-01 00:00 docProps/core.xml 429 1980-01-01 00:00 ppt/revisionInfo.xml --------- ------- 97362 38 files
スライド1枚目の内容はppt/slides/slide1.xml
というファイルに記載されている。unzip -p
でファイルの中身を全て標準出力に出力する。第二引数にファイル名を指定すると、指定したファイルの中身だけ標準出力に出力する。
$ unzip -p message.pptx ppt/slides/slide1.xml <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <p:sld xmlns:a= (略) </p:sld>
スライドに記載されたテキストは<a:t>
タグ内に記載されているようなので、grep -o
を使って切り出す。
$ unzip -p message.pptx ppt/slides/slide1.xml | grep -o '<a:t>[^<]*</a:t>' <a:t>メッセージ</a:t> <a:t>これを読んでいるころは、</a:t> <a:t>シェル芸勉強会に参加されていることでしょう。</a:t> <a:t>どうせ、yes ウンコとかコマンドを</a:t> <a:t>打たさせていると思うと、大変残念です。</a:t> <a:t>どんまいける!</a:t>
あとはタグを消去して完成。解答はこちら。
$ unzip -p message.pptx ppt/slides/slide1.xml | grep -o '<a:t>[^<]*</a:t>' | sed 's;<[^<]*>;;g' メッセージ これを読んでいるころは、 シェル芸勉強会に参加されていることでしょう。 どうせ、yes ウンコとかコマンドを 打たさせていると思うと、大変残念です。 どんまいける!
上田会長の解説に出てきたunzip
の-c
オプションを使うと、ファイル名とファイルの中身を同時に出力出来るのを初めて知った。
Q6
tarアーカイブファイルの中身を覗く問題。tar
コマンドの語源はtape archiveだと思うのだが、テープメディアにtar
コマンドでバックアップをした事ある人はどのくらいいるのだろうか?僕はあるぞ。
小問1
余談はさておき問題を解く。以下上田会長の解答例を解説。とりあえずcat
コマンドで中身を表示すると、ヌル文字(\0)のパディングは表示されず、ファイルのメタデータ(固定長のASCII)とファイルの中身が見える。
$ cat nanika_data.tar ./たーぼーる/0000775000175000017500000000000014342245663015052 5ustar uedaueda./たーぼーる/10周年.txt0000664000175000017500000000002014342245627020203 0ustar uedaueda記念なので ./たーぼーる/うんこ.txt0000664000175000017500000000000714342245701020611 0ustar uedaueda贈呈 ./たーぼーる/シェル芸.txt0000664000175000017500000000001214342245223022016 0ustar uedaueda勉強会
sed
コマンドでファイルのメタデータを行頭に持ってくる。
$ cat nanika_data.tar | sed 's;./たー;\n&;g' ./たーぼーる/0000775000175000017500000000000014342245663015052 5ustar uedaueda ./たーぼーる/10周年.txt0000664000175000017500000000002014342245627020203 0ustar uedaueda記念なので ./たーぼーる/うんこ.txt0000664000175000017500000000000714342245701020611 0ustar uedaueda贈呈 ./たーぼーる/シェル芸.txt0000664000175000017500000000001214342245223022016 0ustar uedaueda勉強会
ファイルを最終更新時刻で並べ替えるため、ファイルのメタデータ部分を最終更新時刻より前と後に分割する。最終更新時刻は137バイト目以降なので、sed
の拡張正規表現を使って(-E
オプションを利用)先頭から137バイト目にスペースを挿入する。
$ cat nanika_data.tar | sed 's;./たー;\n&;g' | LANG=C sed -E 's/^.{136}/& /' ./たーぼーる/00007750001750000175000000000000 14342245663015052 5ustar uedaueda ./たーぼーる/10周年.txt00006640001750000175000000000020 14342245627020203 0ustar uedaueda記念なので ./たーぼーる/うんこ.txt00006640001750000175000000000007 14342245701020611 0ustar uedaueda贈呈 ./たーぼーる/シェル芸.txt00006640001750000175000000000012 14342245223022016 0ustar uedaueda勉強会
sort
コマンドで2列目の最終更新時刻で並べ替えて、改行やヌル文字などのゴミを除去して完成。
$ cat nanika_data.tar | sed 's;./たー;\n&;g' | LANG=C sed -E 's/^.{136}/& /' | sort -k2,2n | grep -a txt | tr '\0' ' ' | awk '{print $1,$NF}' ./たーぼーる/シェル芸.txt 勉強会 ./たーぼーる/10周年.txt 記念なので ./たーぼーる/うんこ.txt 贈呈
小問2
小問1の出力結果から、ファイル名のディレクトリ部分と拡張子部分を削除して完成。
$ cat nanika_data.tar | sed 's;./たー;\n&;g' | LANG=C sed -E 's/^.{136}/& /' | sort -k2,2n | grep -a txt | tr '\0' ' ' | awk '{print $1,$NF}' | sed 's;.*/;;' | sed 's/\.txt //g' シェル芸勉強会 10周年記念なので うんこ贈呈
終わりに
参加レポート記事を書く際に、詳細に書こうとすればするほど時間がかかる。どこまで書けるは時間次第なところではあるが、今後も可能な限りは参加レポート書いて行こうと思う。 今回もLTや二次会も面白かった。会長が問題作成や配信準備、動画編集などされているのもそうだが、かなりの手間や時間がかかっているんだろうなあと痛感した。
自宅から勉強会参加する際はMacbook Proに外付けディスプレイをつなげて使っているのだが、最近外付けディスプレイが2枚に増えた。非常に快適で、サテライト会場開催時にも外付けディスプレイ2枚の環境が欲しいと思う今日この頃である、、、