# z.lua: a frecency-based cd replacement distributed as a single Lua script

> z.lua tracks the directories you visit most and jumps to them with ordered regex matching. It is one file, needs a Lua interpreter, and supports bash, zsh, fish, Nushell, PowerShell, cmd and WSL.

**skywind3000/z.lua** — :zap: A new cd command that helps you navigate faster by learning your habits.

- Repository: https://github.com/skywind3000/z.lua
- Stars: 3,148 · Forks: 148
- Language: Lua
- License: MIT
- Published: 2026-09-24 · Updated: 2026-09-24 · Language: en
- Canonical page: https://hysenlabs.com/projects/skywind3000-z-lua

## What z.lua replaces, and who it is actually for

Typing absolute paths is the tax you pay for working across several projects. z.lua removes most of that tax by keeping a database of directories you have visited and ranking them by frecency, a blend of how often and how recently you were there. The README calls it "a faster way to navigate your filesystem" and describes the matching rule precisely: z takes you to the most frecent directory that matches all the regexes given on the command line, in order. So `z foo bar` matches `/foo/bar` but not `/bar/foo`. Order matters, which is a real difference from tools that treat the arguments as an unordered set.

The audience is people who live in a terminal and dislike maintaining aliases. The README quotes users on exactly that point, including one who describes being "far too lazy to make shortcuts". The other audience is people on constrained machines. One quoted user mentions wanting autojump-like behaviour on a Raspberry Pi 1 "without waiting 30 seconds every time I open a new shell". That framing matters: the project is not only about fewer keystrokes, it is also about shell startup cost, and the README claims z.lua is 10x faster than fasd and autojump and 3x faster than z.sh.

## How the frecency database and ordered matching work

z.lua is distributed as a single script, z.lua, with no other dependency beyond a Lua interpreter. The shell integration is generated at runtime: you invoke the script with `--init` plus a shell name, and it prints shell code that you `eval` or `source`. That generated code installs a hook that records the current directory, and defines the `z` function that queries the database.

The database lives in a datafile, `~/.zlua` by default, and the environment variable `$_ZL_DATA` changes it. The README documents `$_ZL_MAXAGE` as the aging threshold, default 5000, which is what keeps old visits from dominating the ranking forever. Writing on every prompt has a cost, so there is `$_ZL_ADD_ONCE`: set it to 1 and the database is updated only when `$PWD` changed. That is the single most useful knob if you have a prompt that fires often without you moving anywhere.

Matching is not a fuzzy substring search by default. The README says the default algorithm is similar to z.sh "to keep compatible", and that an enhanced matching algorithm is available as an option. Both are selected at init time, which means changing your mind later is a matter of editing the init line, not a config file. Beyond plain matching there are flags: `-r` for highest ranked, `-t` for most recently accessed, `-l` to list instead of cd, `-c` to restrict matches to subdirectories of `$PWD`, `-e` to echo the best match without changing directory, `-i` for interactive selection, `-I` for interactive selection through fzf, and `-b` for parent-directory jumping, including the `z -b foo bar` form that replaces foo with bar in the current path and goes there.

## Installing z.lua in bash, zsh, fish or PowerShell

The install pattern is the same everywhere: generate shell code and evaluate it. For bash, the README puts this in `.bashrc`, and it is the minimal form with the z.sh-compatible matcher.

```bash
eval "$(lua /path/to/z.lua --init bash)"
```

After a new shell, `z foo` should jump to the most frecent directory matching foo. The README then suggests the enhanced matcher plus the once-per-directory database update, and optionally echoing the new directory after the jump.

```bash
eval "$(lua /path/to/z.lua --init bash enhanced once echo)"
```

fzf integration is the same line with one more word, which adds tab completion backed by fzf.

```bash
eval "$(lua /path/to/z.lua --init bash enhanced once fzf)"
```

For zsh the command is identical with `zsh` instead of `bash`, placed in `.zshrc`, and the README notes it can also be initialized through zsh plugin managers such as antigen or oh-my-zsh from the `skywind3000/z.lua` repository. Fish is different: you create `~/.config/fish/conf.d/z.fish` containing a pipe into `source`.

```bash
lua /path/to/z.lua --init fish | source
```

If you want z.lua to cooperate with fish's own directory history, the README says to add `set -gx _ZL_CD cd` to the same file. Nushell needs two steps, writing the generated script to a cache file and then sourcing it from `config.nu`, and the README states that only Nushell v0.96+ is supported.

```bash
lua /path/to/z.lua --init nushell | save -f ~/.cache/zlua.nu
```

PowerShell uses `Invoke-Expression` in `profile.ps1`, and the README carries a warning that Starship Prompt users must place that command after `starship init`. Windows users on cmd or cmder copy `z.lua` and `z.cmd` into the tool's directory, add it to `%PATH%`, and make sure `lua` itself is callable. NixOS users with home-manager get a declarative path instead, with `programs.z-lua.enable`, a per-shell integration flag such as `enableBashIntegration`, and `programs.z-lua.options` taking a list like `[ "enhanced" "once" "fzf" ]`.

## The Lua interpreter is a real dependency, and WSL 1 needs more

Calling z.lua self contained is accurate about the project's own files and misleading about the machine. The script needs a Lua interpreter on `%PATH%` or in the shell's path, and the README states compatibility with Lua 5.1, 5.2, 5.3+ and luajit. That is a wide range, but it is still an interpreter you have to install and keep present, and on Windows cmd or cmder the README lists "ensure that lua can be called" as an explicit setup step. A tool distributed as a compiled binary does not have this problem.

The sharper limitation is WSL 1. The README says `lua-filesystem` must be installed there, via `sudo apt-get install lua-filesystem`, and attributes the requirement to a WSL defect (microsoft/WSL issue 5505). This is not optional polish. If you are on WSL 1 and skip it, you are outside the configuration the README describes. WSL 2 users are not told they need this, but the README does not say WSL 2 is exempt either; it only names wsl-1.

There is also a shell-coverage caveat hiding in the posix instructions. The README says old shells like ksh have missing features and suggests `--init posix legacy` to generate an older posix-compatible script. Legacy mode is a reduced feature set, not a compatibility shim that preserves everything. If your environment is busybox or an old ksh, expect less than the feature list promises, and the README does not enumerate what legacy mode drops.

## z.lua compared with zoxide and the tools it names

The README frames z.lua as "an alternative to z.sh with windows and posix shells support and various improvements", and the performance claims are stated against fasd, autojump and z.sh. The structural difference from z.sh is the implementation language. z.sh is a shell script, so every invocation is parsed by the shell itself; z.lua is a Lua script, so the matching and ranking work happens in the interpreter and only the resulting shell code is evaluated. That is the mechanism behind the speed claim, and it is also why the interpreter dependency exists.

zoxide is the other common comparison, and the difference is packaging philosophy rather than behaviour. zoxide is a compiled binary, so it installs without a Lua runtime and does not depend on which Lua version the distribution ships. z.lua is one text file you can commit to a dotfiles repository, read end to end, and patch. If your constraint is "nothing to compile and no package manager", z.lua wins. If your constraint is "no interpreter to provision on 200 machines", a compiled tool wins. Both rank directories by frecency, and both support fuzzy interactive selection, so the decision usually comes down to that provisioning question rather than to the matching algorithm.

One more distinction worth noting: z.lua's optional native module, czmod, is written in C and linked separately. The README presents it as a way to "gain the ultimate speed". The moment you adopt it, you have taken on a compiled artifact and given up the single-file property.

## Maintenance, licensing and what upgrading costs you

The repository is not archived, and the last push was on 2026-08-10, which is the same day as the 1.8.26 release. The two releases before it are dated 2026-03-09 and 2025-05-24, so the cadence across those three tags is roughly quarterly to semiannual rather than continuous. That is normal for a tool whose core behaviour is settled, but it means you should not expect rapid responses to shell-specific edge cases.

z.lua is MIT licensed. Practically, that means you can vendor the script into a dotfiles repository, modify it, and redistribute it, provided you keep the licence text. The repository contains a LICENSE file and a README.cn.md alongside README.md, so the Chinese documentation is maintained in-tree rather than in a separate wiki. If you fork and modify, keep the licence file with your copy; that is the only obligation the identifier implies here, and it is not legal advice.

Upgrade cost is low by design. There is no package manifest, no lockfile and no compiled step for the base install. Updating means replacing z.lua and opening a new shell. The one thing to check after an upgrade is your init line: the options after `--init <shell>` are positional words, so if a future release changes their names or ordering, your existing `.bashrc` line is where the breakage appears, not in the datafile. The README does not document a migration path or a rollback procedure for the `~/.zlua` datafile format, so keeping a copy of that file before a major version jump is the only safety net the documentation supports.

## Conclusion

Adopt z.lua if you already have a Lua interpreter on the machine, want one file you can drop into a dotfiles repo, and care about startup latency on slow or minimal shells, since the README positions it against fasd, autojump and z.sh on speed. Skip it if you want a compiled binary with no interpreter dependency, or if you need documented rollback and migration procedures, because the README does not document either. Before adopting, verify which Lua version your distro ships, decide whether you want the default z.sh-compatible matching or the enhanced algorithm, and check whether your shell is one of the ones the README lists as fully supported.

## FAQ

### What is the z command in z.lua?

It is the shell function that z.lua generates during `--init`. Calling `z foo` changes to the most frecent directory matching foo, and the README shows variants such as `z -r foo` for highest ranked, `z -t foo` for most recently accessed, and `z -l foo` to list matches instead of changing directory.

### Which shells and operating systems does z.lua support?

The README lists posix shells (bash, zsh, dash, sh, ash, ksh and busybox), Fish Shell 2.4.0 or above, Nushell v0.96+, PowerShell, and Windows cmd with clink or cmder. WSL 1 users must additionally install lua-filesystem.

### What Lua version do I need to run z.lua?

The README states compatibility with Lua 5.1, 5.2 and 5.3+, plus luajit. The interpreter has to be callable as `lua` from the shell where you initialize z.lua.

### Can z.lua use fzf for interactive selection?

Yes. Adding `fzf` to the init line, for example `eval "$(lua /path/to/z.lua --init bash enhanced once fzf)"`, enables fzf-backed tab completion, and `z -I foo` performs interactive selection through fzf.

### Where does z.lua store its directory database?

In `~/.zlua` by default. The README documents `$_ZL_DATA` as the variable that changes the datafile location and `$_ZL_MAXAGE` as the aging threshold, default 5000.

## Sources

- [Issues](https://github.com/skywind3000/z.lua/issues)
- [License: MIT](https://github.com/skywind3000/z.lua/blob/master/LICENSE)
- [README](https://github.com/skywind3000/z.lua/blob/master/README.md)
- [Releases](https://github.com/skywind3000/z.lua/releases)
- [skywind3000/z.lua on GitHub](https://github.com/skywind3000/z.lua)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/skywind3000-z-lua
