2016年2月15日

第21回シェル芸勉強会参加報告

2016-02-13(土)に開催された、第21回シェル芸勉強会(正式名称:jus共催、第3回今度はたぶん初心者向けに嘘はないでしょう午前のシェル勉強会/第21回未経験者大歓迎!誰でも働けるアットホームな職場ですシェル芸勉強会)に参加しました。
ただ、在住地の関係で直接会場へ伺うことは出来なかったため、Youtubeで配信してくださったライブ中継を利用して、遠隔で参加しました。

勉強会は、午前と午後の、2部構成でした。
午前は、シェルに関する勉強会で、正規表現とコマンドライン処理関連の内容でした。
午後は、シェル芸勉強会で、シェル芸力を付けるための問題を解きました。

以下、勉強会の詳細を記します。


実行環境

  • Arch Linux (x86_64, 4.4.1-2-ARCH)

  • zsh (5.2)

  • bash (4.3.42)

  • GNU coreutils (8.25)

  • gawk (4.1.3)

  • nkf (2.1.4)

  • poppler (pdftohtml, 0.40.0)

シェルに関する勉強会

正規表現

USP友の会 今泉光之(@bsdhack)さんによる、正規表現を一通り学ぶ内容でした。
…​が、寝坊したため、参加できずorz
幸い、使用したスライドを公開してくださったため、後からそちらを参照して自習しました。
(スライドはこちらです: http://www.slideshare.net/bsdhack/ss-58213655)

この内容では、拡張正規表現(ERE)では、後方参照に対応していない事を、初めて知りました。
POSIX 1003.1でも、EREは基本的に対応していないとの記載があって、何だか驚き。
(http://pubs.opengroup.org/onlinepubs/9699919799//basedefs/V1_chap09.html)
EREでも対応している場合は、仕様ではなく実装側で独自に対応しているらしい。

でも、どういう理由で、EREでは後方参照への対応が外されたんだろう…​ 理由が気になる。

コマンドライン処理(仮)

USP友の会 鳥海 秀一さんによる、 初心者向け のコマンドラインの仕組み・利用方法を学ぶ内容でした。
初心者向け でしたが、少々難しくて、あまり理解できていなかったり…​w
エイリアスやシェル関数は、再帰的に適用されるが各適用は1回のみである事が、非常に興味深かったです。
また、ワイルドカードは、該当するファイルが存在しない場合、単なる文字として扱われる事も、地味ながら重要だったり。
何よりも、危険シェル芸が爆誕する瞬間を目撃できて、良かったです♥

第21回シェル芸勉強会

復習とコードの修正を兼ねて、再回答。

問題文および解答は、以下のURLから。
https://blog.ueda.asia/?p=7608

問1

解答。

pdftohtml bba.pdf -stdout 2>/dev/null \
| # PDFファイルをHTMLへ変換
grep 'name=1' \
| # テキスト部分の抽出
sed -e 's/<[^>]*>//g'
  # HTMLタグの除去

PDFファイルに含まれるテキストを抽出する問題。
昔作ったPDFをCUI上で表示するシェル関数、pdfview()から流用。
PDFの内容をHTMLに変換するpopplerツール、pdftohtmlを使った。

でも、他の方の解答を見ていたら、HTMLではなくテキストに変換するpdftotextを使っていらっしゃったり…​ 知らなかったorz
また、上田会長(@ryuichiueda)によれば、ページャをパイプに繋げると、ページャで表示された内容をそのまま次のコマンドに渡せるとの事。
ちょうど、w3m -dumpのような感じ。便利!

問2

解答。

cat anydata.cp932 \
| # データの出力
nkf -wx \
| # SJISをUTF-8および全角カナを半角カナへ変換
sed -e 's/[0-9]\{10\}/&\n/g' \
| # 年月日で改行
sed -n '1,6p'
  # 余分な空行の削除

テキストを整形する、シンプルな問題。
nkfでSJISをUTF-8に、全角カナを半角カナに変換した後、出力を整形する。

でも、TLを眺めていると、テキストの折り返しはfoldで実現している方が多かった印象。
あと、nkfでUnix向けの出力にするオプションを、--unixではなく-uと間違えていた…​ 恥ずか死。

問3

解答。

seq 1 12 \
| # 月数値(1~12)の生成
LANG=c xargs -I@ cal @ 2016 \
| # calコマンドの実行
awk '/2016/ || NF == 7 && $0=$1' \
| # 月名および日曜日の出力
sed -e '/^Su/d'
  # 曜日名の除去

2016年の日曜日のみを求める問題。
以前書いた、 Project Eulerをシェル芸で解いてみる(Problem 19) #シェル芸からの流用。
calコマンドの出力から、日曜日を無理やり削りだす。
ただ、この問題も、TLを眺めているとdateコマンドで解答されている方が多かった。そちらの解法が一般的なのかな?

問4

解答。

diff -u data newdata \
| # 両データの差分をUnified形式で出力
sed -e '1,3d' \
| # ヘッダの削除
sed -e 's/^./& /' \
| # 挿入・削除記号を独立フィールドに変換
sort -k 2n,2 -k 1,1 \
| # 入力のソート
sed -e '/^+.*\*/{n;d}' -e '/^-.*\*/d' \
| # 差分の適用
awk '{print $2,$3}'
  # 出力の整形

差分を適用する問題。やや複雑(だと思う)。
diffで差分を取り、更にどの行を適用すべきかを判別して、正答を出力する。

この解答は、ちょっと冗長なコードだったかも…​
ちなみに、usp Tukubaiコマンドを利用すると、非常に簡潔なコードになるとの事。

問5

解答。

cat a.bash \
|
nkf -w80 > a.bash.new
cat b.bash \
|
sed -e '$s;.*;ls ~/;' > b.bash.new

スクリプトエラーを修正する問題。
a.bashは、BOM有りなので、正常に実行が出来ない。
なので、nkfを使ってBOM無しに変換してやればOK。

b.bashは、チルダがASCIIのチルダではなく、スモールチルダ(U+02DC)になっている。
なので、ASCIIのチルダ'~'に書き換えてやればOK。

ちなみに、odコマンドを用いれば、見た目では分からない(判別しにくい)内容をチェック可能。

$ od -tx1a a.bash
0000000  ef  bb  bf  23  21  2f  62  69  6e  2f  62  61  73  68  0a  0a
          o   ;   ?   #   !   /   b   i   n   /   b   a   s   h  nl  nl
0000020  65  63  68  6f  20  48  65  6c  6c  0a
          e   c   h   o  sp   H   e   l   l  nl
0000032
$ od -tx1a a.bash.new
0000000  23  21  2f  62  69  6e  2f  62  61  73  68  0a  0a  65  63  68
          #   !   /   b   i   n   /   b   a   s   h  nl  nl   e   c   h
0000020  6f  20  48  65  6c  6c  0a
          o  sp   H   e   l   l  nl
0000027

$ od -tx1a b.bash
0000000  23  21  2f  62  69  6e  2f  62  61  73  68  0a  0a  6c  73  20
          #   !   /   b   i   n   /   b   a   s   h  nl  nl   l   s  sp
0000020  cb  9c  2f  0a
          K  fs   /  nl
0000024
$ od -tx1a b.bash.new
0000000  23  21  2f  62  69  6e  2f  62  61  73  68  0a  0a  6c  73  20
          #   !   /   b   i   n   /   b   a   s   h  nl  nl   l   s  sp
0000020  7e  2f  0a
          ~   /  nl
0000023

問6

解答。

echo 'a+h{5}(ho){10}[0-9]+' \
| # 元の正規表現の出力
sed -e 's;[+}];&\n;g' \
| # 繰返し指定子部分での改行
sed -e 's;\(^.*\)+;\1\1*;' -e 's;{\([0-9]*\)}; \1;' \
| # 繰返し指定子(+)の展開
awk 'NF==1; NF==2{for(i=1;i<=$2;i++){printf("%s",$1)}; print ""}' \
| # 繰返し指定子(回数指定)の展開
tr -d '()' \
| # 包括指定子の除去
xargs \
| # 行列変換
tr -d ' '
  # 出力の整形

拡張正規表現を、基本正規表現(繰返し指定子は'*'以外用いない)に書き直す問題。
これはちょっと難しかった…​
またまた冗長かも。

問7

解答。

cat text \
| # 原文の出力
tr -d '\n' \
| # 改行の削除
nkf -w \
| # UTF-8への変換
sed -e 's/ /\n/g' \
| # 段落開始部分(全角スペース)を改行に変換
LANG=ja_JP.UTF-8 gawk '$0=length($0)'
  # 各行の文字数の出力

文章の各段落の文字数を求める問題。
gawkのlength()なら、UTF-8な文字でも正確に文字数をカウントできる事を利用した。
それ以外は普通。

問8

解答。

cat 1350369599.Vfc03I4682c8M940114.remote \
| # 原文の出力
sed -n '/image/,$p' \
| # 添付ファイル部分の抽出
awk 'NF == 1 && ! /^--/ || /^$/' \
| # 添付ファイルヘッダの除去
sed -e 's/^$/@/' \
| # コマンド部の確保
tr -d '\n' \
| # 改行の削除
sed -e 's/@/\necho /g' \
| # コマンドの生成_1
awk '/^echo / && sub(/$/, " | base64 -d > img"NR".jpg")' \
| # コマンドの生成_2
sh
  # ファイル抽出の実行

メールテキストから添付ファイルを抽出する問題。
単一のファイルなら簡単だけれど、複数のファイルを一撃で抽出するのが難しかった…​
泥臭い処理が多くて、全体的に冗長なコードかも?

雑記

遠隔だけれど、実はシェル芸勉強会に参加するのは初めて。
なので、問題をちゃんと解けるか心配だったけれど、何とか全問時間以内に解くことができた…​!!
でも、泥臭いコードが多かったり…​orz まだまだ修行が足りんぜよ。

そして、今回の勉強会で一番良かった事は、ベテランの方の解答を間近で拝見できた事。
そんなコマンドがあるの!?とか、こんな活用法があるのか!!といった発見が多くて、とても勉強になりました(^O^)
アイデア次第で本当に独創的な解答が出来る点が、やっぱりシェル芸の一番面白い点だと思う。

勉強会を開催してくださった、上田会長をはじめとした日本UNIXユーザ会とUSP友の会の皆様、ありがとうございました!!

Tags: シェル芸 Unix
このエントリーをはてなブックマークに追加