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
11k
シェル芸人に必要なのは「マスキングテープ」だったのでは
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
Breaking Through Firewalls with Shell-gei
greymd
0
3
狂気!AWS CloudShell細胞分裂!
greymd
0
2.3k
毎日叩ける シェル芸を覚えよう!
greymd
4
4.3k
入門Cureutils
greymd
3
2.4k
Other Decks in Programming
See All in Programming
今年一番支援させていただいたのは認証系サービスでした
satoshi256kbyte
1
250
これでLambdaが不要に?!Step FunctionsのJSONata対応について
iwatatomoya
2
3.6k
DevFest Tokyo 2025 - Flutter のアプリアーキテクチャ現在地点
wasabeef
5
900
命名をリントする
chiroruxx
1
390
17年周年のWebアプリケーションにTanStack Queryを導入する / Implementing TanStack Query in a 17th Anniversary Web Application
saitolume
0
250
Full stack testing :: basic to basic
up1
1
930
たのしいparse.y
ydah
3
120
SymfonyCon Vienna 2025: Twig, still relevant in 2025?
fabpot
3
1.2k
talk-with-local-llm-with-web-streams-api
kbaba1001
0
180
クリエイティブコーディングとRuby学習 / Creative Coding and Learning Ruby
chobishiba
0
3.9k
range over funcの使い道と非同期N+1リゾルバーの夢 / about a range over func
mackee
0
110
Monixと常駐プログラムの勘どころ / Scalaわいわい勉強会 #4
stoneream
0
270
Featured
See All Featured
StorybookのUI Testing Handbookを読んだ
zakiyama
27
5.3k
BBQ
matthewcrist
85
9.4k
What's in a price? How to price your products and services
michaelherold
243
12k
Rebuilding a faster, lazier Slack
samanthasiow
79
8.7k
Build your cross-platform service in a week with App Engine
jlugia
229
18k
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
656
59k
Reflections from 52 weeks, 52 projects
jeffersonlam
347
20k
Put a Button on it: Removing Barriers to Going Fast.
kastner
59
3.6k
Practical Tips for Bootstrapping Information Extraction Pipelines
honnibal
PRO
10
810
Art, The Web, and Tiny UX
lynnandtonic
298
20k
Designing for humans not robots
tammielis
250
25k
Code Reviewing Like a Champion
maltzj
520
39k
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 作り直そうか。。どうしても遅いよぉ。。