日々之迷歩

世の中わからんことだらけ

ITが複雑で難しくなっていく様に翻弄される日々です。微力ながら共著させていただいた「シェル・ワンライナー160本ノック」をよろしくお願い申し上げます。

第28回シェル芸勉強会へ遠隔参加

期間限定ではあるが忙しさがピークに達し自分を見失いかけていたため、福岡サテライト会場開催の決断と通知が遅くなってしまい申し訳ありませんでした。今回も株式会社エスコ様(旧ベータソフト様)の会議室を利用させていただきました。 問題出題と解説の上田さん、ありがとうございました。参加者の皆様もお疲れ様でした。

会場の担当をしていただいている方は当日業務だったのだが、会場の準備や後片付け、鍵の開け閉めをしていただきました。 いつもありがとうございます。今回は6人の参加者が集いました。

勉強会の情報

勉強会主催者の上田さんが公開されているページをご覧ください。 b.ueda.tech

午前の部

今泉さんによるシグナルの解説でした。福岡サテライトでは、私なりに噛み砕いてシグナルの話をさせていただきました。

  • プロセスはOSがPIDで管理
  • シグナルでプロセス制御
    • プロセス終了
    • 一時停止
    • コアダンプ
    • 強制終了
    • パイプクローズ
    • ユーザー定義シグナル
  • killコマンドで指定したPIDにシグナル通知
  • Ctrl-CやCtrl-Zなどのキー操作でシグナル通知
  • kill -lでシグナル一覧
  • SIGHUPで設定ファイル再読み込みや再起動(sshdやApache HTTPd Serverなど)
  • seq 100000 | head でSIGPIPEの事例
  • bashのPIPESTATUS

休憩している時に、CSVデータを行列置換とかがやりたいけど何か方法は無いか?という話題が上がりました。 awkなどで頑張る方法もあるが、rsコマンドの-Tオプションで行列入替えが出来る話をしました。

$ seq 10 | xargs -n 2
1 2
3 4
5 6
7 8
9 10

$ seq 10 | xargs -n 2 | rs -T
1   3   5   7   9
2   4   6   8   10

ちなみに同様な処理をするのに、Tukubaiのtateyokoというコマンドがあります。

Tukubaiオンラインコマンドマニュアル: tateyoko(1)

質問された方に話を詳しく聞いてみたところ、RDBに入れてJOINしたりするためのCSVデータを行列置換したいということでした。 最近テキストファイルをキーで連結する処理をやった事例があり、TLで話してみようかと準備をしていたので、午前中ではあるが資料の話をすることにしました。

speakerdeck.com

昼食

お昼は会場近くの餃子の王将でした。定番になりつつあります。 Software Designの2017年1月号を購入してシェル芸特集を読んでやってみたという方がいらっしゃいました。ありがたいですね。

午後の部

いよいよ実践で悶える時間が始まりました。 今回もsedがメインのようですが、awkの力も借りつつLaTeXの文章を弄くり回す地獄を味わう問題のようです。

福岡サテライトで考えて解説した内容は下記の通りです。今回は気分を変えてUbuntuで解いてみました。

Q1.1

出題者上田さんの解答とほぼ同じでした。

Q1.2

出題者上田さんの解答例を参考にさせていただいて解説しました。 まずはsedgrep-Bオプションで必要になりそうな行を抜き出します。

$ cat contents.tex | sed -n '/\\begin{figure}/,/\\end{figure}/p' | grep -B1 caption
        \includegraphics[width=0.5\linewidth]{./figs/coordinate.eps}
        \caption{世界座標系とロボットの姿勢}
--
        \includegraphics[width=0.5\linewidth]{./figs/observation.eps}
        \caption{計測値}
--
        \includegraphics[width=0.5\linewidth]{./figs/two_poses.eps}
        \caption{ランドマークの計測値から2点の相対姿勢を求める}
--
        \includegraphics[width=0.8\linewidth]{./figs/observation_noise.eps}
        \caption{ランドマークの計測値の不確かさを表す共分散行列}

次に{}の前後を消し去ります。

$ cat contents.tex | sed -n '/\\begin{figure}/,/\\end{figure}/p' | grep -B1 caption | sed 's/^.*{//' | sed 's/}.*$//'
./figs/coordinate.eps
世界座標系とロボットの姿勢
--
./figs/observation.eps
計測値
--
./figs/two_poses.eps
ランドマークの計測値から2点の相対姿勢を求める
--
./figs/observation_noise.eps
ランドマークの計測値の不確かさを表す共分散行列

後は並べ替えして整理すれば完成です。

$ cat contents.tex | sed -n '/\\begin{figure}/,/\\end{figure}/p' | grep -B1 caption | sed 's/^.*{//' | sed 's/}.*$//' | xargs -n 3 | awk '{print $2,$1}'
世界座標系とロボットの姿勢 ./figs/coordinate.eps
計測値 ./figs/observation.eps
ランドマークの計測値から2点の相対姿勢を求める ./figs/two_poses.eps
ランドマークの計測値の不確かさを表す共分散行列 ./figs/observation_noise.eps

Q2

出題者上田さんの解答例を参考にさせていただきました。 内容が複雑で福岡サテライトでは追いつく余裕が無かったため、段落番号を付けて2章だけ抜き出す処理を理解することにしました。

$ cat contents.tex | awk '/\\section/{a+=1}{print a,$0}' | grep '^2'
2 \section{問題}
2 
2 %対向二輪型(その場で回転できるロボット)で、
2 平面上を移動し、向きを持ち、カメラでランドマーク観測ができるロボットで
...
2 任意のロボットの軌跡$\V{x}_{0:T}$がどれだけ$Z_{0:T}$を説明するかを
2 評価関数として定式化し、この評価関数を用いた最適化問題として定義する。
2 この評価関数については、次章の実装の中で説明する。

Q3

詳しく追う余裕がなかったため、一旦一行にしてしまってからキーワードの前後に改行を入れると扱いやすくなる、 というやり方を解説するに留めておきました。

$ cat contents.tex | tr -d '\n' | sed 's/\\footnote{/\n&\n/g' 

Q4

キーを元にして、awkでデータをファイル別に分割する事例ですね。 参加者の方々はやり方をご存知ではないようでしたので、下記の例を解説しました。

数値を10で割った余り毎にデータを分割する例です。

$ seq 100 | awk '{a=$1%10; print $1 > "file"a}'
$ head -n 2 file*
==> file0 <==
10
20

==> file1 <==
1
11

...

==> file8 <==
8
18

==> file9 <==
9
19

Apacheログファイルを接続元ホスト毎に分割する例です。

$ head -n 3 access_log
192.168.0.1 - - [01/Jul/1995:00:00:01 +0900] "GET /history/apollo/ HTTP/1.0" 200 6245
192.168.0.2 - - [01/Jul/1995:00:00:06 +0900] "GET /shuttle/countdown/ HTTP/1.0" 200 3985
192.168.0.3 - - [01/Jul/1995:00:00:09 +0900] "GET /shuttle/missions/sts-73/mission-sts-73.html HTTP/1.0" 200 4085

$ mkdir sources

$ cat access_log | awk '{print $0 > "sources/"$1}'

$ head -n 1 sources/*
==> sources/192.168.0.1 <==
192.168.0.1 - - [01/Jul/1995:00:00:01 +0900] "GET /history/apollo/ HTTP/1.0" 200 6245

==> sources/192.168.0.2 <==
192.168.0.2 - - [01/Jul/1995:00:00:06 +0900] "GET /shuttle/countdown/ HTTP/1.0" 200 3985
...

Q5

出題者上田さんの解説を参考にさせていただき、「わかち書き」が出来るmecabについて説明しました。

Q6

出題者上田さんの解説を参考にさせていただき、私の方からも解説しました。

Q7

余裕が無かったのでパスしました。

Q8

出題者上田さんの解説を参考にさせていただきましたが、思ったような出力にならない不完全解答になってしまいました。

$ cat contents.tex | grep 'section' | grep -v '^%' | sed 's/\\label{.*}$//' | sed 's/{/ /' | sed 's/}$/ /' | awk '/\\sec/{s+=1;ss=0}/\\subsection/{ss+=1;sss=0}/\\subsubsection/{sss+=1}{print s"."ss"."sss,$0}'
1.0. \section はじめに 
2.0. \section 問題 
2.1.0 \subsection ロボットの姿勢と座標系 
2.2.0 \subsection 観測 
2.2.1 \subsubsection ランドマークの識別 
2.2.2 \subsubsection ランドマークの姿勢計測 
2.2.3 \subsubsection 計測値の記録 
2.2.4 \subsubsection 計測値の誤差 
2.3.0 \subsection 完全SLAM問題 
3.0.0 \section graph-based SLAMの実装例 
3.1.0 \subsection グラフのエッジを作る 
3.1.1 \subsubsection $\V{\mu}_{c,t,t'}, \V{e}_{c,t,t'}$の計算 
3.1.2 \subsubsection $\Sigma_{c,t,t'}, \Omega_{c,t,t'}$の計算 
3.2.0 \subsection 最適化問題を作る 
3.2.1 \subsubsection マハラノビス距離 
3.2.2 \subsubsection 最適化する式 
3.3.0 \subsection $\V{e}_{c,t,t'}$の勾配を求める 
3.4.0 \subsection 問題を解く 

午後の部終了後

イントロとして準備しておいた資料の話をしました。

speakerdeck.com

九九の答えが36種類

数学的な証明については、大阪サテライト担当の@kunst1080さんが頑張られているのでご覧ください。

www.kunst1080.net

九九の答えが36種類であることについて、Ruby芸でmapとreduceを使って確認してみました。 考え方は@ebanさんの下記のシェル芸とほぼ同じです。

Rubyのrepeated_combinationと、mapとreduceでやってみました。

$ irb
irb(main):001:0> (1..9).to_a.repeated_combination(2).map{|a|a.reduce(:*)}.uniq.size
=> 36

そもそもreduce不要でした。まあ3個以上の掛け算だったらreduce使えばええですね。

irb(main):001:0> (1..9).to_a.repeated_combination(2).map{|a,b|a*b}.uniq.size
=> 36

次に計算結果を素因数分解して、掛け算のパターンを数えてみます。シェル芸もRuby芸も考え方は同じです。

まずはシェル芸の場合です。1列目をTukubaiのdelfコマンドを使っています。awkなどで消しても良いでしょう。

$ echo {1..9}\*{1..9}\; | bc | factor | delf 1 | sort | uniq

2
2 2
2 2 2
2 2 2 2
2 2 2 2 2
2 2 2 2 2 2
2 2 2 2 3
2 2 2 3
2 2 2 3 3
2 2 2 5
2 2 2 7
2 2 3
2 2 3 3
2 2 5
2 2 7
2 3
2 3 3
2 3 3 3
2 3 5
2 3 7
2 5
2 7
3
3 3
3 3 3
3 3 3 3
3 3 5
3 3 7
3 5
3 7
5
5 5
5 7
7
7 7

$ echo {1..9}\*{1..9}\; | bc | factor | delf 1 | sort | uniq | wc -l
      36

次にRuby芸です。REPL環境のirbを利用しました。

$ irb -r 'prime'
irb(main):001:0> (1..9).to_a.repeated_combination(2).map{|a,b|a*b}.map{|a|Prime.prime_division(a).map{|p,n|[p]*n}.flatten}.uniq
=> [[], [2], [3], [2, 2], [5], [2, 3], [7], [2, 2, 2], [3, 3], [2, 5], [2, 2, 3], [2, 7], [2, 2, 2, 2], [2, 3, 3], [3, 5], [3, 7], [2, 2, 2, 3], [3, 3, 3], [2, 2, 5], [2, 2, 7], [2, 2, 2, 2, 2], [2, 2, 3, 3], [5, 5], [2, 3, 5], [5, 7], [2, 2, 2, 5], [3, 3, 5], [2, 3, 7], [2, 2, 2, 2, 3], [2, 3, 3, 3], [7, 7], [2, 2, 2, 7], [3, 3, 7], [2, 2, 2, 2, 2, 2], [2, 2, 2, 3, 3], [3, 3, 3, 3]]
irb(main):002:0> 
(1..9).to_a.repeated_combination(2).map{|a,b|a*b}.map{|a|Prime.prime_division(a).map{|p,n|[p]*n}.flatten}.uniq.size
=> 36

ASCIIコード問題

競プロな方のTwitter LTで、ASCIIコードの大小を確認する話題がありました。 @ebanさんからLANG=Cな条件だとsortコマンドはASCIIコード順に並べ替えしてくれるというアドバイスをいただきました。

それからASCIIコード順に文字列を列挙する方法として、下記のような例を話しました。

AWKを使う例です。

$ seq 33 125 | awk '{printf "%d %c\n",$1,$1}'
33 !
34 "
35 #
...
123 {
124 |
125 }

iprintを使う例です。(macOSやUbuntuならパッケージでインストール可)

$ seq 33 125 | xargs i
33 0x21 041 0b100001 '!'
34 0x22 042 0b100010 '"'
35 0x23 043 0b100011 '#'
...
123 0x7B 0173 0b1111011 '{'
124 0x7C 0174 0b1111100 '|'
125 0x7D 0175 0b1111101 '}'

man asciiでasciiコードのマニュアルを見てみます。

ASCII(7)             BSD Miscellaneous Information Manual             ASCII(7)

NAME
     ascii -- octal, hexadecimal and decimal ASCII character sets

DESCRIPTION
     The octal set:

     000 nul  001 soh  002 stx  003 etx  004 eot  005 enq  006 ack  007 bel
     010 bs   011 ht   012 nl   013 vt   014 np   015 cr   016 so   017 si
     020 dle  021 dc1  022 dc2  023 dc3  024 dc4  025 nak  026 syn  027 etb
     030 can  031 em   032 sub  033 esc  034 fs   035 gs   036 rs   037 us
     040 sp   041  !   042  "   043  #   044  $   045  %   046  &   047  '
     050  (   051  )   052  *   053  +   054  ,   055  -   056  .   057  /
     060  0   061  1   062  2   063  3   064  4   065  5   066  6   067  7
     070  8   071  9   072  :   073  ;   074  <   075  =   076  >   077  ?
     100  @   101  A   102  B   103  C   104  D   105  E   106  F   107  G
     110  H   111  I   112  J   113  K   114  L   115  M   116  N   117  O
     120  P   121  Q   122  R   123  S   124  T   125  U   126  V   127  W
     130  X   131  Y   132  Z   133  [   134  \   135  ]   136  ^   137  _
     140  `   141  a   142  b   143  c   144  d   145  e   146  f   147  g
     150  h   151  i   152  j   153  k   154  l   155  m   156  n   157  o
     160  p   161  q   162  r   163  s   164  t   165  u   166  v   167  w
     170  x   171  y   172  z   173  {   174  |   175  }   176  ~   177 del

実は先日の夜の飲み会でかなりの酔っぱっており、若干体調が不良だったのでした。 その飲み会の時に誘った方も参加していただきました。 UNIXはあまり触ったことがないということで、出来るだけサポートはさせていただいたつもりだがどうだったでしょうか?