Skip to content
fxrdhanPublic
forked from eza-community/eza

About

A modern alternative to ls. Oh, and ofc it's faster than eza tbh.

Resources

Code of conduct

Contributing

Security policy

Stars

12 stars

Watchers

0 watching

Forks

 
 

Latest commit

 

History

3,877 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

lez

A modern alternative to ls. Oh, and ofc it's faster than eza tbh.

License Rust binary cache Coverage

lez demo


lez is a fast, modern file-listing command-line tool with smart defaults, enhanced file icons, Git integration, and continuous performance improvements.

  • Fast & Lightweight: Written in modern Rust (2024 Edition) with multithreaded directory scanning via Rayon.
  • Rich Visuals: Colors by file type, colored CLI help output, Nerd Font icons, and automatic luminance color scaling.
  • Git Integration: View file and repo status (Modified, Untracked, Ignored, etc.) directly in the file listing.
  • Built-in Tree View: Hierarchical directory tree out of the box (lez --tree).
  • Structured Data Export: --json writes the listing, and with -l every long-view column, as JSON for scripts.
  • Archive Inspection: Inspect files inside .tar and .zip archives directly in the long view (lez -l --inspect-archives).
  • Lines-of-Code Counter: Comment-aware LOC breakdowns for 100+ programming languages (lez --code).
  • Deep OS Integration: Native macOS Finder color tags, Linux capability decoding (security.capability), and Windows PATHEXT executables.

Performance

Measured on lez v0.28.2 against eza v0.23.5 (the upstream release it forked from) and lsd v1.2.0. Apple M1 Pro, 10 cores, macOS; release builds, warm page cache, minimum of 35 interleaved runs per binary.

Workload lez 0.28.2 eza 0.23.5 lsd 1.2.0 vs eza vs lsd
Grid view, 10,000 files 22.4 ms 77.4 ms 465.1 ms 3.45× 20.7×
Recursive tree, 30,000 files 73.6 ms 205.8 ms 1,217.0 ms 2.80× 16.5×
Directory-grouped, 10,000 entries incl. 3,000 symlinks 49.9 ms 146.5 ms 433.3 ms 2.94× 8.69×
Long view, 10,000 files 158.4 ms 215.5 ms 439.0 ms 1.36× 2.77×
Long view with Git status, this repository 18.3 ms 21.7 ms 20.1 ms 1.19× 1.10×

Where the gains come from: metadata is cached lazily via OnceLock and symlink directory lookups use lock-free atomic memoization, so the grouping sort resolves each entry once instead of on every comparison; sorts above 2,048 entries run in parallel on all cores; syscall amplification is minimized by evaluating in-memory styles before probing disk; and traversal elides redundant heap allocations and stats.

Note also that lez sorts through an ICU collator by default, which orders accents, case, and digit runs correctly and costs more than a byte comparison. --sort=lexicographic opts out of it and is faster still.

Memory Footprint (Peak RSS)

Measured peak resident memory when scanning 10,000 files in a flat directory (/usr/bin/time -l on macOS):

Metric lez 0.28.2 lsd 1.2.0 eza 0.23.5 Memory Savings
Peak Resident Set Size (RAM) ~1.0 MB ~14.0 MB ~27.4 MB 27× lighter than eza (14× vs lsd)

lez eliminates unnecessary heap allocations through zero-copy path streaming and lazily evaluated metadata via OnceLock. On resource-constrained systems (Docker CI runners, low-memory VPS instances, embedded systems), memory overhead remains virtually negligible.

Reproducing

# 10,000 files in one flat directory
mkdir -p /tmp/bench && cd /tmp/bench && seq 1 10000 | xargs -I{} touch file_{}.txt

hyperfine --warmup 3 'lez /tmp/bench' 'eza /tmp/bench' 'lsd /tmp/bench'
hyperfine --warmup 3 'lez -l /tmp/bench' 'eza -l /tmp/bench' 'lsd -l /tmp/bench'
hyperfine --warmup 3 'lez --tree ~/.cargo/registry' 'eza --tree ~/.cargo/registry' 'lsd --tree ~/.cargo/registry'

eza requires --icons=auto where lez accepts a bare --icons; passing a bare --icons to eza before a path makes it reject the path as an invalid value, which produces a misleadingly fast result.


Feature Matrix

Feature / Capability lez eza lsd
Nerd Font Icons & Color Themes ✅ ✅ ✅
Directory Tree View (--tree) ✅ ✅ ✅
Hyperlink Support (--hyperlink OSC 8) ✅ ✅ ✅
Custom Column Order (lsd --blocks) ❌ ❌ ✅
Classic GNU ls Mode (--classic) ❌ ❌ ✅
Unicode Emoji Fallback (--icon-theme unicode) ❌ ❌ ✅
Multithreaded Traversal (Rayon Engine) ✅ ⚠️ Limited ❌
Lines-of-Code Counter (--code, --loc) ✅ 100+ langs ✅ 50+ langs ❌
Structured JSON Export (--json) ✅ ❌ ❌
Archive Inspection (--inspect-archives for .tar and .zip) ✅ ❌ ❌
Time-Window Filtering (--since) ✅ ❌ ❌
Size Precision Formatting (--size-digits) ✅ ❌ ❌
Nix Store Hash Abbreviation (--short-nix) ✅ ❌ ❌
Global & Per-Directory Config Files (config.toml / .lez.toml) ✅ ❌ ✅
Deep Git Integration (--git, --git-glyphs, --git-repos) ✅ ✅ ⚠️ Basic
macOS Finder Color Tags (-e, --tags) ✅ ✅ ❌
File Flags & Attributes (-O, --flags) ✅ ⚠️ BSD/Win only ❌
Filesystem Mount Points (-M, --mounts) ✅ ✅ ❌
Automatic Color Scaling & Heatmap (--color-scale) ✅ ✅ ❌

lez is available for macOS, Linux, and Windows. INSTALL.md covers every channel, plus shell completions and the man pages.

Homebrew (macOS & Linux)

Install from the official fxrdhan tap:

brew install fxrdhan/tap/lez

Standalone Shell Installer (Linux & macOS)

Download and install the prebuilt binary to ~/.local/bin in one step:

curl -fsSL https://raw.githubusercontent.com/fxrdhan/lez/main/packaging/install.sh | bash

Cargo / crates.io

Install lez from crates.io:

cargo install lez

Or install precompiled binary quickly with cargo-binstall:

cargo binstall lez

Or track the latest commit on main:

cargo install --git https://github.com/fxrdhan/lez.git

Or build from source:

git clone https://github.com/fxrdhan/lez.git
cd lez
cargo build --release
# Binary available at target/release/lez

Nix

If you already have Nix setup with flake support, you can try out lez with the nix run command:

nix run github:fxrdhan/lez

Nix will build lez and run it.

If you want to pass arguments this way, use e.g. nix run github:fxrdhan/lez -- -la --icons.

Binary Cache

Every commit on main is validated with nix flake check in CI, and the resulting store paths are pushed to a public binary cache on Cachix. Contributors and Nix users can pull prebuilt outputs instead of compiling from scratch:

cachix use lez
nix run github:fxrdhan/lez

Performance & Validation:

Scenario What Happens Duration
Warm Run / Downstream Users Full closure substituted directly from Cachix ~33 s (Instant download)
Cold Build (Building from Source) Single-pass compilation in isolated Nix sandbox ~7 min

Note on Caching: Nix store paths are strictly content-addressed. Builds with modified source code produce a new derivation hash and compile inside the Nix sandbox, while unchanged closures, downstream nix develop environments, and CI runs on main enjoy the ~33 s binary substitution. A weekly cold-build canary keeps the flake honest against upstream bitrot.


Quick start

lez takes the place of ls. A few everyday commands:

lez                     # the current directory, as a grid
lez -la                 # the long view, hidden files included
lez -lt                 # the long view, newest first, as `ls -lt` does
lez -l --git            # the long view, with each file's Git status
lez -T -L2              # a tree, two levels deep
lez -l --sort=size -r   # the long view, largest first
lez --icons             # icons beside the names (needs a Nerd Font)
lez --code              # lines of code per language, across the tree

To have ls run lez in bash, zsh or fish, add alias ls=lez to your shell's startup file. Every option is listed below, and in the lez(1) man page (man lez).


lez’s options are intuitive and familiar. Quick overview:

Display options

Click to expand
  • -1, --oneline: display one entry per line
  • -G, --grid: display entries as a grid (default)
  • -l, --long: display extended details and attributes
  • -R, --recurse: recurse into directories
  • -T, --tree: recurse into directories as a tree
  • --follow-symlinks: drill down into symbolic links that point to directories
  • --code[=MODE]: print lines-of-code summary by language (modes: lines, percent, both)
  • --json: output file listing and metadata as structured JSON (the format is described under JSON OUTPUT in the lez(1) manual page)
  • --explain: list each entry with the rule that chose the color of its name (a glob in LS_COLORS or LEZ_COLORS, a theme entry, a built-in file type, di, ex...) and the one that chose its icon
  • -x, --across: sort the grid across, rather than downwards
  • -F, --classify[=(when)]: display type indicator by file names (always, auto, never)
  • --color=(when), --colour=(when): when to use terminal colors (always, auto, never)
  • --color-scale=(fields), --colour-scale=(fields): highlight levels of fields distinctly (all, age, size)
  • --color-scale-mode=(mode), --colour-scale-mode=(mode): use gradient or fixed colors in --color-scale (fixed or gradient)
  • --theme=(name): use the theme name from the themes folder of the configuration directory instead of theme.yml (also LEZ_THEME, or name under [theme] in the configuration file)
  • --icons[=(when)]: when to display icons (always, auto, never; requires '=' if value provided). The icons are Nerd Font glyphs, so the terminal has to use a Nerd Font; with any other font they show as boxes or question marks
  • --spacing=(spaces): number of spaces between columns (default: 2 in the grid views, 1 in the long view; at most 1000)
  • --no-symlink-targets: do not show symlink targets (the -> ...)
  • --quotes=(when): when to quote file names (always, auto, never; requires '=' if value provided)
  • -N, --literal: print file names without quoting, as ls -N does (the same as --quotes=never)
  • --summary: display total summary statistics of entries (directories, files, symlinks, and total)
  • --hyperlink[=(when)]: when to display entries as hyperlinks (always, auto, never; requires '=' if value provided)
  • --absolute=(mode): display entries with their absolute path (on, follow, off)
  • --short-nix: abbreviate Nix store hashes in file names and paths
  • --print-total: print the total number of files and directories listed
  • --mime-types: determine file MIME types to better inform styling decisions (unix only)
  • -w, --width=(columns): set screen width in columns (clamped to 1..65535)

Filtering options

Click to expand
  • -a, --all: show hidden and 'dot' files (use twice to also show . and ..)
  • -A, --almost-all: equivalent to --all; included for compatibility with ls -A
  • --show-dotfiles: show dot-prefixed files without showing other hidden files
  • -d, --treat-dirs-as-files: list directories like regular files
  • -L, --level=(depth): limit the depth of recursion
  • -r, --reverse: reverse the sort order
  • -s, --sort=(field): which field to sort by; the path field accepts the aliases relative-path, relpath, and relative_path (capitalised variants sort uppercase first)
  • -t: sort by modification time, newest first (GNU ls compatibility; shorthand for --sort=age). Followed by a time field, as in -t modified or -t=accessed, it is the short form of --time instead, and picks the timestamp field to display
  • --group-directories-first: list directories before other files
  • --group-directories-last: list directories after other files
  • --group-dotfiles-first: list dotfiles before other files, within the directory groups
  • -D, --only-dirs: list only directories
  • -f, --only-files: list only files
  • --no-symlinks: don't show symbolic links
  • --show-symlinks: explicitly show links (with --only-dirs and --only-files)
  • --no-system: don't show system files (Windows only)
  • --no-hidden-attrib: don't show entries with the hidden attribute (Windows only)
  • --no-hidden-links: don't show hidden profile links and junctions (Windows only)
  • --git-ignore: ignore files mentioned in .gitignore
  • -W, --warn-hidden: print a tally of hidden and gitignored entries; give twice to always print it
  • --cachedir-ignore: ignore directories containing a CACHEDIR.TAG file
  • --ignore-submodule-contents: don't list the contents of Git submodules
  • --since=(duration): filter and display only files created or modified within the specified duration window (e.g. 10m, 1h, 2d, 1w)
  • -I, --ignore-glob=(globs): glob patterns (pipe-separated) of files to ignore; patterns containing / match against paths relative to the listing root and the flag may be given multiple times
  • --ignore-glob-ci=(globs): glob patterns (pipe-separated) of files to ignore (case-insensitive)

Long view options

Click to expand

These options are available when running with --long (-l):

  • -b, --binary: list file sizes with binary prefixes (overrides --bytes if passed after)
  • -B, --bytes: list file sizes in bytes, without any prefixes (overrides --binary if passed after)
  • --size-digits=(NUM), --digits=(NUM): number of digits to display for file sizes, the decimal point counting as one (1..=8, default: 3, as in 2.3M; also configurable via LEZ_SIZE_DIGITS)
  • --percent-digits=(NUM), --precision-percent=(NUM): number of decimal digits for code shares in --code and --loc (0..=8, default: 1; also configurable via LEZ_PERCENT_DIGITS)
  • -g, --group: list each file’s group
  • --smart-group: only show group if it has a different name from owner (automatically enables group column)
  • -n, --numeric: show user and group as their numeric IDs
  • --owner-width=(COLS): cut user and group names wider than COLS columns, ending them in an ellipsis (firstname.lastname becomes firstna… under --owner-width=8); numeric IDs and --json are left whole
  • -h, --header: add a header row to each column
  • -H, --links: list each file’s number of hard links
  • -i, --inode: list each file’s inode number
  • --loc[=MODE]: display language and lines-of-code columns (modes: lines, percent, both)
  • -m, --modified: use the modified timestamp field
  • -M, --mounts: show mount details (Linux and macOS only)
  • -S, --blocksize: list the allocated size of each file, in bytes
  • --blocks: list the allocated size of each file, in blocks
  • -t, --time=(field): which timestamp field to use (modified [aliases: mod, m], accessed [acc], changed [ch], created [cr])
  • -u, --accessed: use the accessed timestamp field
  • -U, --created: use the created timestamp field
  • --changed: use the changed timestamp field
  • --utc: show timestamps in the UTC timezone
  • -X, --dereference: dereference symlinks for file information and sorting
  • -Z, --context: list each file’s security context
  • -O, --flags: list file flags / attributes (Linux, macOS, BSD, and Windows)
  • -@, --extended: list each file’s extended attributes and sizes
  • --no-extended: don't show the @ marker that a file has extended attributes
  • -e, --tags: list each file's color tags stored in extended attributes (macOS Finder tags)
  • --inspect-archives: list the contents of supported archives (.tar, .zip) in long view, with each entry's file name colored by type
  • --git: list each file’s Git status, if tracked or ignored
  • --git-glyphs: display Git status with Nerd Font glyphs instead of ASCII characters
  • --git-repos: list each directory’s Git status, if tracked
  • --git-repos-no-status: list whether a directory is a Git repository, but not its status (faster)
  • --no-git: suppress all Git fields and .gitignore handling (overrides --git, --git-repos, --git-repos-no-status, --git-ignore)
  • --time-style: how to format timestamps. Valid styles: default, iso, long-iso, full-iso, relative, relative-recent, or custom +<FORMAT> (e.g., +%Y-%m-%d %H:%M; see lez(1) manual page and chrono format).
  • --total-size: show recursive directory size
  • -o, --octal-permissions: list each file's permission in octal format
  • --no-permissions: suppress the permissions field
  • --no-filesize: suppress the filesize field
  • --no-user: suppress the user field
  • --no-time: suppress the time field
  • --no-language: suppress the language field when --loc is enabled

Some of the options accept parameters:

  • Valid --colo[u]r options are always, automatic (or auto for short), and never.
  • Valid sort fields are accessed, changed, created, extension, Extension, inode, lexicographic, Lexicographic, modified, name, Name, path, Path, size, block, type, and none. Fields starting with a capital letter sort uppercase before lowercase. The modified field has the aliases date, time, mod, old, and oldest, while its reverse has the aliases age, new, and newest. The block field has the aliases blocks and blocksize. The lexicographic field has the aliases lex and lg, and compares names code point by code point — no natural ordering of digit runs, no locale collation — so Lexicographic matches ls under the C locale.
  • Valid time fields are modified, changed, accessed, and created.
  • Valid time styles are default, iso, long-iso, full-iso, relative, relative-recent (relative times for the last 7 days, the default style before that; relative-recent:DAYS sets the window), and a custom +FORMAT.

See the man pages for further documentation of usage. They are available:

Meta options

Click to expand

These options work with every view:

  • --stdin: read file names from stdin
  • --stdin0: read NUL-separated file names from stdin (e.g. find -print0 | lez --stdin0)
  • --config: load default options from specified configuration file (.toml, .yaml, or .yml)
  • --no-config: do not load any global or per-directory configuration files, nor a theme file other than one named by --theme

Configuration Files

Click to expand

lez supports structured configuration files (TOML or YAML) to set default flags without needing cumbersome shell aliases or wrapper scripts.

Discovery & Precedence

  1. CLI Flag: --config <PATH> or --no-config
  2. Environment Variable: LEZ_CONFIG_FILE
  3. Local (per-directory) Config: .lez.toml, .lez.yaml, .lez.yml, .eza.toml, .eza.yaml in the current working directory
  4. Global Config: config.toml, lez.toml, config.yaml, config.yml in $LEZ_CONFIG_DIR (or $XDG_CONFIG_HOME/lez, or else ~/.config/lez on Linux, ~/Library/Application Support/lez on macOS, %APPDATA%\lez on Windows)
  5. Built-in Defaults

Example config.toml

[display]
header = true
time_style = "relative-recent"

[icons]
icons = "auto"
spacing = 1

[filter]
git_ignore = true

[git]
git_glyphs = true

An annotated sample configuration is provided in docs/config.example.toml.

Environment Variables

Click to expand

lez supports several environment variables to configure default behavior, styles, and integrations:

Variable Description
LEZ_CONFIG_FILE / EZA_CONFIG_FILE Explicit path to a configuration file to load (.toml, .yaml, or .yml).
LEZ_CONFIG_DIR / EZA_CONFIG_DIR Directory containing config.toml and theme.yml (default: $XDG_CONFIG_HOME/lez, or else ~/.config/lez on Linux, ~/Library/Application Support/lez on macOS, %APPDATA%\lez on Windows).
LEZ_THEME / EZA_THEME Name of a theme in the themes folder of the configuration directory, used instead of theme.yml (--theme overrides it).
LEZ_COLORS / EZA_COLORS / LS_COLORS Specifies color styles and file extensions styling using standard terminal ANSI escape codes. LS_COLORS is not read when a theme.yml is.
LEZ_MIN_LUMINANCE / LEZ_MAX_LUMINANCE Minimum and maximum luminance values (-100..=100) for color scaling on dates and sizes.
LEZ_QUOTING_STYLE / EZA_QUOTING_STYLE Default quoting style for filenames with spaces/special characters (always, auto, never).
QUOTING_STYLE GNU ls's quoting style, read when neither variable above is set: literal is never, shell and shell-escape are auto, and shell-always and shell-escape-always are always. Its other styles are passed over.
LEZ_ICON_SPACING / EZA_ICON_SPACING Number of spaces to insert after Nerd Font icons (default: 1).
LEZ_NO_EMPTY_DIR_ICON / EZA_NO_EMPTY_DIR_ICON Set to anything to give every directory the same icon. Distinguishing an empty one costs a filesystem round trip per directory, which is slow on FUSE and network mounts.
LEZ_STDIN_SEPARATOR / EZA_STDIN_SEPARATOR Delimiter for paths read from standard input with --stdin; ignored by --stdin0 (default: newline \n). Supports escape sequences (e.g. \0, \n, \t, \x00) and null/nul.
LEZ_SIZE_DIGITS / EZA_SIZE_DIGITS Default number of digits (1..=8) to display for formatted file sizes, the decimal point counting as one (default: 3).
TIME_STYLE Default timestamp format style (default, iso, long-iso, full-iso, relative, relative-recent, or +<FORMAT>), also read in GNU ls's locale and posix-<STYLE> forms; any other value is an error.
NO_COLOR Turns colors off when set to anything but an empty string; --color=always still turns them on.

Custom Themes & Schema Validation

Click to expand

lez supports a theme.yml file, where you can customize theme options available for the LS_COLORS, EZA_COLORS, and LEZ_COLORS environment variables, as well as specify custom icons for different file types and extensions.

An example theme file is available in docs/theme.yml, and can be placed in a directory specified by $LEZ_CONFIG_DIR, $EZA_CONFIG_DIR, or looked for by default in the configuration directory: $XDG_CONFIG_HOME/lez, or else ~/.config/lez on Linux, ~/Library/Application Support/lez on macOS and %APPDATA%\lez on Windows (an existing eza directory there is used when there is no lez one).

To keep several themes and switch between them, put each in a themes folder in the configuration directory, as themes/NAME.yml or themes/NAME.yaml, and pick one with --theme=NAME, LEZ_THEME=NAME, or name = "NAME" under [theme] in the configuration file, in that order. A named theme replaces theme.yml, and naming one that is not there is an error. A name holding a directory, such as --theme=./night.yml, is the path to the theme file.

A theme file takes the place of LS_COLORS, which the system's dircolors often sets for every program, so LS_COLORS is not read beside one. LEZ_COLORS and EZA_COLORS are still laid over the theme.

Schema Validation in IDEs

You can enable autocomplete and schema validation in VSCode, Neovim, or Zed by referencing the bundled JSON Schema at the top of your theme.yml:

# yaml-language-server: $schema=https://raw.githubusercontent.com/fxrdhan/lez/main/docs/theme-schema.json

filekinds:
  directory: { foreground: Blue, is_bold: true }
  symlink: { foreground: Cyan }

Full styling details are available in the lez_colors-explanation(5) man page and lez_colors(5) man page.

Contributing to lez

If you want to contribute to lez, please check out our:

  • Code of Conduct
  • CONTRIBUTING.md for development guidelines.
  • AGENTS.md for the architecture, the source layout, the conventions, and the steps for adding a flag, an environment variable, an icon or a language.
  • TESTING.md for running and updating the tests.

Lineage & Credits

lez is a fork of eza, which is itself a maintained fork of exa by Benjamin Sago. The overwhelming majority of this codebase was written by Benjamin Sago, Christina Sørensen, and the eza contributors, and lez continues to port work from eza's open pull requests and issues.

  • exa — original implementation, by Benjamin Sago (ogham/exa), MIT
  • eza — community fork, maintained by Christina Sørensen (eza-community/eza), EUPL-1.2
  • lez — this fork, by fxrdhan, EUPL-1.2

lez is licensed under the EUPL-1.2, inherited from eza; files originating in exa remain under the MIT license, as recorded in their SPDX headers.

The name

This project was called lsr through v0.24.1, until it turned out rockorager/lsr and the lsr crate already held that name.

About

A modern alternative to ls. Oh, and ofc it's faster than eza tbh.

Resources

Code of conduct

Contributing

Security policy

Stars

12 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages