Slide 1

Slide 1 text

シェル芸⼈に必要なのは 「マスキングテープ」だったのでは ぐれさん

Slide 2

Slide 2 text

⾃⼰紹介 (1/3) ぐれさん (Twitter: @grethlen) シェル芸キュアエンジニア 危険シェル芸で国外追放された(⼤嘘) 最近良いことがあった

Slide 3

Slide 3 text

⾃⼰紹介 (2/3) 朗報 ※ 出典: 2020/06/21 放映「ヒーリングっど♡プリキュア」より

Slide 4

Slide 4 text

⾃⼰紹介 (3/3) Software Design シェル芸連載 「シェル芸⼈からの挑戦状」の連載 参加してました 2020 年 4 ⽉号にて⼀旦終了 ※ STOPがかかったわけ ではないではない 150 問以上 ご愛読ありがとうございました!

Slide 5

Slide 5 text

シェル芸漬けの2年半 数多くの問題を解いたり考えたり 締切に追われて苦しみながら シェル芸⼈たちと頭をひねった2年半.. => ある法則に気づいた

Slide 6

Slide 6 text

こういう状況ありませんか 仕事や研究などで遭遇した困りごとをシェル芸で解決しようとするとき... ※ 出典: NHK健康チャンネル「発達障害って何だろう」より

Slide 7

Slide 7 text

「できるけど疲れる」問題1 第16回春だからログ解析するぞシェル芸勉強会 https://b.ueda.tech/?post=05644 準備1 ⼀部抜粋 ⽇付と時刻を次のように正規化しておきましょう。 ### 修正前### ueda@tencore:~/tmp/nasa$ zcat access_log.nasa.gz | head -n 1 199.72.81.55 - - [01/Jul/1995:00:00:01 -0400] "GET /history/apollo/ HTTP/1.0" 200 6245 ### 修正後### ueda@tencore:~/tmp/nasa$ cat access_log | head -n 1 19950701 000001 199.72.81.55 - - [01/Jul/1995:00:00:01 -0400] "GET /history/apollo/ HTTP/1.0" 200 6245 ※ 360 MB のファイルです

Slide 8

Slide 8 text

問題1 解答例 模範解答 zcat access_log.nasa.gz | awk '{print $4,$0}' | sed 's/^\[//' | awk '{gsub(/[\/:]/," ",$1);print}' | awk '{$2=$2=="Jul"?"07":$2;$2=$2=="Aug"?"08":$2;print}' | sed 's;^\(..\) \(..\) \(....\) \(..\) \(..\) \(..\);\3\2\1 \4\5\6;' > access_log => AWK では⽉の名前(Jan, Feb...) => 数字の変換ができない date で⽇付の変換はできるのだが.. $ echo '01/Jul/1995:00:00:01 -0400' | sed 's|/| |g;s/:/ /' | date -f- +'%Y%m%d %H%M%S' 19950701 050001 => 事実上使えない(後述)

Slide 9

Slide 9 text

問題1 解答例(参加者抜粋) while でやった⼈ zcat access_log.nasa.gz | while read LINE; do echo ${LINE} | sed 's/[^[]*\[//; s/].*//; s/-[0-9]\{4\}//; s/Jul/07/; s/Aug/08/;' | tr '/:' ' ' | awk '{printf($3$2$1" "$4$5$6" ")}'; echo $LINE; done => 解けるが、かなり時間かかる Ruby でやった⼈ zcat access_log.nasa.gz| ruby -EASCII-8BIT -rdate -ane '$F.unshift Date.parse($F[3]).strftime("%Y%m%d %H%M%S");puts $F*" "' => 解ける なお、ぐれさんは while + date で⾒事にマシンがホッカイロ(⽩⽬)

Slide 10

Slide 10 text

「できるけど疲れる」問題2 ぐれさんが実際に遭遇した問題イメージ(※ 脚⾊済み) 500 MB の CSV ファイルの4列⽬に名前がある これの姓と名の最初の⽂字を⼤⽂字にしたい 1,aaa,Hoge team,yamada taro,2001 2,bbb,Super fuga team,ueda ryuchan,1998 3,ccc,N/A,toilet hanako,1998 ... ↓ 1,aaa,Hoge team,Yamada Taro,2001 2,bbb,Super fuga team,Ueda Ryuchan,1998 3,ccc,N/A,Toilet Hanako,1998 ...

Slide 11

Slide 11 text

問題2 解答例 名前単体の変換は次のでできる!でも sed はCSV読めないし..せや! $ echo yamada taro | sed 's/\b./\U&/g' Yamada Taro 遅くて使い物にならない(涙) $ cat people.csv | while IFS=, read c1 c2 c3 c4 c5 ;do echo "$c1,$c2,$c3,$(echo "$c4" | sed 's/\b./\U&/g'),$c5" ;done $ cat people.csv | perl -F, -anle '{chomp($F[3]=`echo $F[3] | sed "s/\\b./\\U&/g"`); $,=","; print @F}' 結局 Perl でゴリ押しが現実的なソリューション.. cat people.csv | perl -F, -anle '{ $F[3] =~ s/(\b.)/\U$1/g; $,= ",";print @F}'

Slide 12

Slide 12 text

これらの問題の共通点はなんだろう? 元々の⼊⼒を残したまま⼀部分を書き換えている 処理対象が⼤きい データの加⼯⾃体は古典的なコマンドでできる でも使えないので、結局スクリプトでゴリ押しが現実解 => こんなの絶対おかしいよ!

Slide 13

Slide 13 text

「できるけど疲れる」原因は何か?(1) シェル芸の⾟い所(1): ⼤半のコマンドが「処理したくないデータの無視」ができない date , base64 , factor , iconv , nkf , grep , fold , cut , ...

Slide 14

Slide 14 text

「できるけど疲れる」原因は何か?(2) シェル芸の⾟い所(2): 問題が複雑になるとスクリプティング地獄 「処理したくないデータの無視」ができるコマンドもあるが... awk , sed , perl , ruby , ... 「処理対象の選別」と「データの加⼯」がセット ⽴派なプログラミング⾔語 => ⾔語の制約に縛られる ⾔語の仕様や技法・ライブラリに明るい必要がある => いざというときサクッとできない

Slide 15

Slide 15 text

補⾜:「処理対象の選別」と「データの加⼯」がセット GNU sed ... | sed '/AAA/,/BBB/{ s/hoge/fuga/g }' GNU Awk ... | awk '{gsub("A","B",$1);gsub("B","C",$3);print}' Perl ... | perl -nle '/^(......)(...)/; $a=$1;$b=$2; $b =~ s/./@/g; print "$a$b"'

Slide 16

Slide 16 text

「できるけど疲れる」原因は何か?(3) シェル芸の⾟い所(3): スクリプティング地獄を解消しようとするとfork地獄に陥る Bash の while ⽂ sed、AWK等の内部から別のコマンドを外部コマンドとして呼び出す ⼤量のデータ処理には向かない

Slide 17

Slide 17 text

シェル芸⼈のジレンマ スクリプティング地獄 ↔ fork地獄 とてもつらい

Slide 18

Slide 18 text

シェルとコマンドのアプローチを振り返る パイプ コマンド同⼠をべったりとくっつける糊 どこを処理するかはコマンドにおまかせ UNIX のコマンド 標準⼊⼒を「丸ごと」受け取って処理するコマンドが⼤半 糊につける「マスキングテープ」があればどうだろう? 「べったり」ではなく「⼀部分のみ」 残りの部分は「そのまま」 世の中の問題はシンプルになるのではないか

Slide 19

Slide 19 text

teip コマンド シェル環境で使えるマスキングテープ [URL] https://github.com/greymd/teip macOS brew install greymd/tools/teip DEB file git.io/teip-1.2.0.x86_64.deb RPM file git.io/teip-1.2.0.x86_64.rpm Cargo(要 libclang ) cargo install teip

Slide 20

Slide 20 text

teip はなにができるか teip < フィルタリングルール> -- < コマンド> < フィルタリングルール> : 標準⼊⼒のどの部分を < コマンド> に渡すか設定 < コマンド> : 標準⼊⼒まるごとは受け取る必要はない < フィルタリングルール> にマッチしなかった個所 => そのまま出⼒ < フィルタリングルール> にマッチした個所 場所はそのままだが < コマンド> の結果と置換される

Slide 21

Slide 21 text

teip の基本的な使い⽅ ※ デモ ※ README の Getting Started の内容を紹介

Slide 22

Slide 22 text

teip で先程の問題を解く ※ デモ 問題1 $ zcat access_log.nasa.gz | awk '{print $4,$0}' | sed 's/^\[//' | teip -f 1 sh -c "sed 's|/| |g;s/:/ /' | date -f- +'%Y%m%d %H%M%S'" 問題2 $ cat people.csv | teip -d, -f4 sed 's/\b./\U&/g'

Slide 23

Slide 23 text

こういう問題も解ける(時間があればデモ) 第30回危念シェル芸勉強会 https://b.ueda.tech/?post=10134 Q2 ⼀部抜粋 リンクが相対パスになっているものについては頭に/files/をつけて、/から始まっているも のとhttpやhttpsから始まっているものはそのままにしてください。 (.. 略..)
  • ほげ
  • 
怪しい
  • クソブログ ふげ
  • これはそのまま
  • 更新してない
  • (.. 略..)

    Slide 24

    Slide 24 text

    解答例 sed が同じ⾏を書き換えてしまうのが⾟い $ cat url.html | sed -r 's;(img src="|a href=");&/files/;g' | sed -r 's;(href="|src=")/files//;\\1/;' | sed -r 's;(href="|src=")/files/(https://|http://);\\1\\2;g' | sed 's;/./;/;g' 他の参加者の解答 perl -ple 's@[fc]="(?!(?:https?://|/))(?:./)?(.*?)"@="/files/$1"@g' url.html sed 's,\([fc]="\)\(\./\)\?\([^/][^":]*\)",\1/files/\3",' url.html sed -r -e 's@((href|src)=)([^/])@\1/files/\2@g' -e 's@/files/http@http@g' url.html

    Slide 25

    Slide 25 text

    解答例(teip) 短く解こうと思えばできる $ cat url.html | teip -Gog '[fc]="(?!(https?|/))\K.*?(?=")' -- sed 's|[./]*|/files/|' => ⻤⾞正規表現が使える( -G ) テープの重ねがけ $ cat url.html | teip -og '(a href|img src)=".*?"' \ -- teip -og '".*"' \ -- teip -og '[^"]+' \ -- teip -vg http \ -- teip -vg '^/' \ -- sed 's|[./]*|/files/|' => 正規表現の AND 条件(!)... 表現⼒は超強⼒

    Slide 26

    Slide 26 text

    teip のパフォーマンス 細かくベンチマークをとりながら Rust で開発 実⾏対象のコマンドは使い回す => fork しないので早い(※ ) 実⾏コマンドの標準⼊出⼒をプロキシ 複数プロセス並列実⾏ ⼀時ファイルなし I/O は極⼒バッファリング => 無駄なシステムコールを削る ※ 都度forkする動作も選択可能

    Slide 27

    Slide 27 text

    なぜ Rust か 速度・並⾏性に定評があるコンパイル型⾔語 Rust 製 Coreutils(uutils/coreutils)あり 性能をベンチマーク => 本家に負けず劣らず => ヒーリングっどきた Cの資産の取り回しも悪くない 例: ⻤⾞正規表現 Cの共有ライブラリも⼀緒にビルド

    Slide 28

    Slide 28 text

    teip vs GNU grep 数百100MBのファイルの⾏頭から30⽂字に 拡張正規表現で⾊をつける謎の速さ対決 ※ ページキャッシュ消すのを忘れずに $ time grep --color=always -E '^.{30}' < test_secure | pv > /dev/null $ time teip -og '^.{30}' < test_secure | pv > /dev/null => デモ

    Slide 29

    Slide 29 text

    注意: teip単体で⼗分な⽔準のI/O速度を出せる点のアピール以上の意図は無い ユースケースが異なるものを⽐較してもあまり意味はない teipはまだまだ⽣まれたばかりのソフト いわゆる「第⼀のシステム」※ 成熟したソフトより速度が早い状況は「あるある」

    Slide 30

    Slide 30 text

    ※ 芳尾 桂 (翻訳) Mike Gancarz (著). UNIX という考え⽅― その設計思想と哲学. オーム社, 2001.

    Slide 31

    Slide 31 text

    ベンチマーク (1) なので、こんなベンチマークはどうでしょう? GNU sed vs GNU sed (+ teip) $ sed -r 's/< 正規表現>/.../g' < ファイル $ teip -og '< 正規表現>' -- sed -r 's/< 正規表現>/.../g' < ファイル GNU Awk vs GNU Awk (+ teip) $ awk '{gsub("< 正規表現>","...",$0);print}' < ファイル $ teip -og '< 正規表現>' -- awk '{gsub("< 正規表現>","...",$0);print}' < ファイル ※ < 正規表現> はすべて同じもの

    Slide 32

    Slide 32 text

    ベンチマーク (2) ⼤きなファイルのIPアドレスを置換 上: 置換前 下: 置換後

    Slide 33

    Slide 33 text

    ベンチマーク (3) $ teip -og '< 正規表現>' -- awk '{print "..."}' < ファイル $ teip -og '< 正規表現>' -- sed -n 'i...' < ファイル teip があるから実現可能な効率的な⽅法 よりフェアな⽐較(?) 正規表現の実⾏は1回のみ

    Slide 34

    Slide 34 text

    0 2 4 6 8 10 awk sed teip + awk teip + sed teip + awk (print) teip + sed (i text) Total Runtime(sec) 8.390 5.393 4.306 3.795 2.106 1.836 ※ 詳細・追試⽅法は https://github.com/greymd/teip/wiki/Benchmark

    Slide 35

    Slide 35 text

    まとめ シェル芸は「処理したくないデータの無視」が苦⼿ 「処理対象の選別」と「データの加⼯」が癒着してた 複雑な問題では、スクリプティング地獄 あるいはfork地獄 teip とは 「⼀つのこと」(処理対象の選別)をうまくやる 問題の解法がシンプルになる オマケに「処理対象の選別」と「データの加⼯」を並列化できる 性能向上が図れる

    Slide 36

    Slide 36 text

    今後の展望 teip のこれから sed の /AAA/,/BBB/ みたいなのがほしい sed みたいにN倍数の⾏数のみ、とか(ほしいです?) これで仕事が楽になる⼈は多い気がするので布教 エネルギーの浪費で地球環境に悪い 地球をお⼿当て、そう今のプリキュアのように ああああプリキュアが⾒たいいいいしぬんじゃぁ ぼやき まてよ、Rust 製 Coreutils があるなら、Cureutils がないとおかしいのでは? Rustめっちゃいい。。egzact 作り直そうか。。どうしても遅いよぉ。。