世界を動かす技術を、日本語で。

Show HN: Lstr – Rustで書かれた現代的でインタラクティブなツリーコマンド

概要

lstr はRust製の高速・ミニマルなディレクトリツリービューア tree コマンドを現代的に進化させたインタラクティブTUIモード搭載 Git連携 やアイコン表示、ファイルサイズ・権限表示など多機能 パイプやfzf連携、シェル統合によるcd機能もサポート オープンソースでGitHub・Crates.ioにて公開中

lstr: Rust製高速ディレクトリツリービューア

  • Rust製 のコマンドラインツリービューア
  • treeコマンド を現代的に進化させたプロジェクト構造表示
  • インタラクティブTUIモード によるキーボード操作・ファイルオープン対応
  • 並列処理 による高速なディレクトリスキャン
  • 最小限主義 設計で余計な機能を排除したシンプル体験

主な特徴

  • 並列ディレクトリスキャン で最大限の速度を実現
  • クラシックモード対話的TUIモード の2つを提供
    • lstr: 通常のツリー表示
    • lstr interactive: インタラクティブなTUI
  • ファイルアイコン (Nerd Font必須)、 権限 (-p)、 サイズ (-s)表示に対応
  • Git連携 (-G)でファイルのステータスをツリー内に表示
  • .gitignore対応 (-g)で不要なファイルを自動除外
  • 再帰深度制御 (-L)、 ディレクトリのみ表示 (-d)も可能

インストール方法

  • Rustツールチェーン のインストールが必要
  • または nix develop で開発環境構築
  • リポジトリをクローン
    • git clone https://github.com/bgreenwell/lstr.git
    • cd lstr
  • Cargoでビルド&インストール
    • cargo install --path .

基本的な使い方

  • lstr [OPTIONS] [PATH]
  • lstr interactive [OPTIONS] [PATH]
    • PATH省略時はカレントディレクトリ(.)を対象

主なオプション一覧

  • -a, --all: 隠しファイル 含む全ファイル表示
  • --color <WHEN>: カラー出力 のタイミング指定
  • -d, --dirs-only: ディレクトリのみ 表示
  • -g, --gitignore: .gitignore 等を尊重
  • -G, --git-status: Gitステータス 表示
  • --icons: ファイルアイコン 表示(Nerd Font必須)
  • -L, --level <LEVEL>: 最大再帰深度 指定
  • -p, --permissions: ファイル権限 表示(Unix系のみ)
  • -s, --size: ファイルサイズ 表示
  • --expand-level <LEVEL>: インタラクティブモード用 初期展開深度

インタラクティブモードのキー操作

  • ↑ / k: 上へ移動
  • ↓ / j: 下へ移動
  • Enter:
    • ファイル上: $EDITORで開く
    • ディレクトリ上: 展開/折り畳み切替
  • q / Esc: 正常終了
  • Ctrl+s: 選択パスをstdoutへ出力して終了 (シェル統合向け)

使用例

  • カレントディレクトリ一覧:
    • lstr
  • .gitignoreを無視してプロジェクトを対話的に探索:
    • lstr interactive -g --icons
  • ファイルサイズ・権限付きでディレクトリ表示:
    • lstr -sp
  • Gitステータス付きで全ファイル表示:
    • lstr -aG
  • すべての情報を表示してインタラクティブ起動:
    • lstr interactive -gG --icons -s -p

シェル連携・パイプ活用

  • クラシックモード は他のコマンドラインツールとパイプ連携可能
    • 例: fzfと連携したインタラクティブ検索
      • lstr -a -g --icons | fzf
    • 例: lessやbatでページング
      • lstr -L 10 | less -R
      • lstr --icons | bat

lstrによるディレクトリ移動(lcd関数)

  • シェルの起動ファイル(~/.bashrcや~/.zshrc)に以下を追加
lcd() {
  local selected_dir
  selected_dir="$(lstr interactive -g --icons)"
  if [[ -n "$selected_dir" && -d "$selected_dir" ]]; then
    cd "$selected_dir"
  fi
}
  • 反映後、lcdコマンドで インタラクティブUI起動→Ctrl+sでディレクトリ選択→即座にcd

パフォーマンスと並列処理

  • デフォルトで並列ディレクトリ走査 (rayonスレッドプール利用)
  • RAYON_NUM_THREADS 環境変数でスレッド数制御可能
    • シングルスレッド実行例:
      • RAYON_NUM_THREADS=1 lstr .

開発思想・インスピレーション

  • C言語製treeコマンド に着想を得てRustで再実装
  • 高速・安全・最小限・インタラクティブ を追求
  • オープンソース として公開、フィードバック歓迎
    • GitHub: https://github.com/bgreenwell/lstr
    • Crates.io: https://crates.io/crates/lstr

HN向けメッセージ(作者コメント要約)

  • treeコマンド に現代的機能(インタラクティブ・Git連携等)を追加したくて Rustで自作
  • v0.2.0で以下を追加
    • インタラクティブTUIモード (lstr interactive)
    • Gitステータス連携 (-G)
    • シェル統合 (Ctrl+sでパス出力→cd等に利用可能)
  • Nerd Font対応アイコン・サイズ・権限表示・.gitignore対応 も充実
  • オープンソース で公開中、ぜひ試してフィードバックを
    • GitHub: https://github.com/bgreenwell/lstr
    • Crates.io: https://crates.io/crates/lstr

Hackerたちの意見

いい感じ!erdtreeとbrootの組み合わせみたいだね。毎日両方使ってるけど、これらの特徴で目立つものってあるのかな?

まず、表示がめっちゃいいね!それと、このタイプのプログラムの依存ツリーがこんなに深いとは思わなかった -- 合計141個も!そのほとんどがurlクレートで、これ自体がgitクレートの依存なんだけど、他にもいろいろあるね。今、Rustを学び始めたばかりなんだけど、これってRustプロジェクトにはよくあることなの?それともTUIプロジェクト全般に言えることかな?(追記:取り消し線)~~バイナリも53Mになってるけど、/usr/sbin/treeは私のマシンで80Kなんだよね。今日のストレージではあまり問題じゃないけど、サイズが500~1000倍違うのは無視できないよね。~~ もしかしてリンクに関係してるのかな?本当にどうやってチェックするか分からない。(追記:多くの人が、cargo build --releaseを他のオプションと一緒に使うと、もっと小さいバイナリが得られるって教えてくれた。ありがとう!)

おそらく、機能を調整する必要があるね。Rustで似たようなEzaと比べたら、コンパイル後は1.6 MiBだったよ。Cargo.tomlを見たら、git2がdefault-features = falseで含まれてるね。 https://github.com/eza-community/eza/blob/main/Cargo.toml

おそらくデバッグビルドを見てるんじゃないかな。Linuxでは、リリースビルド(cargo build -r)は約4.3Mで、ストリップすると約3.5Mになるよ。リリースビルドプロファイルにいくつかのトリックを使うと、さらにサイズを減らせるかも。

リリースビルド中:cargo build --release du -sh ./target/release/lstr -> 4.4M 他のリリースオプションを使うと2.3Mまで下がるよ:[profile.release] codegen-units = 1 opt-level = "s" lto = true panic = "abort" strip = "symbols"

バイナリも53M それはデバッグバイナリで、そのほとんどはデバッグシンボルだよ。このプロジェクトのリリースビルドは4.3Mで、桁違いに小さい。さらに、git2クレートのデフォルト機能をコンパイルから外すと、いくつかの依存関係が消えて、さらに3.6Mまで減るよ。 https://github.com/bgreenwell/lstr/pull/5 https://github.com/rust-lang/git2-rs/pull/1168 バイナリをさらにストリップすると2.9Mに改善されて、さらなる最適化でパフォーマンスを損なうことなく2.2Mまで小さくできるよ。(サイズを重視して最適化すればもっと小さくできるけど、パフォーマンスよりサイズを重視しない限りはお勧めしないな。)

cargo build --release --no-default-featuresを試してみて。そうすると、もっと小さいバイナリ(約5~10MB)が得られるよ。Rustは依存関係を静的にリンクするけど、オプション機能のための条件付きコンパイルもサポートしてるからね。

いいね!コメントで2MBに抑えるって言ってたけど、それでもめっちゃ大きいよね。ざっくり計算すると、2MBって約10万行のコードになるって考えると、すごいことだよ。

かなりクールそう!でも、デモのgifに(壊れたアイコンっぽいのが?)使われてるのは大胆な選択だね :p

笑、いい発見だね!俺は完全に見逃してた。GIFを録画するのにVHS使ったから、ネイティブターミナルでスクリプトが動いてないかも!修正しないといけないな。

気づかなかった、これがどれだけすごくて便利なツールか見るのに夢中だったから。しかもファジーマッチングも内蔵されてるなんて?マジで素晴らしい。OP、いい仕事してるね。

Rustを学ぶためにこれを書いた後、Rustについてどう思った?特に好きなところや嫌いなところ、驚いたことはあった?

よくメンテされてるパッケージのエコシステムが気に入ってる。でも、構文があんまり好きじゃなくて、ゴーランに比べるとRustは読みづらくて学びにくいな(Rには慣れてるけど、あれはコンパイル言語じゃないけど素晴らしい開発コミュニティがある)。Cargoに組み込まれてるコンパイラやサポートツール(fmtやclippyなど)は大好き。

みんなのコメントや有用なフィードバックに感謝!(初めてのRustパッケージだから)。特にバイナリのサイズを減らす方法が知りたい!

これめっちゃいいね!俺の好みにぴったり。余談だけど、リンクのGIFで使ってるテーマは何?俺の好きなテーマ、onedarkとgruvboxのちょうど真ん中にあるんだよね。

Rustコミュニティのクラシックツールに対する「モダン」なアプローチが本当に好き。eza(別名exa)を使ってて、lsとしてエイリアスしてるんだけど、「tree」も内蔵されてる(ltとしてエイリアス)。これが「ls」の代わりになってて、日常のコマンドライン使用で一番の生産性向上になってる。ezaにはtreeが内蔵されてて、しかもめっちゃ速いから、このツールは今のところ必要ないかな。いつかインタラクティブモードが魅力的になるかも。リリースおめでとう!リリースの仕方も素晴らしいね:しっかりしたREADME、良い説明、ちょうどいい機能のハイライトがある見た目の良いGIF。

インタラクティブモードの「ls tree」ツールの中で、面白いのがbrootだね。 https://github.com/Canop/broot

めっちゃいいね!それなら、gitignoreに載ってるファイルは表示しないってこと?

非同期じゃないのがいいね。今日学んだことだけど、Rayonの構文はほぼオッカムみたいだね。 https://github.com/bgreenwell/lstr/blob/44b9bbf118ca90558138... でも、結局は並列ファイルシステムトラバーサルをまとめたライブラリを選んだんだね。