colorls 1.0.11

A fast, production-ready Rust rewrite of colorls: a beautified ls with icons, colors, git status and tree view.
colorls-1.0.11 is not a library.

colorls (Rust)

A fast, production-ready Rust rewrite of colorls (the Ruby gem): a beautified ls with Nerd Font icons, per-type colors, git status, and a tree view. Same purpose as upstream colorls — not a line-for-line port — rebuilt from scratch as a single static binary with a YAML config-override system that plays the same role as upstream's ~/.config/colorls/*.yaml files.

Cross-platform: Linux, macOS, and Windows. No Ruby, no gems, no runtime dependencies beyond an optional git binary on PATH for --gs.

Supported platforms

Every tagged release (vX.Y.Z) builds and publishes prebuilt binaries for:

OS Targets
Linux (glibc) x86_64, aarch64, armv7 (armhf, e.g. Raspberry Pi), i686
Linux (musl, static) x86_64, aarch64, armv7
Android / Termux aarch64 (arm64, most phones), armv7 (armv7l, older 32-bit devices), i686, x86_64 (emulators)
macOS x86_64 (Intel), aarch64 (Apple Silicon)
Windows x86_64, i686, aarch64 (Windows-on-ARM, e.g. Surface Pro X)

See Releases for prebuilt archives, or build from source for anything not listed (any target rustc/cross supports should work — the code has no target-specific unsafe/FFI beyond standard library and users/colored, both of which are cross-platform).

Install

Cargo

$ cargo install colorls

Build from source

Requires Rust 1.75+ (matches the toolchain used for the project's other Rust tools). cargo build alone works — no native system libraries are required (git status is invoked via git on PATH at runtime, not linked).

cd colorls-rs
cargo build --release
# binary at target/release/colorls (colorls.exe on Windows)

Cross-compiling for another target locally (with the target installed via rustup, or via cross for targets that need a foreign C toolchain, e.g. Android or musl/ARM):

rustup target add aarch64-unknown-linux-gnu
cargo build --release --target aarch64-unknown-linux-gnu

# or, for targets needing a cross-toolchain (Android, musl, ARM, ...):
cargo install cross --git https://github.com/cross-rs/cross
cross build --release --target aarch64-linux-android

Termux (Android)

Termux runs standard Android NDK (*-linux-android) binaries directly, since Termux itself targets bionic libc the same way an NDK build does. Download the aarch64-linux-android archive (or armv7-linux-androideabi for older 32-bit devices) from Releases, then:

tar xzf colorls-*-android*.tar.gz
cd colorls-*-android*
chmod +x colorls
mv colorls $PREFIX/bin/
colorls --init-config

Owner/group columns in -l show numeric uid/gid on Android/Termux instead of resolved names (bionic doesn't expose the conventional multi-user passwd/group database glibc/musl provide, so there's no name to resolve). --init-config, git status (--gs, if git is installed via pkg install git), and everything else behaves identically to the Linux build.


Every build produces two identical binaries: colorls and lls. Same code, same flags, same everything — lls is just a shorter, collision-free name for the common case of an old Ruby colorls/colorls.bat already sitting on PATH (as e.g. where colorls on Windows would show if you've ever gem install colorls'd). --help/--version on each reports its own name, not the other one's.

Copy target/release/colorls and/or target/release/lls (.exe on Windows) anywhere on PATH. There is no installer and no required config — it works with sane defaults out of the box. To alias it over the real ls/dir, add to your shell profile:

alias ls='lls'
alias ll='lls -l'
alias la='lls -a'
alias lt='lls --tree'

On Windows (PowerShell profile):

function ls { lls @args }
function ll { lls -l @args }

Usage

colorls [OPTIONS] [PATHS]...
Flag Meaning
-a, --all show hidden entries, including . and ..
-A, --almost-all show hidden entries, without ./..
-l, --long long listing: permissions, owner, size, date
-1, --oneline one entry per line
--tree[=N] tree view, default depth 3
--gs, --git-status show per-entry git status column, plus a branch header above the listing
--sd, --group-directories-first directories before files
--sf, --sort-files files before directories
-t sort by modification time, newest first
-S, --sort-size sort by size, largest first
-X, --sort-extension sort by extension
-r, --reverse reverse the active sort order
-R, --recursive recurse into sub-directories (flat, ls -R style)
--light / --dark color theme
--report print a directory/file/size summary after listing
--no-icons / --icons disable/force-enable Nerd Font icons
`--color <auto always
--config <PATH> use a specific config dir (or its config.yaml)
--init-config write default config files, without clobbering existing ones
--print-config-dir print the resolved config directory and exit
-q, --quiet suppress non-fatal warnings
-v, -vv increase verbosity
-p, --paginate page output through $PAGER (or less -R), preserving color

Run colorls --help for the full, always-up-to-date list.

Nerd Font required for icons. If your terminal font doesn't include Nerd Font glyphs, icons render as boxes/?. Either install a Nerd Font, or run with --no-icons.

Piping and paging colored output

By default, colorls/lls disables color whenever stdout isn't a terminal — same convention as ls --color=auto, grep --color=auto, etc. So lls -rt | more showing no color is expected, not a bug: more (especially the classic Windows more.com) doesn't understand ANSI color codes at all, and even tools that do (like less) will show the raw ^[[92m... escape sequences as garbage text instead of color unless told to interpret them.

Two ways to get colored output through a pipe:

-p / --paginate (recommended) — spawns $PAGER (or less -R if $PAGER isn't set) itself, forces color on for it specifically, and pages interactively:

lls -rt -p

If no usable pager is found on PATH (common on a bare Windows install without less.exe), it prints a one-line warning and falls back to plain output rather than risking unreadable escape-code garbage.

Manual piping — if you'd rather manage your own pager pipeline, force color explicitly and make sure your pager is told to interpret raw control characters:

lls -rt --color=always | less -R

Do not pipe --color=always output into more (or less without -R) — both will show literal escape codes instead of color, which is exactly what "chaos" looks like. -p/--paginate avoids this entirely by using less -R as its own default.

Configuration

colorls looks for a config directory in this order:

  1. --config <path> (a directory, or a config.yaml file inside one)
  2. $COLORLS_CONFIG environment variable
  3. Platform default:
    • Linux: ~/.config/colorls
    • macOS: ~/Library/Application Support/colorls
    • Windows: %APPDATA%\colorls
    • Android/Termux: $HOME/.config/colorls (Termux's own $HOME, since the platform's usual directory-resolution APIs need JNI and aren't available to a plain native binary)

Run once to scaffold it:

colorls --init-config

This writes eight files (only the ones missing — existing files are never overwritten):

File Purpose
config.yaml default behaviour (theme, icons on/off, git status on/off, sort options, --report, tree depth)
dark_colors.yaml / light_colors.yaml category → color name, e.g. dir: bright_cyan
icons.yaml file extension → Nerd Font glyph
filenames.yaml exact filename (e.g. dockerfile, readme.md) → glyph, takes priority over extension
folders.yaml folder name (e.g. node_modules, .git) → glyph
aliases.yaml file extension → color category (source_code, image, document, ...)
extension_colors.yaml file extension → color name directly, highest priority (see below)

Any key you add to these files overrides the built-in default for that key only — you don't need to redefine everything, just the entries you want to change. Example, to make .rs files render in bright red instead of the default source-code color:

# ~/.config/colorls/aliases.yaml
rs: dead_link   # (or add a new category + matching color entry)

or simpler, just recolor an entire category in dark_colors.yaml:

source_code: bright_red
dir: "#00FFFF"
document: FFFF00

Coloring one specific extension without touching its whole category

aliases.yaml + dark_colors.yaml/light_colors.yaml color by category (all "compressed" files share one color, all "source_code" files share another). If you want a single extension to have its own color without inventing a whole new category, use extension_colors.yaml instead — it's checked first, before category resolution even runs:

# ~/.config/colorls/extension_colors.yaml
zip: bright_red
jar: bright_green
bz2: bright_yellow
gz: yellow
log: bright_black
7z: "#FF5500"
rar: 00AAFF

This is exactly how the built-in defaults keep archive formats visually distinct from each other out of the box (.zip/.rar/.7z red, .gz/ .bz2/.xz yellow, .tar magenta, .jar green) instead of every archive type sharing the single "compressed" category color. Any extension not listed here just falls back to its category color as usual.

Hex colors

Anywhere a color name is accepted — dark_colors.yaml, light_colors.yaml, and extension_colors.yaml — you can use a 24-bit hex color instead of a named one:

# ~/.config/colorls/extension_colors.yaml
zip: FF8800        # bare hex — recommended, see gotcha below
jar: "#00FF88"      # quoted hex also works
log: "#666"          # 3-digit shorthand, quoted (each digit doubled: 666 -> 666666)

YAML gotcha: an unquoted # starts a comment, so zip: #FF0000 silently parses as an empty value, not the color you meant — colorls will warn you about exactly this (without crashing or discarding the rest of the file) if it happens. Either quote the value (zip: "#FF0000") or drop the # entirely (zip: FF0000); both are treated identically.

Hex colors always render as exact true 24-bit color (\e[38;2;r;g;bm) wherever the terminal honors 24-bit ANSI — which is effectively every terminal still receiving updates in 2026. Unlike most tools that check the COLORTERM environment variable and silently substitute the nearest of 16 named colors when it's absent, colorls does not do that downgrade for explicitly-configured hex colors: COLORTERM is an unreliable signal in practice (frequently missing over SSH, in tmux, or other passthrough shells even when the terminal displaying the output supports truecolor fine), and a color you explicitly typed as hex should render as exactly that color, not a guess.

config.yaml example:

theme: dark
icons: true
git_status: true
group_directories_first: true
sort_files_first: false
report: false
tree_depth: 4
long: false
all: false

Every config.yaml key has a matching CLI flag; the CLI flag always wins when both are given, so config.yaml only sets your personal defaults.

Design notes

  • Git status shells out to git status --porcelain=v1 --ignored rather than linking libgit2; this keeps the build dependency-free and avoids the native-toolchain headaches libgit2-based crates cause when cross-compiling for Windows. --gs is silently disabled (with a one-line warning, suppressible with -q) when git isn't on PATH or the target isn't inside a work tree.
  • Broken pipes (colorls | head, output redirected then closed early) exit cleanly instead of panicking — all output goes through a small writer that treats BrokenPipe as a normal, silent exit(0).
  • Color is force-synced with colored::control::set_override so --color=always/--color=never are honored even when stdout isn't a TTY (piped into head, redirected to a file, etc.) instead of being silently overridden by the crate's own auto-detection.
  • Owner/group columns are populated on Unix via the users crate and show - on Windows, which has no equivalent POSIX uid/gid concept.
  • -p/--paginate redirects all output through a spawned pager's stdin pipe instead of real stdout (see util::init_output_writer / close_output in src/util.rs), and upgrades --color=auto to --color=always for the duration — since the destination is no longer the terminal directly, plain TTY detection would otherwise (correctly, in isolation) strip color right before it reaches the pager.

👤 Author

Hadi Cahyadi

Buy Me a Coffee

Donate via Ko-fi

Support me on Patreon