忙しかったので遅れてしまったけど、先日開催された第38回シェル芸勉強会に参加してきたので、その復習。 前回はちょっと別の用事があったので参加しておらず、ちょっと久しぶりな感じ。

今回は某北◯鮮企画ということで、北朝◯に絡めた問題だけにしたらしい。 (えぇ…?(´・ω・`))

問題及び模範解答はこちら。最初に、問題等に使用するファイルをgitからcloneしておくといい。

Q1

ウェブからデータをスクレイピングしてきて、金ファミリーの誕生日一覧をゲットしようという問題。 うーわ、やりたくないw。と思ったけど、とりあえず取得しよう。(Googleの検索履歴に残るの嫌だからシークレットウィンドウ使おう。。。)

総書記だけならGoogle検索だけで行けるようだけど、ファミリーの中ではメジャーな正男も入れるとGoogleだと出てこなかったので、決め打ちでWikipediaから取ってくることにする。

echo {日成,正日,正恩,正男} | fmt -1 | xargs -I@ bash -c 'printf @:;curl -s https://ja.wikipedia.org/wiki/金@ | grep -m1 -A 3 出生 | grep -m1 -o -E "[0-9]{4}年|[0-9]{1,2}月[0-9]{1,2}日"|uniq|xargs' #シェル芸
[REMOTE][blacknon@bs-pub-ubuntu-03][~]                                                               2019-02-19 09:18:01 (火) JST
(`・ω・´) < echo {日成,正日,正恩,正男} | fmt -1 | xargs -I@ bash -c 'printf @:;curl -s https://ja.wikipedia.org/wiki/金@ | grep -m1 -A 3 出生 | grep -m1 -o -E "[0-9]{4}年|[0-9]{1,2}月[0-9]{1,2}日"|uniq|xargs' #シェル芸
日成:1912年 4月15日
正日:1941年 2月16日
正恩:1984年 1月8日
正男:1971年 5月10日

Q2

「2月16日は、2も16も2のべき乗で、2と1と6を足すと3のべき乗になります。このような日付は他にあるでしょうか?(0乗になる数は除く)」(全文ママ)という、数学っぽいお題。 読解力が低くて現場ではイマイチよく何やるんだかよくわかってなかったので解けなかった。 とりあえず、以下の要素を持つ日付を探せばいいようだ。

  • (x月y日だとして) xとy、どちらも2のべき乗の値(2,4,8,16,32 ...)を使っている日付であること([2,4,8]月[2,4,8,16])日
  • xとyの各桁を合計した時の値が3のべき乗(3,9,27...)であること( 2/16 → 2 1 6 → 2 + 1 + 6 = 9 )

解答例では一度全部の日付を求めているが、現場では以下のように2のべき乗を持つ日付のみを先に出力してから解けば楽だったという話があった。

echo {2,4,8}-{2,4,8,16} | fmt -1

あー、なるほど(´・ω・`)。確かに。 それを踏まえて、解き直したのが以下。

echo {2,4,8}-{2,4,8,16} | # 日付の数字を出力 \
    fmt -1 | # 一行づつに変換 \
    awk -v FS='' '{x=0;for(i=1;i<=length($0);i++){x+=$i};print $0" "x}' | # 月日の合計と日付を出力 \
    grep -f <(seq -f 'echo " "$((3**%g))' 1 10 |bash) # 3のべき乗を持つ日付のみをgrep
[REMOTE][blacknon@bs-pub-ubuntu-03][~]                                                               2019-02-21 22:12:58 (木) JST
(`・ω・´) < echo {2,4,8}-{2,4,8,16} | # 日付の数字を出力 \
>     fmt -1 | # 一行づつに変換 \
>     awk -v FS='' '{x=0;for(i=1;i<=length($0);i++){x+=$i};print $0" "x}' | # 月日の合計と日付を出力 \
>     grep -f <(seq -f 'echo " "$((3**%g))' 1 10 |bash) # 3のべき乗を持つ日付のみをgrep
2-16 9

なるほど、確かに2-16だけがヒットするようだ。

Q3

イランを基準とした時、北朝鮮との時差が何時間あるかを解答せよ、という問題。 北朝鮮はコロコロとタイムゾーンが変わるということだけど、今は日本と同じらしい(コロコロ変えてどうするんだろう…?謎だ)。 単純なタイムゾーンごとの時刻であれば、以下のようにタイムゾーンを指定すれば求めることができる。

TZ=Asia/Tehran date -d '2020-01-01 00:00:00 UTC'
blacknon@bs-pub-ubuntu-03:~$ TZ=Asia/Tehran    date -d '2020-01-01 00:00:00 UTC' +%Y%m%d-%H%M%S
20200101-033000
blacknon@bs-pub-ubuntu-03:~$ TZ=Asia/Pyongyang date -d '2020-01-01 00:00:00 UTC' +%Y%m%d-%H%M%S
20200101-090000

であれば、後はこれを元に時差を計算してやればいいようだ。 地味に30分ズレてるのがめんどくさい…。 しょうがないので、一度時差ベースでの時刻を出した後、その時間をUTCベースのUNIX時刻と扱って、そこから時間を計算させてみる。

eval echo "\$(date -d \"\$(TZ="{Asia/Pyongyang,Asia/Tehran}" date -d '2020-01-01 00:00:00 UTC' '+%F %R')\" +%s)" | xargs bash -c 'x=$(($0-$1));echo $(($x/3600)):$(($x%3600/60))'
blacknon@bs-pub-ubuntu-03:~$ eval echo "\$(date -d \"\$(TZ="{Asia/Pyongyang,Asia/Tehran}" date -d '2020-01-01 00:00:00 UTC' '+%F %R')\" +%s)" | xargs bash -c 'x=$(($0-$1));echo $(($x/3600)):$(($x%3600/60))'
5:30

Q4

Githubリポジトリにある「name」ファイルというファイルの中身について、読めるように復号化しろという問題。 「KPS9566-ISO2022KR」というファイルにそれぞれの文字コードの対応表があるので、それを使うということらしい。

今まで知らなかったのだが、どうやら 北朝鮮にも文字コードがある らしい。そもそも、北朝鮮語とかあるんだ…中国語とか韓国語かと思ってたよ。 で、なんと総書記の名前に使われた文字については、専用の文字コードを割り当ててるとかなんとか。(いやいやいやいや…「」ってかぶってるじゃん。狂ってるよ(´・ω・`)。)

なんかUnicodeにそのキム専用金の文字とかも申請したようだけど、当たり前に弾かれたらしい( 参考資料 。6ページ目の16-21がそれのようだ。地味に丸囲みとかのもあるらしい。)。 普通に考えたらそりゃそうだろとしか言えないのだけど、まぁなんとも言えないですな…(´・ω・`)。

で、文字コードから複合するの自体はあまり興味がなかったので、模範解答にコメントをつけて載っけとくだけにしておく。

cat name                     | # ファイルを開く \
    xxd -ps                  | # xxdでhexにする \
    fold -b4                 | # 4バイトずつにする \
    tr a-f A-F               | # 小文字→大文字変換 \
    awk '{print $1,NR}'      | # 行番号を末尾に追加する  \
    sort                     | # ソートする(joinの前処理) \
    join - KPS9566-ISO2022KR | # 「KPS9566-ISO2022KR」ファイルとJOINする \
    sort -k2,2               | # 2列目でソートする \
    awk '{print $3}'         | # 3列目のみを出力 \
    tr -d '\n'               | # 改行を削除 \
    sed 's/^/0x1B2429430E/g' | # 行頭にバイナリ文字を追加
    xxd -r                   | # バイナリから文字列に出力し直す(ISO2022KR) \
    iconv -f ISO2022KR       | # ISO2022KRからUTF-8に変換

それにしても狂ってますね。 世が世なら、総書記専用ザクとか絶対あったろうなぁと思った。

Q5

echo 正日金成恩男」というコマンドからはじめて、重複ありのランダムな3文字の文字列を得よという問題。
シェル芸botでよくあるスロットマシーン的なものなので、シェル芸botでよく遊んでる人間にはおなじみのやり方で対応できる。

echo 正日金成恩男|shuf -rn 3 -e $(cat|sed 's/\B/ /g')|tr -d '\n';echo
blacknon@bs-pub-ubuntu-03:~/ShellGeiData/vol.40$ echo 正日金成恩男|shuf -rn 3 -e $(cat|sed 's/\B/ /g')|tr -d '\n';echo
正日恩
blacknon@bs-pub-ubuntu-03:~/ShellGeiData/vol.40$ echo 正日金成恩男|shuf -rn 3 -e $(cat|sed 's/\B/ /g')|tr -d '\n';echo
成男男
blacknon@bs-pub-ubuntu-03:~/ShellGeiData/vol.40$ echo 正日金成恩男|shuf -rn 3 -e $(cat|sed 's/\B/ /g')|tr -d '\n';echo
男正男
blacknon@bs-pub-ubuntu-03:~/ShellGeiData/vol.40$ echo 正日金成恩男|shuf -rn 3 -e $(cat|sed 's/\B/ /g')|tr -d '\n';echo
金恩日
blacknon@bs-pub-ubuntu-03:~/ShellGeiData/vol.40$ echo 正日金成恩男|shuf -rn 3 -e $(cat|sed 's/\B/ /g')|tr -d '\n';echo
成恩正
blacknon@bs-pub-ubuntu-03:~/ShellGeiData/vol.40$ echo 正日金成恩男|shuf -rn 3 -e $(cat|sed 's/\B/ /g')|tr -d '\n';echo
日恩金

Q6

北朝鮮選手団が夏季オリンピックで獲得したメダル数について、西暦年、オリンピック名、金銀銅の種別、枚数の一覧表を作ろう。 なお、不参加の年はゼロ枚とする、という問題。

とりあえず、一覧についてはWikipediaにあるので、それを利用すればいい。

w3m -dump https://ja.wikipedia.org/wiki/%E3%82%AA%E3%83%AA%E3%83%B3%E3%83%94%E3%83%83%E3%82%AF%E3%81%AE%E5%8C%97%E6%9C%9D%E9%AE%AE%E9%81%B8%E6%89%8B%E5%9B%A3 | # curlでのデータの取得 \
  sed -n '/^夏季オリンピック\[/,/合計/p' | # 「夏季オリンピック[」から「合計」までの行を抽出 \
  grep '^[0-9]' | # 行頭が[0-9]の行のみを抽出する \
  sed 's/不参加/0 0 0 0/g' | # 不参加の年はすべて0として扱う \
  awk '{for(i=3;i<=5;i++){print $1,$2,"x" i,$i}}' | # 3列目から5列目を別の行に分解する \
  sed 's/x3/金/g;s/x4/銀/g;s/x5/銅/g' # 分解した行をそれぞれの意味(3列目→金、4列目→銀、5列目→銅)に置換する
blacknon@bs-pub-ubuntu-03:~/ShellGeiData/vol.40$ w3m -dump https://ja.wikipedia.org/wiki/%E3%82%AA%E3%83%AA%E3%83%B3%E3%83%94%E3%83%83%E3%82%AF%E3%81%AE%E5%8C%97%E6%9C%9D%E9%AE%AE%E9%81%B8%E6%89%8B%E5%9B%A3 | # curlでのデータの取得 \
>   sed -n '/^夏季オリンピック\[/,/合計/p' | # 「夏季オリンピック[」から「合計」までの行を抽出 \
>   grep '^[0-9]' | # 行頭が[0-9]の行のみを抽出する \
>   sed 's/不参加/0 0 0 0/g' | # 不参加の年はすべて0として扱う \
>   awk '{for(i=3;i<=5;i++){print $1,$2,"x" i,$i}}' | # 3列目から5列目を別の行に分解する \ >   sed 's/x3/金/g;s/x4/銀/g;s/x5/銅/g' # 分解した行をそれぞれの意味(3列目→金、4列目→銀、5列目→銅)に置換する
1972 ミュンヘン 金 1
1972 ミュンヘン 銀 1
1972 ミュンヘン 銅 3
1976 モントリオール 金 1
1976 モントリオール 銀 1
1976 モントリオール 銅 0
1980 モスクワ 金 0
1980 モスクワ 銀 3
1980 モスクワ 銅 2
1984 ロサンゼルス 金 0
1984 ロサンゼルス 銀 0
1984 ロサンゼルス 銅 0
1988 ソウル 金 0
1988 ソウル 銀 0
1988 ソウル 銅 0
1992 バルセロナ 金 4
1992 バルセロナ 銀 0
1992 バルセロナ 銅 5
1996 アトランタ 金 2
1996 アトランタ 銀 1
1996 アトランタ 銅 2
2000 シドニー 金 0
2000 シドニー 銀 1
2000 シドニー 銅 3
2004 アテネ 金 0
2004 アテネ 銀 4
2004 アテネ 銅 1
2008 北京 金 2
2008 北京 銀 1
2008 北京 銅 3
2012 ロンドン 金 4
2012 ロンドン 銀 0
2012 ロンドン 銅 2
2016 リオデジャネイロ 金 2
2016 リオデジャネイロ 銀 3
2016 リオデジャネイロ 銅 2

Q7

過去に北朝鮮のDNSから流出したデータ から、.kp ドメインを持つホストの一覧を作成しろという問題。 普通にgrepで対応できる内容なので、以下のようにすればいい。

awk '$4=="A"' *.zone | cut -f1 | sort -u
blacknon@bs-pub-ubuntu-03:~/ShellGeiData/vol.40/NorthKoreaDNSLeak$ awk '$4=="A"' *.zone | cut -f1 | sort -u
airkoryo.com.kp.
cooks.org.kp.
friend.com.kp.
gnu.rep.kp.
kass.org.kp.
kcna.kp.
kiyctc.com.kp.
knic.com.kp.
koredufund.org.kp.
korelcfund.org.kp.
korfilm.com.kp.
ma.gov.kp.
naenara.com.kp.
ns1.airkoryo.com.kp.
ns1.co.kp.
ns1.com.kp.
ns1.cooks.org.kp.
ns1.edu.kp.
ns1.friend.com.kp.
ns1.gnu.rep.kp.
ns1.gov.kp.
ns1.kass.org.kp.
ns1.kcna.kp.
ns1.kiyctc.com.kp.
ns1.knic.com.kp.
ns1.koredufund.org.kp.
ns1.korelcfund.org.kp.
ns1.korfilm.com.kp.
ns1.kptc.kp.
ns1.ma.gov.kp.
ns1.masikryong.com.kp.
ns1.naenara.com.kp.
ns1.net.kp.
ns1.nta.gov.kp.
ns1.org.kp.
ns1.portal.net.kp.
ns1.rcc.net.kp.
ns1.rep.kp.
ns1.rodong.rep.kp.
ns1.ryongnamsan.edu.kp.
ns1.sdprk.org.kp.
ns1.silibank.net.kp.
ns1.star-co.net.kp.
ns1.star-di.net.kp.
ns1.star.co.kp.
ns1.star.edu.kp.
ns1.star.net.kp.
ns1.vok.rep.kp.
ns2.airkoryo.com.kp.
ns2.co.kp.
ns2.com.kp.
ns2.cooks.org.kp.
ns2.edu.kp.
ns2.friend.com.kp.
ns2.gnu.rep.kp.
ns2.gov.kp.
ns2.kass.org.kp.
ns2.kcna.kp.
ns2.kiyctc.com.kp.
ns2.knic.com.kp.
ns2.koredufund.org.kp.
ns2.korelcfund.org.kp.
ns2.korfilm.com.kp.
ns2.kptc.kp.
ns2.ma.gov.kp.
ns2.masikryong.com.kp.
ns2.naenara.com.kp.
ns2.net.kp.
ns2.nta.gov.kp.
ns2.org.kp.
ns2.portal.net.kp.
ns2.rcc.net.kp.
ns2.rep.kp.
ns2.rodong.rep.kp.
ns2.ryongnamsan.edu.kp.
ns2.sdprk.org.kp.
ns2.silibank.net.kp.
ns2.star-co.net.kp.
ns2.star-di.net.kp.
ns2.star.co.kp.
ns2.star.net.kp.
ns2.vok.rep.kp.
nta.gov.kp.
rodong.rep.kp.
ryongnamsan.edu.kp.
sdprk.org.kp.
vok.rep.kp.

ちなみにこんなにドメインがあるのに、紐付いてるグローバルIPの数を数えると19個くらいしかないようだ。 つまり、1個のグローバルIPに複数のドメインが紐付いてることになる。(ドメインは一番上のしか出力していない)

blacknon@bs-pub-ubuntu-03:~/ShellGeiData/vol.40/NorthKoreaDNSLeak$ awk '$4=="A"' *.zone | sort -V -k5 -u
ns1.airkoryo.com.kp.    432000  IN  A   175.45.176.8
ns2.airkoryo.com.kp.    432000  IN  A   175.45.176.9
ns1.co.kp.      432000  IN  A   175.45.176.15
ns2.co.kp.      432000  IN  A   175.45.176.16
naenara.com.kp.     21599   IN  A   175.45.176.67
rodong.rep.kp.      21599   IN  A   175.45.176.68
airkoryo.com.kp.    21599   IN  A   175.45.176.69
kcna.kp.        21599   IN  A   175.45.176.71
gnu.rep.kp.     21599   IN  A   175.45.176.73
kcna.kp.        21599   IN  A   175.45.176.74
vok.rep.kp.     21599   IN  A   175.45.176.75
ma.gov.kp.      21599   IN  A   175.45.176.76
naenara.com.kp.     21599   IN  A   175.45.176.77
rodong.rep.kp.      21599   IN  A   175.45.176.78
ryongnamsan.edu.kp. 21599   IN  A   175.45.176.79
cooks.org.kp.       21599   IN  A   175.45.176.81
gnu.rep.kp.     21599   IN  A   175.45.176.83
vok.rep.kp.     21599   IN  A   175.45.176.85
ns1.star.edu.kp.    432000  IN  A   175.45.179.76

会場で何個かサイト見てたけど、なんというか…BadStoreとか、そういう2000年台頭のWebサイトの作りという感想。 ログインフォームとか入力もできなかったようだし、Flash動いてるし…。 普通に脆弱性とかてんこ盛りで見つかりそうな印象だった。

Q8

「朝鮮民主主義人民共和国」の総画数を求めよ、という問題。 総画数を求めるとなると結構面倒。どうやら 漢字画数データベース というものがあるので、これで提供しているデータを利用するのが良いようだ。

以下、解答例。

wget https://raw.githubusercontent.com/cjkvi/cjkvi-ids/master/ucs-strokes.txt
echo 朝鮮民主主義人民共和国 | grep -o . | xargs -I@ grep @ ./ucs-strokes.txt | awk '{x+=$NF}END{print x}' #シェル芸
blacknon@bs-pub-ubuntu-03:~/ShellGeiData/vol.40$ echo 朝鮮民主主義人民共和国 | grep -o . | xargs -I@ grep @ ./ucs-strokes.txt | awk '{x+=$NF}END{print x}' #シェル芸
86

LTとかで気になったこと

大阪のLTを見ていたときに、周りで「ターミナルで画像が見れるんだっけ?」みたいな声があった。 見てる限り、使ってるターミナルが自分と同じiTerm2だったので、多分 iTerm2の機能 を使って表示させてたのだろうと思う。LinuxだとTerminatorあたりもできた気がするけど、こちらではあまり試したことがないなぁ。 どちらもSixelGraphicsではない独自機能っぽいので、スクリプトは使い回せないだろうなぁ…。

iTerm2、 OSCエスケープシーケンスでプロファイルをスイッチ できたり(printf "\E]50;SetProfile=プロファイル名\a")もするし、プロファイルごとに背景画像も設定できたりするので、やはりメインの開発環境としてはMacから離れられないなぁ…と思ったりもした。

( GnomeターミナルとかのLinuxのターミナルでもOSCエスケープシーケンスで背景色や文字色は変えられる けど、プロファイルまでは無理(そもそもプロファイルごとに背景画像設定できないし…)だし)