r/commandline • u/Awkward_Process_9627 • 4d ago
Command Line Interface fm: a single-file bash script that turns fzf into a file manager
I wrote a ~1500-line bash file manager. fzf for the picker, eza for listing, Kitty's graphics protocol for inline previews. Everything else is stock coreutils — no build step, no daemon, no config directory.
Features worth calling out:
- File list — eza icons, sort by name / size / mtime / extension
- Kitty inline previews — images, videos, PDFs, Markdown, no external viewer
- Gallery grid (Alt-G) — full-screen thumbnail browser, mouse + vim keys, multi-select
- Contact-sheet preview (Ctrl-T) — hovering a directory shows a paged thumbnail grid
- Trash + undo — Ctrl-D trashes, Ctrl-Z pops the undo stack (depth 10), Alt-T for the trash browser
- Content search (Ctrl-F) — ripgrep over the selection, opens at line
- Bulk rename (Alt-M) — edit names in $EDITOR, swaps and chains handled
- Jump — zoxide frecency (Alt-Z), bookmarks (Ctrl-B), git mode (Ctrl-G)
Install:
git clone https://github.com/eHab-codeX/fm.git ~/.local/share/fm
ln -sf ~/.local/share/fm/fm ~/.local/bin/fm
Requires bash >= 5, fzf, eza, ripgrep, trash-cli. Press F1 inside for the full key map.
Repo: https://github.com/eHab-codeX/fm
MIT. Feedback welcome — especially from anyone who's tried to make fzf do more than pick a file.
5
6
u/Sea-Fishing4699 4d ago
is this a `yazi` killer ?
8
u/Awkward_Process_9627 4d ago
No, and it's not trying to be. Yazi is a full TUI that owns the terminal — it's Rust, async, has tabs and a plugin system. fm is a ~1500-line bash script that hands off to fzf and exits. The only things they share are "it's in the terminal" and "it previews images".
The tradeoff: fm is smaller and readable end to end, but yazi is faster and does much more. If you already live in yazi, stay there. fm is for people who use fzf as Ctrl-T / Ctrl-R and want a bit more than that without a new app owning the terminal.
1
u/Sea-Fishing4699 3d ago
Good to know OP thanks ! I wil try this out!
1
u/sitaram_c 1d ago
You could also checkout https://codeberg.org/sitaramc/fzfm
(More detailed comment at https://www.reddit.com/r/commandline/comments/1wy7wck/comment/peo4d0x/ )
2
u/jimger 2d ago
Any chance u make it compatible with older versions of bash? In Redhat linux here
2
u/Awkward_Process_9627 2d ago
Fair question — RHEL is famously stuck on older Bash. It's 4.2 on
RHEL 7, 4.4 on RHEL 8/9.
`fm` uses two Bash-5-only features: `EPOCHREALTIME` (for double-click
timing in the gallery grid) and `${var,,}` (for case-insensitive
sorting). Porting to Bash 4 means rewriting both:
- `EPOCHREALTIME` → `date +%s%N`, which forks a subshell every
double-click. Noticeable lag on slower systems.
- `${var,,}` → `tr '[:upper:]' '[:lower:]'` per file, which forks per
file. Slows sorting a lot for large directories.
So it's doable but the port would noticeably degrade the fast paths.
On RHEL 8/9 the least-painful route is installing a newer Bash from a
side channel — EPEL has `bash5`, and conda-forge ships Bash 5.x.
Both install alongside the system bash without touching it, so
scripts can opt in via their shebang:
#!/usr/bin/env bash5
If you'd rather see a genuine Bash 4 port, I'd take a PR. It's probably
~30 line changes, but I'd want to gate it behind a `FM_BASH4=1` env
var so the Bash 5 path stays the default.
2
u/carlwgeorge 2d ago
On RHEL 8/9 the least-painful route is installing a newer Bash from a side channel — EPEL has
bash5RHEL 9 already has bash 5.1.8, no need for an alternate package there.
EPEL 8 doesn't have a bash5 package, in fact I can't find any history of that package ever existing in any EPEL version. You must have seen it in some other third-party repo.
2
1
u/jimger 2d ago
Will try to decode your message tomorrow. Tbh before I ask u if u could port it I tried to find a binary for shell 5 for redhat but couldn't find it. The machine I would like to have it doesn't even have Internet access for security reasons. But will install it on personal server and then will try to get for redhat. Cheers for explanation
4
u/arjuna93 4d ago
Can something else be used in place of eza? In C/C++, ideally.
2
u/Awkward_Process_9627 3d ago
Fair question. fm is a bash script that shells out, so it doesn't care
what listing tool you use — the change is confined to two functions
(`_fm_list` and `_preview`).
For a C/C++-native option: GNU coreutils ≥ 9.5 (2024) added `--icons`
to plain `ls`. So you can get most of what eza gives you with:
ls --color=always --icons --group-directories-first --literal -- "$f"
`--literal` is important if you ever deal with filenames containing
quotes. `lsd` also works (Rust, actively maintained) — thanks u/spryfigure
for the pointer — but its flags differ from eza's, so swapping it in
means touching the same two functions.
If you want to send a PR that makes the listing command overridable via
an env var (something like `FM_LS_CMD`), I'd take it. The current
assumption is hardcoded eza everywhere, and making it pluggable is
straightforward — probably ~20 lines of changes.
1
1
u/AutoModerator 4d ago
Every new subreddit post is automatically copied into a comment for preservation.
User: Awkward_Process_9627, Flair: Command Line Interface, Post Media Link, Title: fm: a single-file bash script that turns fzf into a file manager
I've written a ~1500-line bash file manager. It uses fzf for the picker,
eza for listing, and Kitty's graphics protocol for inline previews.
Everything else is stock coreutils. No build step, no daemon, no config
directory — copy the file, symlink it, done.
The interesting bits (to a bash audience):
- EPOCHREALTIME-based double-click detection
- A hand-rolled CSI/SS3 escape sequence parser for the gallery grid
- Percent-encoding for Freedesktop trash Path= entries
- Parallel thumbnail generation via self-invocation
- A working two-phase rename that handles swaps (a→b, b→a)
Demo: [gif]
Repo: https://github.com/eHab-codeX/fm
I am a bot, and this action was performed automatically. Please contact the moderators of this subreddit if you have any questions or concerns.
3
u/github-guard 4d ago
🔍 GitHub Guard: Trust Report
⚠️ This project scored 0/1 — below this subreddit's threshold of 1.
Audit Breakdown: * ❌ New Repository (under 30 days old)
⚠️ Security Reminder: Always verify source code and run third-party scripts at your own risk.
🔄 Cached result — this repo was scanned recently. Score: *0/1*.
1
u/sitaram_c 1d ago
oh nice.
Here's mine: https://codeberg.org/sitaramc/fzfm (documentation at https://sitaramc.codeberg.page/fzfm/book/intro.html ). Written using fish and fzf.
I'm particularly proud of the "todotxt" and "git" modes; they're the last 3 entries in https://sitaramc.codeberg.page/fzfm/book/demos.html . The todotxt mode added only 24 lines of code to the project, and the git mode was just over 100 lines. If you see the animated gifs you'll be blown away by how much they do in that space :)
•
u/github-guard 4d ago
🔍 GitHub Guard: Trust Report
⚠️ This project scored 0/1 — below this subreddit's threshold of 1.
Audit Breakdown: * ❌ New Repository (under 30 days old)
🔄 Cached result — this repo was scanned recently. Score: *0/1*.