「第62回シェル芸勉強会」リモート参加レポート
2022-12-03(土)「第62回シェル芸勉強会」が開催された。まずは上田さんへ、問題作成と勉強会運営ありがとうございました。参加者の皆様もお疲れ様でした。今回は東京と福岡の会場開催がありませんでしたが、大阪と長崎ではサテライト会場が設けられました。
勉強会の情報
勉強会主催者の上田さんが公開されているリンク集をご覧ください。
勉強会の内容について
今回の問題はいろんな形式のデータファイルをいじる問題。ファイルフォーマットの仕様を調べることが必要になりました。
問題と解答
問題に使うデータファイルは、出題者上田さんが公開している下記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枚の環境が欲しいと思う今日この頃です。