Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
シェル芸人に必要なのは「マスキングテープ」だったのでは
Search
greymd
June 27, 2020
Programming
19
10k
シェル芸人に必要なのは「マスキングテープ」だったのでは
2020/06/27 第48回シェル芸勉強会LT資料
https://www.youtube.com/watch?v=PIqx9fCSbaU&t=609s
greymd
June 27, 2020
Tweet
Share
More Decks by greymd
See All by greymd
狂気!AWS CloudShell細胞分裂!
greymd
0
2.2k
毎日叩ける シェル芸を覚えよう!
greymd
4
4.3k
入門Cureutils
greymd
3
2.4k
Other Decks in Programming
See All in Programming
シールドクラスをはじめよう / Getting Started with Sealed Classes
mackey0225
4
640
シェーダーで魅せるMapLibreの動的ラスタータイル
satoshi7190
1
480
現場で役立つモデリング 超入門
masuda220
PRO
15
3.2k
Jakarta EE meets AI
ivargrimstad
0
130
ヤプリ新卒SREの オンボーディング
masaki12
0
130
Macとオーディオ再生 2024/11/02
yusukeito
0
370
Jakarta Concurrencyによる並行処理プログラミングの始め方 (JJUG CCC 2024 Fall)
tnagao7
1
290
TypeScriptでライブラリとの依存を限定的にする方法
tutinoko
2
660
リアーキテクチャxDDD 1年間の取り組みと進化
hsawaji
1
220
初めてDefinitelyTypedにPRを出した話
syumai
0
400
Arm移行タイムアタック
qnighy
0
310
Quine, Polyglot, 良いコード
qnighy
4
640
Featured
See All Featured
Testing 201, or: Great Expectations
jmmastey
38
7.1k
Into the Great Unknown - MozCon
thekraken
32
1.5k
Rails Girls Zürich Keynote
gr2m
94
13k
StorybookのUI Testing Handbookを読んだ
zakiyama
27
5.3k
RailsConf 2023
tenderlove
29
900
Automating Front-end Workflow
addyosmani
1366
200k
How to Think Like a Performance Engineer
csswizardry
20
1.1k
The Invisible Side of Design
smashingmag
298
50k
The Pragmatic Product Professional
lauravandoore
31
6.3k
The Language of Interfaces
destraynor
154
24k
No one is an island. Learnings from fostering a developers community.
thoeni
19
3k
A designer walks into a library…
pauljervisheath
203
24k
Transcript
シェル芸⼈に必要なのは 「マスキングテープ」だったのでは ぐれさん
⾃⼰紹介 (1/3) ぐれさん (Twitter: @grethlen) シェル芸キュアエンジニア 危険シェル芸で国外追放された(⼤嘘) 最近良いことがあった
⾃⼰紹介 (2/3) 朗報 ※ 出典: 2020/06/21 放映「ヒーリングっど♡プリキュア」より
⾃⼰紹介 (3/3) Software Design シェル芸連載 「シェル芸⼈からの挑戦状」の連載 参加してました 2020 年 4
⽉号にて⼀旦終了 ※ STOPがかかったわけ ではないではない 150 問以上 ご愛読ありがとうございました!
シェル芸漬けの2年半 数多くの問題を解いたり考えたり 締切に追われて苦しみながら シェル芸⼈たちと頭をひねった2年半.. => ある法則に気づいた
こういう状況ありませんか 仕事や研究などで遭遇した困りごとをシェル芸で解決しようとするとき... ※ 出典: NHK健康チャンネル「発達障害って何だろう」より
「できるけど疲れる」問題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 のファイルです
問題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 => 事実上使えない(後述)
問題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 で⾒事にマシンがホッカイロ(⽩⽬)
「できるけど疲れる」問題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 ...
問題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}'
これらの問題の共通点はなんだろう? 元々の⼊⼒を残したまま⼀部分を書き換えている 処理対象が⼤きい データの加⼯⾃体は古典的なコマンドでできる でも使えないので、結局スクリプトでゴリ押しが現実解 => こんなの絶対おかしいよ!
「できるけど疲れる」原因は何か?(1) シェル芸の⾟い所(1): ⼤半のコマンドが「処理したくないデータの無視」ができない date , base64 , factor , iconv
, nkf , grep , fold , cut , ...
「できるけど疲れる」原因は何か?(2) シェル芸の⾟い所(2): 問題が複雑になるとスクリプティング地獄 「処理したくないデータの無視」ができるコマンドもあるが... awk , sed , perl ,
ruby , ... 「処理対象の選別」と「データの加⼯」がセット ⽴派なプログラミング⾔語 => ⾔語の制約に縛られる ⾔語の仕様や技法・ライブラリに明るい必要がある => いざというときサクッとできない
補⾜:「処理対象の選別」と「データの加⼯」がセット 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"'
「できるけど疲れる」原因は何か?(3) シェル芸の⾟い所(3): スクリプティング地獄を解消しようとするとfork地獄に陥る Bash の while ⽂ sed、AWK等の内部から別のコマンドを外部コマンドとして呼び出す ⼤量のデータ処理には向かない
シェル芸⼈のジレンマ スクリプティング地獄 ↔ fork地獄 とてもつらい
シェルとコマンドのアプローチを振り返る パイプ コマンド同⼠をべったりとくっつける糊 どこを処理するかはコマンドにおまかせ UNIX のコマンド 標準⼊⼒を「丸ごと」受け取って処理するコマンドが⼤半 糊につける「マスキングテープ」があればどうだろう? 「べったり」ではなく「⼀部分のみ」 残りの部分は「そのまま」
世の中の問題はシンプルになるのではないか
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
teip はなにができるか teip < フィルタリングルール> -- < コマンド> < フィルタリングルール>
: 標準⼊⼒のどの部分を < コマンド> に渡すか設定 < コマンド> : 標準⼊⼒まるごとは受け取る必要はない < フィルタリングルール> にマッチしなかった個所 => そのまま出⼒ < フィルタリングルール> にマッチした個所 場所はそのままだが < コマンド> の結果と置換される
teip の基本的な使い⽅ ※ デモ ※ README の Getting Started の内容を紹介
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'
こういう問題も解ける(時間があればデモ) 第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> (.. 略..)
解答例 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
解答例(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 条件(!)... 表現⼒は超強⼒
teip のパフォーマンス 細かくベンチマークをとりながら Rust で開発 実⾏対象のコマンドは使い回す => fork しないので早い(※ )
実⾏コマンドの標準⼊出⼒をプロキシ 複数プロセス並列実⾏ ⼀時ファイルなし I/O は極⼒バッファリング => 無駄なシステムコールを削る ※ 都度forkする動作も選択可能
なぜ Rust か 速度・並⾏性に定評があるコンパイル型⾔語 Rust 製 Coreutils(uutils/coreutils)あり 性能をベンチマーク => 本家に負けず劣らず
=> ヒーリングっどきた Cの資産の取り回しも悪くない 例: ⻤⾞正規表現 Cの共有ライブラリも⼀緒にビルド
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 => デモ
注意: teip単体で⼗分な⽔準のI/O速度を出せる点のアピール以上の意図は無い ユースケースが異なるものを⽐較してもあまり意味はない teipはまだまだ⽣まれたばかりのソフト いわゆる「第⼀のシステム」※ 成熟したソフトより速度が早い状況は「あるある」
※ 芳尾 桂 (翻訳) Mike Gancarz (著). UNIX という考え⽅― その設計思想と哲学.
オーム社, 2001.
ベンチマーク (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}' < ファイル ※ < 正規表現> はすべて同じもの
ベンチマーク (2) ⼤きなファイルのIPアドレスを置換 上: 置換前 下: 置換後
ベンチマーク (3) $ teip -og '< 正規表現>' -- awk '{print
"..."}' < ファイル $ teip -og '< 正規表現>' -- sed -n 'i...' < ファイル teip があるから実現可能な効率的な⽅法 よりフェアな⽐較(?) 正規表現の実⾏は1回のみ
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
まとめ シェル芸は「処理したくないデータの無視」が苦⼿ 「処理対象の選別」と「データの加⼯」が癒着してた 複雑な問題では、スクリプティング地獄 あるいはfork地獄 teip とは 「⼀つのこと」(処理対象の選別)をうまくやる 問題の解法がシンプルになる オマケに「処理対象の選別」と「データの加⼯」を並列化できる
性能向上が図れる
今後の展望 teip のこれから sed の /AAA/,/BBB/ みたいなのがほしい sed みたいにN倍数の⾏数のみ、とか(ほしいです?) これで仕事が楽になる⼈は多い気がするので布教
エネルギーの浪費で地球環境に悪い 地球をお⼿当て、そう今のプリキュアのように ああああプリキュアが⾒たいいいいしぬんじゃぁ ぼやき まてよ、Rust 製 Coreutils があるなら、Cureutils がないとおかしいのでは? Rustめっちゃいい。。egzact 作り直そうか。。どうしても遅いよぉ。。