Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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

F082484f19b518225501fafa28d05b93?s=47 greymd
June 27, 2020

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

2020/06/27 第48回シェル芸勉強会LT資料
https://www.youtube.com/watch?v=PIqx9fCSbaU&t=609s

F082484f19b518225501fafa28d05b93?s=128

greymd

June 27, 2020
Tweet

Transcript

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

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

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

  4. ⾃⼰紹介 (3/3) Software Design シェル芸連載 「シェル芸⼈からの挑戦状」の連載 参加してました 2020 年 4

    ⽉号にて⼀旦終了 ※ STOPがかかったわけ ではないではない 150 問以上 ご愛読ありがとうございました!
  5. シェル芸漬けの2年半 数多くの問題を解いたり考えたり 締切に追われて苦しみながら シェル芸⼈たちと頭をひねった2年半.. => ある法則に気づいた

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

  7. 「できるけど疲れる」問題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 のファイルです
  8. 問題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 => 事実上使えない(後述)
  9. 問題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 で⾒事にマシンがホッカイロ(⽩⽬)
  10. 「できるけど疲れる」問題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 ...
  11. 問題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}'
  12. これらの問題の共通点はなんだろう? 元々の⼊⼒を残したまま⼀部分を書き換えている 処理対象が⼤きい データの加⼯⾃体は古典的なコマンドでできる でも使えないので、結局スクリプトでゴリ押しが現実解 => こんなの絶対おかしいよ!

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

    , nkf , grep , fold , cut , ...
  14. 「できるけど疲れる」原因は何か?(2) シェル芸の⾟い所(2): 問題が複雑になるとスクリプティング地獄 「処理したくないデータの無視」ができるコマンドもあるが... awk , sed , perl ,

    ruby , ... 「処理対象の選別」と「データの加⼯」がセット ⽴派なプログラミング⾔語 => ⾔語の制約に縛られる ⾔語の仕様や技法・ライブラリに明るい必要がある => いざというときサクッとできない
  15. 補⾜:「処理対象の選別」と「データの加⼯」がセット 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"'
  16. 「できるけど疲れる」原因は何か?(3) シェル芸の⾟い所(3): スクリプティング地獄を解消しようとするとfork地獄に陥る Bash の while ⽂ sed、AWK等の内部から別のコマンドを外部コマンドとして呼び出す ⼤量のデータ処理には向かない

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

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

    世の中の問題はシンプルになるのではないか
  19. 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
  20. teip はなにができるか teip < フィルタリングルール> -- < コマンド> < フィルタリングルール>

    : 標準⼊⼒のどの部分を < コマンド> に渡すか設定 < コマンド> : 標準⼊⼒まるごとは受け取る必要はない < フィルタリングルール> にマッチしなかった個所 => そのまま出⼒ < フィルタリングルール> にマッチした個所 場所はそのままだが < コマンド> の結果と置換される
  21. teip の基本的な使い⽅ ※ デモ ※ README の Getting Started の内容を紹介

  22. 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'
  23. こういう問題も解ける(時間があればデモ) 第30回危念シェル芸勉強会 https://b.ueda.tech/?post=10134 Q2 ⼀部抜粋 リンクが相対パスになっているものについては頭に/files/をつけて、/から始まっているも のとhttpやhttpsから始まっているものはそのままにしてください。 (.. 略..) <li><a

    href="./hoge.html"> ほげ</a></li> <li><img src="ayasii.jpg" alt=" 怪しい" /></li> <li><a href="https://blog.ueda.tech/"> クソブログ</a><a href="huge.html"> ふげ</a></li> <li><a href="/root.jpg"></a> これはそのまま</li> <li><a href="http://www.usptomo.com/"> 更新してない</a></li> (.. 略..)
  24. 解答例 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
  25. 解答例(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 条件(!)... 表現⼒は超強⼒
  26. teip のパフォーマンス 細かくベンチマークをとりながら Rust で開発 実⾏対象のコマンドは使い回す => fork しないので早い(※ )

    実⾏コマンドの標準⼊出⼒をプロキシ 複数プロセス並列実⾏ ⼀時ファイルなし I/O は極⼒バッファリング => 無駄なシステムコールを削る ※ 都度forkする動作も選択可能
  27. なぜ Rust か 速度・並⾏性に定評があるコンパイル型⾔語 Rust 製 Coreutils(uutils/coreutils)あり 性能をベンチマーク => 本家に負けず劣らず

    => ヒーリングっどきた Cの資産の取り回しも悪くない 例: ⻤⾞正規表現 Cの共有ライブラリも⼀緒にビルド
  28. 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 => デモ
  29. 注意: teip単体で⼗分な⽔準のI/O速度を出せる点のアピール以上の意図は無い ユースケースが異なるものを⽐較してもあまり意味はない teipはまだまだ⽣まれたばかりのソフト いわゆる「第⼀のシステム」※ 成熟したソフトより速度が早い状況は「あるある」

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

    オーム社, 2001.
  31. ベンチマーク (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}' < ファイル ※ < 正規表現> はすべて同じもの
  32. ベンチマーク (2) ⼤きなファイルのIPアドレスを置換 上: 置換前 下: 置換後

  33. ベンチマーク (3) $ teip -og '< 正規表現>' -- awk '{print

    "..."}' < ファイル $ teip -og '< 正規表現>' -- sed -n 'i...' < ファイル teip があるから実現可能な効率的な⽅法 よりフェアな⽐較(?) 正規表現の実⾏は1回のみ
  34. 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
  35. まとめ シェル芸は「処理したくないデータの無視」が苦⼿ 「処理対象の選別」と「データの加⼯」が癒着してた 複雑な問題では、スクリプティング地獄 あるいはfork地獄 teip とは 「⼀つのこと」(処理対象の選別)をうまくやる 問題の解法がシンプルになる オマケに「処理対象の選別」と「データの加⼯」を並列化できる

    性能向上が図れる
  36. 今後の展望 teip のこれから sed の /AAA/,/BBB/ みたいなのがほしい sed みたいにN倍数の⾏数のみ、とか(ほしいです?) これで仕事が楽になる⼈は多い気がするので布教

    エネルギーの浪費で地球環境に悪い 地球をお⼿当て、そう今のプリキュアのように ああああプリキュアが⾒たいいいいしぬんじゃぁ ぼやき まてよ、Rust 製 Coreutils があるなら、Cureutils がないとおかしいのでは? Rustめっちゃいい。。egzact 作り直そうか。。どうしても遅いよぉ。。