第28回シェル芸勉強会参加報告
2017-04-22(土)に開催された、第28回シェル芸勉強会(正式名称:jus共催 第10回初心者向けなのかと百条委員会化する午前のシェル勉強会/第28回基準値を超えるシェル芸勉強会 - USP友の会)に参加しました。
ただ、会場へは直接伺わなかったため、Youtubeで配信してくださったライブ中継を利用して、遠隔で参加しました。
勉強会は、午前と午後の2部構成でした。
午前は、シェルに関する勉強会で、シグナルについて学ぶ内容でした。
午後は、シェル芸勉強会で、シェル芸力を付けるための問題を解きました。
以下、勉強会の詳細を記します。
実行環境
Arch Linux 4.10.10-1
GNU bash 4.4.12
GNU coreutils 8.26-1
GNU findutils 4.6.0-2
grep (GNU grep) 3.0
sed (GNU sed) 4.4
gawk 4.1.4
シェルに関する勉強会
シグナル
USP友の会 今泉 光之( @bsdhack)さんによる、シグナルについて理解を深める内容でした。
man 7 signal
などを適時参照しつつ、POSIX標準シグナルの各種シグナルハンドラについて、丁寧な解説がおこなわれました。
普段、 kill
コマンド等で頻繁に使うにも関わらず、実はハンドラ毎の違いをよく理解していなかったため、非常に勉強になる内容でした。
また、一部のシグナル名は、通信に電話回線を使用していた際の名残である(SIGHUPは電話が切断された)等、興味深い話も多く聞くことが出来ました。
第28回シェル芸勉強会
問題文および模範解答は、以下のURLから。
https://blog.ueda.asia/?p=9283
参加しなかった方にも、地獄をおすそ分け❤
問1
問1.1
LaTeXソースから、 figure
環境部分を抽出する問題。
初めの問題だけあって、まだ簡単な難易度。sed
の範囲指定アドレスを使って、 figure
環境に該当する部分のみを出力するだけ。
#!/bin/sh
cat contents.tex | # 元ファイルの出力
sed -n '/\\begin{figure}/,/\\end{figure}/p' # figure環境レコードの抽出
以下、コピペ実行用のワンライナーコード。
cat contents.tex | sed -n '/\\begin{figure}/,/\\end{figure}/p'
問1.2
LaTeXソースから、画像ファイル名とパスの組み合わせを列挙する問題。
1問目の副問題なのに、いきなり難易度が上がったような…
LaTeXでは、画像ファイルパスは caption
、ファイル名は caption
マクロで管理している。
なので、上記2つのマクロを含むレコードを抽出し、余分な部分を除去していけばOK。
除去が完了したら、問題指定のフォーマットに沿うように、出力を整形してやる。
#!/bin/sh
cat contents.tex | # 元ファイルの出力
grep -e '\\includegraphics' -e '\\caption' | # includegraphics, captionマクロを含むレコードの抽出
grep -o '{[^{}]*}' | # マクロ内部の抽出
tr -d '{}' | # 波括弧の除去
xargs -n 2 | # 1レコード2フィールドに変換
awk '{print $2,$1}' # 第1・第2レコードの入れ替え
以下、コピペ実行用のワンライナーコード。
cat contents.tex | grep -e '\\includegraphics' -e '\\caption' | grep -o '{[^{}]*}' | tr -d '{}' | xargs -n 2 | awk '{print $2,$1}'
問2
第2章の第1文目を抽出する問題。
この回で一番簡単だった問題。sed
を使って、該当するレコードを元データから少しずつ削り出していく。
#!/bin/sh
cat contents.tex | # 元ファイルの出力
sed '/^$/d' | # 空行の除去
sed '/^%/d' | # コメント行の除去
sed -n '/\\section/{n; p}' | # sectionの次行の抽出
sed -n '2p' # 第2章 第1文の抽出
以下、コピペ実行用のワンライナーコード。
cat contents.tex | sed '/^$/d' | sed '/^%/d' | sed -n '/\\section/{n; p}' | sed -n '2p'
問3
注釈部分のみを抽出する問題。
当日の回答は間違ってた…
ホールドスペースの練習のような問題。
3行目の内容をホールドスペースに入れる&&出力されないように削除して、7行目のパターンスペース処理時に追記するだけ。
#!/bin/sh
cat contents.tex | # 元ファイルの出力
sed 's/。}[、。]/\n&/g' | # footnoteマクロ末端部分に改行を挿入(sedの範囲指定処理のため)
sed -n '/\\footnote/,/。}[、。]/p' | # footnoteマクロ該当レコードの抽出
tr -d '\n' | # 改行の除去
sed 's/\\footnote/\n&/g' | # footnoteマクロ直前に改行を挿入
grep -o '\\footnote{.*。}[、。]' | # footnoteマクロ部分の抽出
sed 's/.$//' # 末尾の句読点を除去
以下、コピペ実行用のワンライナーコード。
cat contents.tex | sed 's/。}[、。]/\n&/g' | sed -n '/\\footnote/,/。}[、。]/p' | tr -d '\n' | sed 's/\\footnote/\n&/g' | grep -o '\\footnote{.*。}[、。]' | sed 's/.$//'
問4
章毎に個別のファイルに分割する問題。
比較的簡単だった問題。awk
でカウンタ変数を使いつつ、リダイレクトでセクションごとにファイル出力していく。
#!/bin/sh
cat contents.tex | # 元ファイルの出力
awk '{if(match($0,/\\section{/)){s++}; print $0 > "contents_"s".tex"}' # セクション毎にファイルに出力
以下、コピペ実行用のワンライナーコード。
cat contents.tex | awk '{if(match($0,/\\section{/)){s++}; print $0 > "contents_"s".tex"}'
問5
ソースに含まれる「○○座標系」という用語を列挙する問題。
GNU grep
に依存しまくりの解答。
ただ、他の方の解答を拝見すると、どうやらPerlの正規表現を使うと、よりスマートに出来るらしい。
#!/bin/sh
cat contents.tex | # 元ファイルの出力
grep -o '[^[:punct:]ぁ-ん]*座標系' | # ○○座標系の抽出
sort | # レコードのソート
uniq # 重複レコードの除去
以下、コピペ実行用のワンライナーコード。
cat contents.tex | grep -o '[^[:punct:]ぁ-ん]*座標系' | sort | uniq
問6
各段落の先頭に全角スペースを挿入する問題。
当日に解答したコードは、段落開始部分以外にも全角スペースを入れてしまっており、微妙に間違ってたorz
まず、候補レコード全てに全角スペースを付加してやり、その後不要なレコードから全角スペースを除去してやると、ええ感じにできた。
#!/bin/sh
cat contents.tex | # 元ファイルの出力
sed -r '/\\(section|sub|begin|end)/!{/^[ \t]{2,}\\/!{/^[^%]/s/^/ /}}' | # section・環境以外のレコード先頭に全角スペースを追加
gawk '/^ *$/{s=0} {if(index($0, " ")){s++}; print (s>1) ? gensub(/ /,"","g",$0) : $0}' # 段落開始以外のレコードから全角スペースを除去
以下、コピペ実行用のワンライナーコード。
cat contents.tex | sed -r '/\\(section|sub|begin|end)/!{/^[ \t]{2,}\\/!{/^[^%]/s/^/ /}}' | gawk '/^ *$/{s=0} {if(index($0, " ")){s++}; print (s>1) ? gensub(/ /,"","g",$0) : $0}'
問7
本文部分の改行を除去する問題。
この問題も、当日のコードは間違っていた。というか、文意を間違えていて、適切に改行を除去していなかった…
単純に初めに改行を全て除去してしまうと、後に復元のしようが無くなってしまう。
そのため、初めに必要な部分に、俺俺改行マーク <:LF:>
を付加しておく。
付加した後は、テキストの加工をおこなっていき、最後に俺俺改行マークを改行に変換し、復元すればOK。
#!/bin/sh
cat contents.tex | # 元ファイルの出力
tac | # 入力を反転出力
sed '/^$/{n; /。$/s/.*/&<:LF:>/}' | # 段落末端に改行マークを付加
tac | # 入力を反転出力
sed '/^%/s/.*/&<:LF:>/' | # コメントレコードに改行マークを付加
sed '/^\\begin/,/^\\end/s/.*/&<:LF:>/' | # LaTeX環境レコードに改行マークを付加
sed '/^\\begin/s/.*/<:LF:>&/' | # LaTeX環境レコード開始前に改行マークを付加
sed '/section{/s/.*/&<:LF:>/' | # sectionマクロレコードに改行マークを付加
sed '/^$/s/.*/&<:LF:>/' | # 空行に改行マークを付加
tr -d '\n' | # 改行の除去
sed 's/<:LF:>/\n/g' # 改行マークを改行に変換
以下、コピペ実行用のワンライナーコード。
cat contents.tex | tac | sed '/^$/{n; /。$/s/.*/&<:LF:>/}' | tac | sed '/^%/s/.*/&<:LF:>/' | sed '/^\\begin/,/^\\end/s/.*/&<:LF:>/' | sed '/^\\begin/s/.*/<:LF:>&/' | sed '/section{/s/.*/&<:LF:>/' | sed '/^$/s/.*/&<:LF:>/' | tr -d '\n' | sed 's/<:LF:>/\n/g'
問8
LaTeXソースから、目次を作成する問題。
最終問題は、それほど難しくは無かった。
目次を作成するために必要な、 \section
, \subsection
, \subsubsection
マクロを含んだレコードを抽出し、
不要な文字を除去する。
その後、 awk
で章・節・小節のカウンタ変数を作り、章節番号と各タイトルを出力してやる。
cat contents.tex | # 元ファイルの出力
grep -E '^\\(|sub|subsub)section{' | # section, subsection, subsubsectionレコードの抽出
sed 's/\\label.*//' | # labelマクロの除去
sed 's/}$//' | # *sectionマクロ終了文字'}'の除去
sed 's/{/<FS>/' | # *sectionマクロ開始文字'{'をフィールドスペースに変換
awk -F'<FS>' '
/\\sec/{s++; ss=0; print s,$2}
/\\subsec/{ss++; sss=0; print s"."ss,$2}
/\\subsub/{sss++; print s"."ss"."sss, $2}
' # 出力の整形
以下、コピペ実行用のワンライナーコード。
cat contents.tex | grep -E '^\\(|sub|subsub)section{' | sed 's/\\label.*//' | sed 's/}$//' | sed 's/{/<FS>/' | awk -F'<FS>' '/\\sec/{s++; ss=0; print s,$2} /\\subsec/{ss++; sss=0; print s"."ss,$2} /\\subsub/{sss++; print s"."ss"."sss, $2}'
参照Webサイト
jus共催 第10回初心者向けなのかと百条委員会化する午前のシェル勉強会/第28回基準値を超えるシェル芸勉強会
→ 勉強会の概要。第28回シェル芸勉強会(午後の部) → 今回の勉強会の動画配信(アーカイブ)。
シグナル - SlideShare
→ 午前勉強会の資料第28回基準値を超えるシェル芸勉強会 - Togetterまとめ
→ 参加者の当日の様子。第28回シェル芸勉強会へ遠隔参加 - 日々之迷歩
→ 大阪・福岡のサテライト会場、主催者さんの記事。第28回基準値を超えるシェル芸勉強会のまとめ - 上田ブログ
→ 勉強会の資料や参加記事等のリンク集。
雑記
…今回の問題、難しすぎィ!!
ここ最近、シェル芸勉強会の問題の難易度が、某バトル漫画並にインフレを起こしている気がする…w
難易度が高いのは、頭をフル回転させられるし、ベテランの方の様々なテクニックも拝見できるので、それはそれで面白いのだけれど…
さすがに高難易度が続くと少し疲れてしまうので、たまには簡単な問題も混ぜてもらえますと、個人的にありがたいですorz
あと、回を重ねるごとに、LTの変態度もアップしている気がする。
今回の勉強会で一番印象に残ったのは、上田会長が、お子さんが宿題に取り組んでいる際、「検算をちゃんとやろう」と指導しているというエピソード。
今回の午後演習でもそうだったけれど、シェル芸で真面目な処理をおこなう際、実は出力結果が間違っていたというケースが、ここ最近多くあったので…
そもそも、とりあえず解答が得られたら、それで満足してしまう僕自身の態度が、あまり宜しくないのだと思うorz
こういった自体を防ぐために、簡単で良いので、シェル芸でも検算が必要だと改めて考えさせられた。
たとえば、シェル芸で検算をやるなら、以下のような操作をしてみると、良いかもしれない。
出力結果に対して、
grep
やgrep -v
を実行し、意図した出力が得られているか、意図しない出力が得られていないかを確認するフィールド区切り形式データの場合、
awk '$0=NF'
や Tukubaiのretu
等で、全レコードでフィールド数が揃っているかどうかを確認するフィールド数1のデータの場合、
sort | uniq -c
を実行してみて、大雑把に集計してみる
これらを実行してみるだけでも、意図した出力がきちんと得られているかどうか、判別できるようになるはず。
幸い、シェル芸で上記の検算をおこなうには、お尻にパイプとコマンドをくっ付けるだけとお手軽なので、積極的にやる癖を付けていきたい。
最後に、勉強会を開催してくださった、上田会長をはじめとした日本UNIXユーザ会とUSP友の会の皆様、いつも本当にありがとうございます!!