第30回シェル芸勉強会に行ってきたので、その復習。

問題、解説はこちら。 今回は、前半の問題は対象の知識があるかどうかに重きを置いていた様子。 後半はいつものような内容の問題だったので、個人的には後半のほうが取っ付きやすい印象だった。

Q1.

特定ディレクトリ配下から、「main.md」ファイルのみを対象に2行目の「Keywords:」行を抽出するという問題。 最初、2行目のみを抽出するためにawkを利用する方法を取っていた。

find ./posts/ -name main.md | xargs grep -n Keywords | awk -F: '$2==2{OFS=":";$2="";print}' | sed 's/::/ /g;s|./posts/||g;s|/main.md||g'
blacknon@BS-PUB-UBUNTU-01:~/ShellGeiData/vol.30$ find ./posts/ -name main.md | xargs grep -n Keywords | awk -F: '$2==2{OFS=":";$2="";print}' | sed 's/::/ /g;s|./posts/||g;s|/main.md||g'
20170806_check_of_webhook Keywords: Webhook
template Keywords:
20170820_injection Keywords: injection
20170810_negi Keywords: ネギ
20170810_negistagram Keywords: Twitter, Instagram, ネギ
20170820_bootstrap Keywords: Bootstrap
20170812_working Keywords: 働けども働けども, bashcms2
20170814_layout Keywords: table, 雑
20170818_bash Keywords: 嫌がらせ

勉強会中に知ったのだが、どうやらgrepでは-mオプション(マッチしたキーワードを-mで指定した回数まで出力する)というオプションがあるらしい。 それと完全に忘れてたのだが、globのパターンマッチングを行うことで(shoptでglobstarが有効にしてる必要があるけど)findを使わずにファイル名を指定して指定ができる。

grep -H -m 1 Keywords: **/main.md | sed -r 's,(posts/|/main.md),,g;s/:/ /1'
blacknon@BS-PUB-UBUNTU-01:~/ShellGeiData/vol.30$ grep -H -m 1 Keywords: **/main.md
posts/20170806_check_of_webhook/main.md:Keywords: Webhook
posts/20170810_negi/main.md:Keywords: ネギ
posts/20170810_negistagram/main.md:Keywords: Twitter, Instagram, ネギ
posts/20170812_working/main.md:Keywords: 働けども働けども, bashcms2
posts/20170814_layout/main.md:Keywords: table, 雑
posts/20170818_bash/main.md:Keywords: 嫌がらせ
posts/20170820_bootstrap/main.md:Keywords: Bootstrap
posts/20170820_injection/main.md:Keywords: injection
posts/template/main.md:Keywords:
blacknon@BS-PUB-UBUNTU-01:~/ShellGeiData/vol.30$ grep -H -m 1 Keywords: **/main.md | sed -r 's,(posts/|/main.md),,g;s/:/ /1'
20170806_check_of_webhook Keywords: Webhook
20170810_negi Keywords: ネギ
20170810_negistagram Keywords: Twitter, Instagram, ネギ
20170812_working Keywords: 働けども働けども, bashcms2
20170814_layout Keywords: table, 雑
20170818_bash Keywords: 嫌がらせ
20170820_bootstrap Keywords: Bootstrap
20170820_injection Keywords: injection
template Keywords:

Q2.

htmlファイル「url.html」で、リンクが相対リンクのもののみ、リンクの頭に「/files/」を付与してやるという問題。 当日はゴリゴリに書いてしまったのだが、後方参照等をうまいこと利用して以下のようにしてやる事で対処できた。

cat url.html|sed -r 's,(href="|src=")([\.a][^"]*),\1/files/\2,g;s,/./,/,g'
blacknon@BS-PUB-UBUNTU-01:~/ShellGeiData/vol.30$ cat url.html|sed -r 's,(href="|src=")([\.a][^"]*),\1/files/\2,g;s,/./,/,g'
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
</head>
<body>
    <ul>
    <li><a href="/files/hoge.html">ほげ</a></li>
    <li><img src="/files/ayasii.jpg" alt="怪しい" /></li>
    <li><a href="https://blog.ueda.tech/">クソブログ</a><a href="huge.html">ふげ</a></li>
    <li><a href="/root.jpg"></a>これはそのまま</li>
    <li><a href="http://www.usptomo.com/">更新してない</a></li>
    </ul>
</body>
</html>

Q3.

Markdownでリスト形式で記述されているlistファイルを、html形式で書き出してサンプルと同じ内容に出力するという問題。 今まで知らなかったのだが、PandocというMarkdownから指定したフォーマットへ変換するコマンドがあるようなので、それを利用してやるといいようだ。

pandoc list -t html5 -s | sed '5,12d;1iContent-Type: text/html\n'
blacknon@BS-PUB-UBUNTU-01:~/ShellGeiData/vol.30$ pandoc list -t html5 -s | sed '5,12d;1iContent-Type: text/html\n'  
Content-Type: text/html

<!DOCTYPE html>

<html>
  <head>
    <meta charset="utf-8">
      <body>
        <ul>
          <li>妬み</li>
          <li>嫉み</li>
          <li>僻み</li>
        </ul>
      </body>  
</html>

Q4.

&&;で無理やりワンライナーにして、GitHubにリポジトリを新規作成して任意のテキストをpushするという問題。 GitHubの提供しているhubコマンドの紹介も兼ねた問題らしく、以下のようにコマンドを実行することでリポジトリの新規作成、ファイルのpushが簡単に行えるらしい(模範解答から抜粋)。

mkdir hoge && cd hoge && git init && echo aho > aho.txt && git add -A && git commit -m "aho" && hub create ryuichiueda/hoge && git push origin master

Q5.

complexファイルの1行目の複素数と2行目の複素数をかけ算するという問題。 perlやPythonなどを使って計算するために、事前にそれぞれの言語用にフォーマットを変換してやる必要がある。

awk '{x[NR]="("$0")"}END{print x[1]" * "x[2]}' complex | perl -lne '{use Math::Complex;print(eval $_)}'
blacknon@BS-PUB-UBUNTU-01:~/ShellGeiData/vol.30$ awk '{x[NR]="("$0")"}END{print x[1]" * "x[2]}' complex | perl -lne '{use Math::Complex;print(eval $_)}'
11+10i

Q6.

フィボナッチ数列で、6765より4個前の数字を出力するという内容。 無理やりPythonで出力をさせて、そこから4個前を抽出するという方法で対処した。

seq 1 50|xargs -I@ python -c 'from math import sqrt;print int(((1+sqrt(5))**@-(1-sqrt(5))**@)/(2**@*sqrt(5)))'|grep 6765 -B 4|head -1
blacknon@BS-PUB-UBUNTU-01:~/ShellGeiData/vol.30$ seq 1 50|xargs -I@ python -c 'from math import sqrt;print int(((1+sqrt(5))**@-(1-sqrt(5))**@)/(2**@*sqrt(5)))'|grep 6765 -B 4|head -1
987

Q7.

数列から2文字ごとの組み合わせを取得し、それらに含まれていない数字の組合せを抽出するという内容。 これについては、比較的短めに記述できた気がする。

seq -w 0 99|grep -v -f <(sed 'p;1s/^.//g' nums|grep -o ..)
blacknon@BS-PUB-UBUNTU-01:~/ShellGeiData/vol.30$ seq -w 0 99|grep -v -f <(sed 'p;1s/^.//g' nums|grep -o ..)
31
33
42
43
56
61
64
65

Q8.

各行に記述されたアルファベット区間から、一番長いものを抽出するという問題。 ブレース展開を利用するのが一番わかりやすかったので、それで解答した(もうちょっと短い書き方あったと思うが)。

cat al*|awk -F- '{print "echo {"$1".."$2"}"}'|bash|awk '{print NR,NF}'|sort -k2|head -1|awk '{print $1}'|xargs -I@ sed -n <a href="https://twitter.com/p" target="_blank" rel="nofollow noopener">@p</a> al*
blacknon@BS-PUB-UBUNTU-01:~/ShellGeiData/vol.30$ cat al*|awk -F- '{print "echo {"$1".."$2"}"}'|bash|awk '{print NR,NF}'|sort -k2|head -1|awk '{print $1}'|xargs -I@ sed -n @p al*
e-q