「第32回シェル芸勉強会 大阪サテライト」の記録

イベント関連ページ

各会場の告知

レポート・まとめ

午前の部

講師

  • 鳥海秀一(@hid_tori)さん/USP友の会

動画

資料

感想など

この勉強会の数日前から、オライリーのフクロウ本で予習らしきものをしていたものの、やはり難しい!!

マッチ時パターン展開やアトミックグループは、使いこなせればかなり便利そうなのですが、普段キャプチャ付き括弧程度しか使(?:わ|え)ない自分のような一般人にとっては、これらの高度な機能も宝の持ち腐れになってしまうのが悲しいところです。

……そもそもスライドに挙げられている変態Perlプログラマの解答例が何かの怪しい呪文にしか見えません。

午後の部

動画

問題・模範解答

解答

A1: 抜けた数字の行を飛ばして出力

当日は次のような解答

$ echo 14679 | grep -o . | awk '{n[NR]=$1}END{for(k in n){printf n[k]" ";for(i=n
[k];i<n[k+1];i++)printf i"\n"}}' | awk  '{if(NR!=9){print $2?$1:" "}else{print $
1}}'
1


4

6
7

9

をツイートしましたが、今ひとつ美しくないということで

$ echo 14679 | grep -o . | diff -y - <(seq 9) | cut -f1
1


4

6
7

9

という別解を思い付きました。

これの手順は、

  1. grep -o .で1行1文字にする
  2. 1.の出力とseq 9の出力を-yオプションを付けたdiffに与え、side-by-side形式(man diff参照)で出力
  3. cut -f1で第1フィールドのみ出力

となっています。

A2: A1の空白行にa,b,c,…を埋める

$ echo 14679 | grep -o . | diff -y - <(seq 9) | cut -f1 | perl -nlE 'BEGIN{$c=97}{say $_?$_:chr($c++)}'
1
a
b
4
c
6
7
d
9

当日は解答できませんでしたが、A1の別解の発展形として解くことができました。

最後のperl -nlE 'BEGIN{$c=97}{say $_?$_:chr($c++)}'で、空白行に対して小文字アルファベットを挿入しています。

なお、Perlの組み込み関数chr()については、http://perldoc.jp/func/chrを参照して下さい。

A3: TCPのポート番号が素数のサービス一覧を作成

$ cat /etc/services | sed '/[0-9]\+\/tcp/!d' | sed 's|/.*||' | awk '{printf $1" ";system("factor "$2)}' | awk 'NF==3{print $3"\t"$1}' | sort -n
7       echo
11      systat
13      daytime
17      qotd
19      chargen
(...略...)
8081    tproxy
9103    bacula-sd
13721   bpdbm
20011   isdnlog
22273   wnn6

当日の解答が間違っていたので一からやり直しました。

手順としては、

  1. sedで「ポート番号/tcp」が含まれていない行を削除
  2. 2回目のsedでサービス名とポート番号以外を削除
  3. awksystem()関数で第2フィールドの値に対してfactorを実行
  4. 第2フィールドの値が素数の場合、行のフィールド数は3になるため、それに当てはまる行であれば「ポート番号・タブ・サービス名」の順に出力
  5. sort -nで数値順にソート

となっています。

A4: 並び替え

$ cat nums.txt
136
725
948
$ cat nums.txt | grep -o . | xargs | awk '{print $7$4$8$1$5$9$2$6$3}' | grep -o .
9
7
4
1
2
8
3
5
6

まだまだシェル芸力が低いので、

  1. grep -o .で1行1文字にする
  2. xargsで各数字を空白区切りで1行に並べる
  3. awkで並び替え
  4. grep -o .で再び1行1文字にする

という方法しか思い付きませんでした。

A5: ウムラウトを含む単語のみ抽出

$ cat umlaut.txt
wäschst wash 山田x Schrödinger
y上田 Ö アイウエオ unko
Übel Ärztin hoge カキクケコ
$ cat umlaut.txt | tr ' ' '\n' | perl -nlE 'say if /\xc3[\x84-\xb6]/'
wäschst
Schrödinger
Ö
Übel
Ärztin

ウムラウト記号が付いた文字のコードを調べると

$ cat umlaut.txt | grep -o . | LANG=C sed '/^..$/!d' | xxd -p | sed 's/0a/\n/g' | sort

c384
c396
c39c
c3a4
c3b6

のようになるので、

  1. trで1行につき単語1ヶにする
  2. perlで文字コードがc384からc3b6の範囲にある文字を含む行のみを出力

すればOKです。

A6: ツイッターアカウントの監視

$ getTweetNum(){ w3m -dump https://twitter.com/mikkun_jp 2>/dev/null | grep '^  • ツイートツイート、現在のページ。 [0-9,]\+$'; }; prev=$(getTweetNum); while :; do sleep 10; curr=$(getTweetNum); if [ "$prev" != "$curr" ]; then echo 'んほぉ!'; prev=$curr; fi; done
んほぉ!

関数getTweetNum()で、ツイッターの特定のアカウントのWebページを取得し、ツイート数が記載されている行を抽出しています。

そして、この関数の中身を実行すると、次のような出力が得られます。

$ w3m -dump https://twitter.com/mikkun_jp 2>/dev/null | grep '^  • ツイートツイート、現在のページ。 [0-9,]\+$'
  • ツイートツイート、現在のページ。 2,063

whileループで10秒ごとにgetTweetNum()を実行し、以前の実行結果と異なっていればメッセージを出力するようにします。

A7: ノイズ除去

$ cat image.txt
00010000000000001000000
00000000111111111110000
01111110011111111110001
00011110001111111111000
10011110001111111111000
00000001000000001000000
$ cat image.txt | sed 's/./ &/g' | awk '{for(i=1;i<=NF;i++){n[NR][i]=$i}}END{for(i=1;i<=NR;i++){for(j=1;j<=NF;j++){printf n[i-1][j]*n[i+1][j]*n[i][j-1]*n[i][j+1]==0?0:1;printf " "}printf "\n"}}' | awk '{for(i=1;i<=NF;i++){n[NR][i]=$i}}END{for(i=1;i<=NR;i++){for(j=1;j<=NF;j++){printf n[i-1][j]+n[i+1][j]+n[i][j-1]+n[i][j+1]>=1?1:0}printf "\n"}}'
00000000000000001000000
00000000001111111100000
00001100011111111110000
00011110001111111111000
00001100000111111110000
00000000000000001000000

問題文にある2つの条件

  1. ある数字について、上下左右の数字どれか1つに0が含まれる場合は0、そうでなければ1にする
  2. 次に、上下左右の数字どれか1つに1が含まれる場合は1、そうでなければ0にする

に従って、二次元配列が使えるプログラミング言語(この解答ではgawk)で解いてみました。

A8: 漢字やカタカナが行頭に来るように改行

$ cat japanese.txt
ん僕らは既に死んでいる
死んでいるからシェル芸だ。
$ perl -C -nlE 's/(\p{Hiragana})([\p{Han}\p{Katakana}])/$1\n$2/g;say' japanese.txt
ん
僕らは
既に
死んでいる
死んでいるから
シェル芸だ。

Perlの正規表現では、「\p{…}」(否定の場合は「\P{…}」)といった、Unicodeのスクリプト(書記系)の名前による検索を利用できますので、任意のひらがな1文字と任意の漢字およびカタカナの間に改行が入るように置換を行い、出力しています。

LT大会

動画

diffとdiffのdiffを見る

発表者: くんすと(@kunst1080)さん

2つのdiffのdiffを見るためのCLIツール、interdiffについてのLTです。

動画でも挙げられていますが、このinterdiff、元々は2つのパッチ間の差異を表示するツールということでGit rebaseの確認に便利そうです。

潜入工作任務用シェル芸

発表者: たいちょー(@xztaityozx_001)さん

「キーロガーに気づかれない危険シェル芸とは?」という、いささか不穏な空気が感じられますが、bashの組み込みコマンドreadlinebindの使い方を学ぼうという至って真っ当なLTです。

AMAZON DASH HACK

発表者: MSR(@msr386)さん

dasherを利用して、AMAZON DASHをシェルコマンド実行用のボタンに改造してみようというLTです。

sed で始める簡単ループエンジン

発表者: いるやん(@Iruyan_Zak)さん

sedを使ったシェル芸の実演です。🔃🍣楽しい!!🍣🔃

感想など

前回に引き続きパズル的要素の強い問題が多く出題されましたが、中にはQ6のWebページの監視やQ7のノイズ除去のような実用的?な問題もあり、パズルを解いていく楽しさとはまた一味違う楽しさもありました。

近頃は公私ともに多忙(年末進行+介護)で、文章を書くのも遅くなりがちですが、継続は力なりということでゆるゆるとマイペースでやっていこうと思っています。

最後に、

に改めてお礼を申し上げます。ありがとうございました。

……それでは皆さん、良い年末年始を。

Written on December 27, 2017