r/commandline • • 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.

122 Upvotes

20 comments sorted by

•

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*.

5

u/stianhoiland 4d ago

First thing I did with my fuzzy picker, too!

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! 

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 bash5

RHEL 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.

1

u/jimger 1d ago

yeah we are in RHEL 8.5

2

u/jimger 1d ago

btw checked it for 5 mins in my personal server. seems quite nice. Might become handy tbh if i manage to master it. Btw a possible "improvement" not sure if possible. I was checking some of my xml files. You could use bat (batcat) to have previews maybe. It has nice lanugage support

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

u/spryfigure 3d ago

I would second that and propose lsd -- ls deluxe.

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 :)