Rustでgrepの検索結果の周囲を表示するコマンド suln を作ってみた
2022/10/3 #Rust #CLI #自作
grepの検索結果の続きを表示したくなったので専用のツールをRustで作ってみた
めちゃくちゃニッチなツールだとは思う
https://gyazo.com/493e944f3ec0c3c07d08f272c1c5a8ed
作ったもの
https://github.com/jiro4989/suln
clates.io にも登録したので cargo でインストールできる
https://crates.io/crates/suln
インストール方法はリポジトリのREADMEを参照
名前はいつも適当に決めていて「surroundings of line number」から文字を4つ拾ってsulnにした
発音は僕も分かってなくて、たぶん「サルン」になると思う
できること
grepで検索をした時に、ファイル名と行番号を表示できる
code:sh
⟩ grep -Hn '###' README.md
README.md:62:### Usecase: searching JSON data
README.md:133:### Pre-requisite
README.md:137:### Build
README.md:143:### Test
-B -A -Cで検索結果の前後の行を表示できる
-A は後ろの行を追加で表示する
code:sh
⟩ grep -Hn -A 2 '###' README.md
README.md:62:### Usecase: searching JSON data
README.md-63-
README.md-64-Here is an example of AND search for multiple keys in JSON.
--
README.md:133:### Pre-requisite
README.md-134-
README.md-135-* rustc 1.64.0 (a55dd71d5 2022-09-19)
この検索結果にgrepを重ねると、いわゆるAND条件での検索になるが、当然前後の行データは失われる
code:sh
⟩ grep -Hn -A 2 '###' README.md | grep rustc
README.md-135-* rustc 1.64.0 (a55dd71d5 2022-09-19)
そこで、このAND検索後の出力に対してsulnを付け足すことで、その前後の行データを表示できる
code:sh
⟩ grep -Hn -A 2 '###' README.md | grep rustc | suln -B 2
README.md:133:### Pre-requisite
README.md:134:
README.md:135:* rustc 1.64.0 (a55dd71d5 2022-09-19)
sulnのオプションは-B -A -Cのみ。これはgrepのオプションと同じ名前で、同じように振る舞う
今回の例だと以下のようにgrepだけでも同じことができるが、違いはgrepが表示していない場所でも表示できること
code:sh
⟩ grep -Hn -A 2 -m 2 '###' README.md | grep -B 2 rustc
README.md:135:### Pre-requisite
README.md-136-
README.md-137-* rustc 1.64.0 (a55dd71d5 2022-09-19)
こんな感じで連結しまくれる
code:sh
⟩ grep -Hn -A 2 -m 2 '###' README.md | grep Pre
README.md:135:### Pre-requisite
⟩ grep -Hn -A 2 -m 2 '###' README.md | grep Pre | suln -A 1
README.md:135:### Pre-requisite
README.md:136:
⟩ grep -Hn -A 2 -m 2 '###' README.md | grep Pre | suln -A 1 | suln -A 1
README.md:135:### Pre-requisite
README.md:136:
README.md:137:* rustc 1.64.0 (a55dd71d5 2022-09-19)
⟩ grep -Hn -A 2 -m 2 '###' README.md | grep Pre | suln -A 1 | suln -A 1 | suln -A 2
README.md:135:### Pre-requisite
README.md:136:
README.md:137:* rustc 1.64.0 (a55dd71d5 2022-09-19)
README.md:138:
README.md:139:### Build
それだけ
背景
grepの結果の続きを表示するのをパイプつなぐだけで簡単に表示したいなぁと思ったから
awkとか使ってゴニョゴニョやれば同じことを実現できると思うけれど、ちょい面倒
sulnが役に立つシーンはめちゃくちゃ限定的だと思うけれど、局所的に役に立つと思う
Rustで実装した理由
rustが勉強したかったから
「Linux」、バージョン6.1でRustを導入へ--トーバルズ氏が明言という記事を見かけて、そのうちLinux周りでRustのコードを読む機会が出てくるかもしれない、と思ったので軽く読み書きできる程度には理解しておくか、と思った
経験則的に、勉強するなら何かツールを作る時に使うのが一番身に付くので、何かツール作る機会を探していた
たまたま今回のニーズを思いついて、せっかくなのでRustで実装してみるか、となった
別にRustである必要はなくて、GoやNimでも同じくらいの手間で実装できると思う
ただripgrepやbatなど、ちょいちょい便利ツールがrustで実装されている実情を踏まえて、CLIツールとRustは相性良いだろうと以前から注目していたのもある
感想
超久しぶりにRustを勉強し直したので、ほとんど覚えていなかった
teipに改修を入れるMRを出したのが2022年なので、今から2年も前になる
https://github.com/greymd/teip/pull/14
それから全くRust書いてなかったので、完全に忘れてしまっていた
まぁその時も必要な領域の勉強しかしてなかったので、ほとんど理解してなかったような気もするが
パフォーマンス計測とかはしていないので、実際早いのか、とか省メモリなのか、とかは分からない
コンパイラにバチボコに怒られまくったけれど、一通り作り終えて見ると所有権と借用についてはある程度分かった気がする
後面白かったのはResult型とOption型をunwrapできることやmatchの結果を変数に代入できること
パターンマッチングが強力で便利そうだった
イテレータ用のメソッドが充実しているのも良かった
Goはこのあたり弱いのでいつも羨ましく思ってる
ファイルを開いても変数がdropされる時にファイルをクローズしてくれるのも良いね
https://doc.rust-lang.org/std/fs/struct.File.html
Files are automatically closed when they go out of scope. Errors detected on closing are ignored by the implementation of Drop. Use the method sync_all if these errors must be manually handled.
所有権はRustで初めて見た概念だったので初めは苦戦したけれど所有権を理解する - The Rust Programming Language 日本語版を一通り読んだらだいたいどういうものなのか理解できたのであんまり時間かからなかった
少なくとも、この程度のツールを実装するのに土日を半日ずつ使う程度で済んだ
11時間程度かかった
https://gyazo.com/a8a717b2636a470c2dbbd59ea9ff0aca
正規表現やCLI用のライブラリが標準に含まれてなかったのには困惑したが
書いてみて結構楽しかったけれど、やっぱりGoのが好みなのでしばらく書くことは無いと思う