teip コマンド に関するお話
teip
v2.0 に新機能:マッチオフロード機能
- テープの穴をあける行の指定に外部コマンドが使えるようになった
- あるコマンドの抽出範囲のみを、範囲外は残したまま、別のコマンドで編集可能になる
- 例1: grep で抽出した範囲だけを、sed で編集
- 例2: sed で抽出した範囲だけを、awk で編集
- 例3: tail で抽出した範囲だけを、perl で編集
- コマンドの「パターンマッチ」と「編集」を切り離して利用できる時代が到来
- ぐれさん (Twitter: @grethlen)
- gr3.ie
- ヨーロッパにいるよ
- このあと仕事だよ
- シェル芸本、かいてるよ、よろしくね
- この本も、ちょっとかいたよ、よろしくね
- 1 年と 10 ヶ月前 (2020/06/27) にお披露目したシェル環境の「マスキングテープ」
- 多くの反響(大した宣伝はしてないのに ☆ 300 超えた)
- 海外からも届く Issue / PR
- 界隈で粛々と使われている(みんなだいすき)
- シェル芸本でも登場
※ 詳しくは以下の資料をみてね。 資料: シェル芸人に必要なのは「マスキングテープ」だったのでは - Speaker Deck
対話シェル環境の「マスキングテープ」
$ echo 100 200 300 400 | teip -f 3
100 200 [300] 400
- 標準入力全体はマスキングテープに覆われる
- ただし、300 の位置に穴があいている(と理解してください)
- テープの下はいじれない → そのままの位置でキープ
- 穴が空いたところだけ、パイプに渡して加工
$ echo 100 200 300 400 | teip -f 3 -- sed 's/.*/うんこ/g'
100 200 うんこ 400
-
色んなアプローチで穴を開けられる
- 正規表現、カンマ区切り、行番号指定、文字範囲指定
teip
にteip
を重ねることで AND 条件で複雑に指定ができる- 例:
teip -g AAA -- teip -f 3 -- sed 's/./@/'
=> "AAA" を含む行の 3 列目に穴をあけてsed
で置換
- 例:
-
高パフォーマンス
- I/O バッファリング、複数スレッド、高速に GB クラスのファイルでもさばける
- Rust 製 メモリ安全 おれ騒然(川柳)
- GNU 製コマンド単体より、むしろ
teip
を併用すると高速化するケースも
- CSV ファイルの 3 列目だけ編集
- 巨大なログの日付を
date
で変換 - パターンマッチ機能を持たないコマンドを加工に利用できる
- 文章の一部分の URL を短縮する、Base64 変換する、漢字だけふりがなを振る、参考文献の番号を振り直す.. etc
- 新オプション
-e
(external command という意味で e) teip
ではなく、外部コマンドにもマスキングテープの穴を開けさせることが可能に
teip -e 'コマンド文字列' -- コマンド
-
今までは
teip
しか穴の位置は制御できなかった-f
(フィールド指定)、-g
正規表現、-l
(行番号指定)、-c
(文字範囲指定) など
-
ふるつきさんと Issue で議論した末に誕生 => GitHub Issue 12
- ありがとうございますありがとうございます
-e
には文字列としてコマンドラインを指定でき、このコマンドラインの出力結果の数字が、行番号指定に使われる。
$ echo -e "AAA\nBBB\nCCC" | teip -e 'echo 3'
AAA
BBB
[CCC]
=> 3 行目に穴があく
複数行もいける(昇順である必要あり)。
$ echo -e "AAA\nBBB\nCCC" | teip -e 'echo 1;echo 2'
[AAA]
[BBB]
CCC
以下で、2 行ごとに穴があく。
$ echo -e 'AAA\nBBB\nCCC\nDDD\nEEE\nFFF\n' | teip -e 'seq 1 2 inf' -- sed 's/./@/g'
@@@
BBB
@@@
DDD
@@@
FFF
コマンドラインの出力が多少汚くても動く
- スペースやタブ文字が文頭に含まれていてもそれらは無視
- 一旦数字が与えられれば、数字よりも右側に文字列があっても問題なし
- 内部的には、
^\s*([0-9]+)
という表現における 1 つめのグループを数字として認識
$ echo - 'AAA\nBBB\nCCC' | teip -e 'echo " 1"'
[AAA]
BBB
CCC
$ echo - 'AAA\nBBB\nCCC' | teip -e 'echo " 2うんこうんこ"'
AAA
[BBB]
CCC
-e
のコマンドラインは teip
本体と同じ標準入力のコピーが与えられている
seq
のような数字を出力するコマンドだけでなく、
「入力を踏まえて数字を出力する」コマンド(grep
、sed
、awk
、perl
…)も使える。
例: 以下は「"CCC"という文字列を含んだ行と、それよりあとの2行の行番号」を表示する grep
のコマンドで、-n
と -A
オプションを利用
$ printf 'AAA\nBBB\nCCC\nDDD\nEEE\nFFF\n' | grep -n -A 2 CCC
3:CCC
4-DDD
5-EEE
これを、-e
に与えると、「"CCC"という文字列を含んだ行と、それよりあとの2行」に穴を開けることができる
$ printf 'AAA\nBBB\nCCC\nDDD\nEEE\nFFF\n' | teip -e 'grep -n -A 2 CCC'
AAA
BBB
[CCC]
[DDD]
[EEE]
FFF
GNU sed
には =
という、処理中の行番号を表示するコマンドがある。
下記は "BBB"を含んだ行から、 "EEE" を含んだ行までに穴をあける例。
$ printf 'AAA\nBBB\nCCC\nDDD\nEEE\nFFF\n' | teip -e 'sed -n "/BBB/,/EEE/="'
AAA
[BBB]
[CCC]
[DDD]
[EEE]
FFF
もちろん、同様の操作は awk
でも実現可能。
$ printf 'AAA\nBBB\nCCC\nDDD\nEEE\nFFF\n' | teip -e 'awk "/BBB/,/EEE/{print NR}"'
$ printf 'AAA\nBBB\nCCC\nDDD\nEEE\nFFF\n' | perl -nle 'print $. if /BBB/../EEE/'
-e
と nl
は非常に相性が良い。
tail
と nl
を組み合わせ、入力の末尾の3行のみに穴をあけることができる。
(末尾から N 行、という指定は他のコマンドでは難しい、ですよね?)
$ printf 'AAA\nBBB\nCCC\nDDD\nEEE\nFFF\n' | teip -e 'nl -ba | tail -n 3'
AAA
BBB
CCC
[DDD]
[EEE]
[FFF]
-e
の引数は単一の文字列なので、|
などの記号を使うこともできる。
通常のシェル芸では苦労する処理、実現が現実的ではない処理(諦めてスクリプトを書き始めるような処理)すら、簡単にできてしまう。
$ cat file | teip -e 'grep -C 2 うんこ' -- sed 's/./@/g'
以前から引き続き、-e
もまた数 100 MB の利用もストレスがない高速動作。
100 MB のファイル の「admin」を含む行とその前後 2 行に含まれる文字をすべて @
に置換する検証
107 万行ちょっとのファイルのうち、16万行、だいたい全体の 15 % くらいをマッチさせる。 適宜 admin が含まれている位置は全体的にバラけている。
$ wc -l test_secure
1078333 test_secure
$ grep -C 2 admin test_secure | wc -l
160893
$ grep admin test_secure | wc -l
66771
- 以前同様、AWS の t3.medium 上で検証、ただし RAM ディスク上で検証
grep -n -C 3 admin < test_secure | awk -F '[-:]' '{print $1}' | awk NF | awk '{print $0"s/./@/g"}' | sed -f- test_secure
=> 28448 秒(7.9 時間)
grep -n -C 3 admin < test_secure | awk -F '[-:]' '{print $1}' | awk NF | awk '{ do{ for(s=e=$1; (r=getline)>0 && $1<=e+1; e=$1); print s==e ? s : s","e }while(r>0) }'| awk '{print $0"s/./@/g"}' | sed -f- test_secure
=> 469.16 秒
teip -e grep -n -C 3 admin -- sed s/./@/g
=> 31.68 秒
-
めっちゃスレッド作って頑張った。 https://github.com/greymd/teip/blob/16cd7e4b6764b0530006901645687bc5cb12a0d1/src/main.rs#L305-L349
-
スレッド同士でデッドロック起こすこともあった。
-
動作が正しいことを確かめるために時間がかかった。
-
デリシャスパーティープリキュアが最近見れてない。
-
とてもつかれた(2 年前同様、そして燃え尽きてしばらく手が止まる)。
現状、メモリはモリモリ使う。
- 本来一時ファイルを作ってやるべきような処理をキューイングで頑張っている
- 複数スレッドで標準入力を処理するスピードに差があると、その差分はメモリ上に乗る
-e
のコマンドのスレッドと、teip
のマスキングテープを処理するスレッドに速度差があると、
- 最悪のケース(片方のスレッドをノロマにする)など複数のケースでベンチマーク。
- ピーク時は概ね ファイルサイズ x 3 のメモリを食う模様。
- GB 単位の処理には注意。
--------------------------------------------------------------------------------
Command: ./target/release/teip -e grep -n -C 3 admin -- sed s/./@/g
Massif arguments: --time-unit=ms --massif-out-file=mem_usage_real.txt
ms_print arguments: ./mem_usage_real.txt
--------------------------------------------------------------------------------
MB
320.5^ #
| #:::
| @#:: ::
| ::@#:: ::::
| : @#:: :::::::
| ::: @#:: ::::: :::
| : : @#:: ::::: ::::::
| @@: : @#:: ::::: ::::: ::::
| @ : : @#:: ::::: ::::: : : ::
| @:@ : : @#:: ::::: ::::: : : : ::::
| @@@:@ : : @#:: ::::: ::::: : : : : : ::
| @ @:@ : : @#:: ::::: ::::: : : : : : :
| @@@ @:@ : : @#:: ::::: ::::: : : : : : : ::
| @ @ @:@ : : @#:: ::::: ::::: : : : : : : : :
| @@ @ @:@ : : @#:: ::::: ::::: : : : : : : : ::::
| @@@ @ @:@ : : @#:: ::::: ::::: : : : : : : : ::::::::
| @@@@@ @ @:@ : : @#:: ::::: ::::: : : : : : : : ::::::::::::::
| :@ @@@ @ @:@ : : @#:: ::::: ::::: : : : : : : : ::::::::::: :::
| @:@ @@@ @ @:@ : : @#:: ::::: ::::: : : : : : : : ::::::::::: ::::
| :@:@ @@@ @ @:@ : : @#:: ::::: ::::: : : : : : : : ::::::::::: :::::
0 +----------------------------------------------------------------------->s
0 31.68
jq
や moreutils のような存在を目指す- 「ビルトインじゃないけど、みんな使ってるデファクトスタンダードだよね」という立ち位置
- きっと世の中を変える、と思うので、見守ってくだしあ。
- 5 年、10 年スパンで粛々と。
- 抱負
- そろそろ Homebrew 本家に入れる
- 各ディストロのリポジトリに入れたい(EPEL とか、snap とか)
- 他のアーキテクチャのビルドも用意する
- お前らのプルリクエスト待ってるぜ!!
- もっとメモリ節約できないかなぁ
- イースターエッグ入れたよ