I tried zoxide for a while but I really disliked how it made things fuzzy, and most of the use cases for it I found were 90% solved by using ZSH's history search which I use routinely anyway.
It gives you this potentially constantly shifting set of shortcuts, essentially, and the problem is that means I have to constantly check I did get the result I wanted, and that I haven't accidentally gone to the wrong place. I found that more annoying to me than just using tab completions or history, which are much more predictable.
I can see how someone who has different workflows or environments might find it great though.
How are you using Zsh history to navigate to specific folders? E.g., does that mean you always start your `cd` from the home directory (e.g., `~`)? I'm asking because it's usually less key strokes to `cd` to a relative directory (assuming you're working in several related directories). But then the `cd` entry in your history would assume a specific starting path (and therefore wouldn't be universally helpful to recall from history)?
Also, re:
> the problem is that means I have to constantly check I did get the result I wanted, and that I haven't accidentally gone to the wrong place.
Is there a reason you don't add your current path to your prompt? I don't know how I'd work without that, never knowing which directory I'm in.
I like using fzf combined with zsh's shell history filtering.
You can type `cd ~` and press CTRL+r to immediately fuzzy match commands you've run with `cd ~`. fzf naturally ranks paths to cd into on top. If you find that too noisy you can just hit CTRL+r with an empty prompt and then search for `^cd ~` to only find cd commands.
If you want to go into ultra lazy mode you can also type `cd ` and spam the up / down arrows to only show commands from your history where you cd'd into a directory. That use case is also covered in the above post. I normally don't use this for changing directories but it can be done.
The premise is the same: Dump history into fzf and add a grep/awk.
My point was that requiring a new shell (or even history) is a limiting factor here, and either backwards search over commands (as suggested ITT), or just plan fzf directory changes are more functional and already integrated into bash.
`cd foo` is useless in history if you're not already in foo's parent directory. This is the problem Zoxide solves, `z foo` will still do something useful in that case. (Side note about fzf, recursively fuzzy finding subdirectories fine for some use cases, but it doesn't scale as well as Zoxide.)
Yes, the marginal improvements from changing your entire shell are not to be disregarded. I'll change to asserting that it's entirely possible to do a nice 80/20 without changing shells.
I use autojump, which is a lot like zoxide (possible predates it). It stores all the directories you've visited in an SQLite DB (along with the rank for each). I wrote a shell keybinding that presents me with fzf, along with the directories I've visited, in rank order.
With just a few keystrokes, I can visit any directory I've ever visited, really fast. It doesn't need to be the top ranking directory for my query.
Yeah the problem with this approach to me is populating the history with a bunch of cd to absolute paths to begin with, which is not something I'd do natural (I have many ways I'm navigating the file system), and definitely wouldn't do manually. Not having to populate that list is the advantage of zoxide.
- Recursive directory tab completion isn't viable for many key use cases (it'll hang with both monorepos and networked volumes). The only thing that makes zoxide viable is that it only matches against visited directories. (Similarly fzf doesn't have much of a relation here besides encouraging recursive fuzzy matching, which I tend to avoid because it doesn't scale to complex directory structures.)
- Zoxide is probably sponsored by Warp, I doubt they have a relation beyond that, I'm not sure but they don't have much of a synergy beyond both being written in Rust. (The main point of relevance here is Warp is venture funded.)
- Zoxide having a database outside of the shell is actually a huge advantage to me, because it makes it easy to access your database outside of the shell (e.g., Zoxide integration in Vim).
I think that's ok if you have absolute paths mixed in.
If I have some long path like ~/src/open-source/dotfiles or /home/nick/src/open-source/dotfiles it all works with fuzzy matching. You can hit CTRL+r and then search for "cd dotfiles" and it finds it. The ~ isn't necessary, you can also do "^cd dotfiles"` for a tighter list of matches for paths that are more ambigious with other non-cd commands.
It's not that I always use absolute/home-relative paths, but I'm almost always working from the same folders for the context: if I'm working on a project, I'll be in the project directory and work relative to that the vast majority of the time, for example. I also use the substring history search which makes it more useful for the equivalent to zoxide's case.
And I do have my path in my prompt, I'm not talking about something that actually takes time, but more interrupts flow (for me, as I say, I get how for other people it'd work better).
The issue for me with this approach is one: It assumes a clear root for a project (e.g., your base you're cd-ing off of), I think that's only good assumption for small-scale projects? E.g., sufficient complexity, for programming at least, necessitates modularity which dilutes the concept of a "root".
The other issue is that it creates a separate "hop" which adds key strokes and cognitive load (i.e., I can't just jump directly to a subdirectory or related directory I first have to jump to a "junction" directory then to my destination).
In any event, I could see how that would be a reasonable approach in the absence zoxide, but those are the reasons I personally still prefer zoxide. (For the record, zoxide has some nice techniques for making a match more specific, e.g., `z foo bar` will hop to a dir containing `bar` only if it's in a subdirectory containing `foo`.
Mcfly[1] takes your working directory into account when searching shell history.
From the readme:
> The key feature of McFly is smart command prioritization powered by a small neural network that runs in real time. The goal is for the command you want to run to always be one of the top suggestions.
> When suggesting a command, McFly takes into consideration:
- The directory where you ran the command. You're likely to run that command in the same directory in the future.
- What commands you typed before the command (e.g., the command's execution context).
- How often you run the command.
- When you last ran the command.
- If you've selected the command in McFly before.
- The command's historical exit status. You probably don't want to run old failed commands.
I love that mode in Atuin. I can never remember which of the run commands to use between make/cmake/bazel/yarn/npm/uv and hitting ctrl-r twice and scrolling up is better than having to root around in a readme, which I may or may not have bothered to write for my future self.
Oh I forgot about "just"! (and I have Opinions about that name.) Add that to the list. Making order from chaos is not unfamiliar to me. Previously I standardized on make and was fastidious about making makefiles, but with atuin, command directory history the extra effort to create makefiles became superfluous.
I used to have this fantasy that after I die, someone will care enough to go through my ~/projects folder and go through everything I worked on, and all those makefiles and readmes were going to help them, but no one cares that much for me. I'm okay with that, depressing as it is.
That's not default behavior in most shells (e.g., `autocd` in Zsh, and, for the record, that's also not default up arrow behavior in Bash or Zsh [it is in Fish]).
But my question is specifically about relative vs. absolute paths when recalling directory traversal from history. I'm still struggling to follow how you'd use Zsh history as a zoxide replacement without always using absolute paths.
I generally work in only a few folders (we'll call them project a, project b, project c), so once I taught it these are high priority, it just works when I type any part of their names
In Zsh with `setopt autocd cdablevars`, shell variables more or less work like cd-aliases. You could also just add `a=projectA; b=projectB; c=projectC` to your `~/.zprofile` and then at prompts type 1-letter commands `a`, `b`, `c`.
One added bonus of such file tree bookmarks via variables (over a similar `alias a='cd foo'`) is that if you get muscle-memory/active memory for those few abbreviations other use cases like `make -C $a` also work. I usually leave `$hb` & `$lb` set to `$HOME/bin` and `/usr/local/bin`, for example.
# BOOKMARKS (mark b)
declare -x -A bookmarks
bookmarks=(
d "$HOME/Desktop"
dd "$HOME/Downloads"
c "$XDG_CONFIG_HOME"
)
for key value in ${(kv)marks}; do
hash -d $key=$value
done
Then I can just use ~c, ~d or ~dd or anything temporary I want to put there
`hash -d` (aka "named directories") is surely an alternative and if that suits you, by all means.
I see at least two downsides: 1) now you have to remember to say `make -C ~c` { not `make -C $c` which I think most would find more natural } 2) the hash cannot be exported to inheriting subprocesses like a regular scalar $var.
Of course, 1) is kind of weak since you can also use "~var" most places. It's just not as familiar to many as $var.
One notable equivalency is that Zsh prompt escapes for $PS1 and friends like %~ would treat both the same - converting them to a "~c" inside your prompt.
So, in terms of "looking like what you type", that maybe makes ~c better. Maybe there is some setopt to make Zsh expand %~ as $c or $dd? Not sure. There are a lot of setopts.
Anyway, I actually use the exporting feature to non-Zsh subprocesses myself. So, I'm pretty locked-in to vars not just hash entries.
The comment above is something I love about HN. I never knew this existed and would not have even thought to search for it but, but that’s something that I see me using everyday.
Glad to be appreciated. You might also enjoy this snippet from my zshrc:
my-expand() BUFFER=${(e)BUFFER} CURSOR=$#BUFFER
zle -N my-expand # Make Alt-E expand environ vars w/o val escape
bindkey '^[e' my-expand # Allows eVar "short cuts" for history !!:gs etc
It's vaguely related in that it lets you put a variable in the main Zsh Line Editor (ZLE) buffer (with the leading `$`) and then press Alt-E to expand it. ('e' for E)nvironment-E)xpand).
As per the comment I did this so that I could have "history substitution expressions" stored in variables, then Alt-E - look (& maybe edit) & ENTER.
The history syntax itself is very cryptic, derived from early 1980s BSD csh. As with most such crypticnesses, figuring it out once, storing it somewhere you can remember, and expanding it on demand is a not awful way to learn it.
But besides cryptic history directives you could also use it for the `$a<Alt-E>` above to "see before you do". You need an extra `$`, though, as written. It would be pretty easy to auto-add a `$` prefix in `my-expand`, though (with Alt-E just becoming a sort of different kind of TAB for expansion rather than completion).
Yeah, I've been trying it recently and I'm not entirely convinced I want to keep using it.
My biggest annoyance at the moment (and this may be me missing something), is that I have two directories: "thing" and "thing-api". I'm doing work in "thing" much more often than in the "thing-api", but whenever I run "z thing", it takes me to "thing-api" first, and I have to "z thing" again to get to where I wanted to go. It ends up being more effort than if I'd just tab-completed or history searched a plain cd command.
Perhaps helpful: There's also a `zi` command, which prompts you with a list of all matches before changing directories. Personally there's only few directories where I need it, and I just memorize using zi instead of z for those.
However I agree z should ideally have some syntax like `thing$` to denote a full directory name instead.
> Yeah, I've been trying it recently and I'm not entirely convinced I want to keep using it.
> My biggest annoyance at the moment (and this may be me missing something), is that I have two directories: "thing" and "thing-api". I'm doing work in "thing" much more often than in the "thing-api", but whenever I run "z thing", it takes me to "thing-api" first, and I have to "z thing" again to get to where I wanted to go. It ends up being more effort than if I'd just tab-completed or history searched a plain cd command.
AFAIK the z command does take frequency into account (or was it most recent visit). However to avoid going into thing-api instead of thing I believe you just type thing/ i.e. At the slash and z will take you to thing (that obviously doesn't work with tab completion though).
I found that after some time I have gotten so used to z (which I aliases to cd) that I wouldn't want to live without it.
The aha moment for me was to type a space after the characters I'm searching for - then hit tab. You then get the list of options ranked (and a nice view showing the contents of each folder).
I wrote a shell keybinding that presents me with the candidates using fzf (in rank order). This way I can see which one it will go to and pick the "correct" one if need be. It's blazing fast.
You're right, "z d" to always to always go to your downloads folder if way more powerful than whatever fuzzy match can achieve, and unfortunately the project didn't want to add a predictable input mechanism, though maybe that'll change in the future...
For me, this simple tools is the single best command line changer! Instead of a lot of commands to traverse the folder tree, I jump where and when I want.
True! lots of people sleeping on Fish. Probably because it's not POSIX compliant -- which is something I was hesitant about at first, too. My favorite features are: built-in vim mode, alt+s instead of sudo !!, backwards search with arrow-up, overall good default settings
Fish was weird at first, with it's insistence on `or` instead of `||`, and `and` instead of `&&`. Since they relented on this, there's not much non-POSIX weirdness for me. From time to time I'll try to `export` something, but fish just reminds you what to do instead. 3rd party integration is excellent now, too.
When I write scripts I'll just target /bin/sh, or /bin/bash if necessary. Never saw a reason to write zsh or fish scripts.
Note that `/bin/bash` isn't guaranteed to exist, even if Bash is installed. `/usr/bin/env` is guaranteed to exist on POSIX systems, so `/usr/bin/env bash` is the generally recommended way to invoke it in a shebang. `/bin/sh` is guaranteed to exist on a POSIX system, but `/usr/bin/env sh` can be used for consistency.
100% this! I tried fish, loved it, then didn't use it for about a year becayse I'd convinced myself posix compliance was important. The truth is day to day I use 5 or less shell language primitives, mostly I run other programs from the shell, and pipe them into each other, so the shell language compliance doesn't matter.
I don't understand this reticence about POSIX compliance. When would you need your shell to be POSIX compliant? I've been using fish for ten years and I've never had an issue, even with scripting. If I need to run a script, it always has bash in the shebang line, so it runs normally.
In my personal experience, oh-my-zsh slows down things too much. You're better off just taking whatever you really like about oh-my-zsh and configure it yourself.
If you work with a lot of longer commands, you can press Alt+e to edit the current prompt in $EDITOR, so you can use your favorite editor to do whatever you'd like to the command.
I configured Atuin to use the hostname of the machine in the history file name, so when using distrobox I get separated histories even though my home directory is shared.
The folder-scoped and repo-scoped histories are great too.
One useful thing I discovered recently about zoxide is that it has a basedir flag, so in theory you scan scope your query to the directory you’re in or based off some git root.
something like
alias zg=‘zoxide —basedir $(git rev-parse --show-toplevel)’
cd alternatives always give me PTSD from one of my earliest computer memories, aged 13 or so. That was on DOS 3.3 on my parents 286, and I had recently installed some Norton utilities, among them ncd (Norton Change Directory), which kept its own database of directories and allowed for fuzzy cd with regex.
It was quite cool to see the power of such a tool, until the day I wanted to ncd into a directory with some code experiments I had built and just delete them all. Unfortunately the completion sent me into another directory, which I noticed after deleting all my parents tax documents.
DOS 3.3 didn't have an undelete command like later versions of DOS, so a colleague of my dad had to spend an evening trying to restore some of that data with some external tools. On the positive side, he also installed DOS 6.22 (we were really behind).
The classic z [0] is a shell script and thus doesn't require any installation other than loading this script in your shell's rc file.
I found Navita [1] an improvement over it that works more reliably on different shells such as Bash and ZSH, though.
I have been testing this as a daily driver since the last big mention on HN to simplify my .bashrc file.
I use it with `eval "$(zoxide init bash --cmd cd)"` so I can continue to use CD due to muscle memory.
- I like that if there are multiple /foo directories known by Zoxide, say /foo/ and /abc/foo/, that you can do `cd abc foo` to go the path containing both.
- I am not a fan of having to do `cd foo**` for tab completion to folders outside CWD. I feel it slows me down as a tab complete fanatic.
- Also don't enjoy if I `cd foo/bar/batz` directly, then try `cd bar`, Zoxide has no reference. You would need to CD into each directory individually to build the database. I have seen scripts kicking around online to put a complete directory structure into Zoxide database by CD'ing to each subdirectory for you.
Not sure if I am officially sold, or I'll go back to aliases and simple tab completes forwards, and backwards (logic I use for tab complete backwards to exact directory name backwards from CWD instead of `cd ../../../etc` https://gist.github.com/GNOMES/6bf65926648e260d8023aebb9ede9...)
Initial creator of autojump here: just use zoxide. I passed autojump mainternship to someone else a few years ago but it has now been abandoned. Rust is superior to python for this application anyway.
Switched as well, with "alias j='z'" seems to be 1:1 replacement so far. Also did 'zoxide import --from=autojump ".local/share/autojump/autojump.txt" --merge'.
I don't use autojump, but glancing over its readme it's missing nushell integration that zoxide provides.
It being a native binary instead of Python-based might also help it execute more instantaneously. Most Python-based CLI helpers that I tried add a slight but noticeable delay to simple commands, whereas zoxide is so quick it's easy to forget you even invoked a helper in the first place.
I was using autojump for years (on debian) until I lost my jump history several times in the past few months. Turns out it's a known race condition bug fixed in a newer version:
This has me lamenting just how fiddly it really is to implement a standalone "cd" that isn't a builtin.
I am certainly there are a whole host of security reasons not to, but it sure would be handy if a parent process could easily just read the final state of all environmental variables of a child process and possibly integrate them back into its own.
Shells could just have a syntax for accepting sub process environmental variables. I'd propose something easy like starting a line with = absorbing all set environmental variables.
We could build a custom cd tool, "custom-cd-bin" in this example and all that would need to do is change the PWD variable.
$ =custom-cd-bin ./foo
Maybe this will be something for my dream shell I'm never going to actually get around to building. It would take something gross like wrapping setenv though
Can you tell me an example where this has a difference in behaviour?
Basically somewhere that source would not behave as you wanted?
I'm also uncertain what you mean wrt a binary. A binary will only have the environment variables available to the shell it's executed in - or do you mean that you'd also want it to inherit variables the binary itself might read from disk via .env files or similar?
I see several comments saying they use fzf instead of zoxide.
You should use both in tandem!
Several years ago, I set up a keybinding that presents me all the directories stored in the zoxide[1] DB in fzf - in rank order. With just a few keystrokes, I get where I need to be, and I'm not presented with all possible directories - just ones I've visited in the past.
This solves the problem of "I want to go to a directory that's not the most ranked one for the string I typed, and zoxide keeps putting me in the other one".
Once you have this flow, there's no going back!
[1] Actually, I use autojump, but it should work with zoxide as well.
I used zoxide for a while before realizing that the `zi` command allows you to search for your desired destination before changing directories. I use it instead of the default `z` command the vast majority of the time now.
I love shell tools, and by no means disparage the use of zoxide, z, etc. But I find I get 90% of the usefulness of these tools using the native cd command and adding my most used directories to CDPATH.
This additionally is consistent and works without needing to “train” it first.
I agree, but I get 100% of the usefulness of these tools by installing them with one command and using them. Why settle for 90% when 100% takes a second?
Fair point. I don't disagree. I personally just like to stay as close to default GNU/Linux tooling as is practical. It's a matter of personal taste.
For me, this makes it so my expectations and muscle memory transfer cleanly between my workstation and other servers, devices, etc. I find the default tools are much more powerful than is often understood, and you can replicate most third party functionality fairly easily. That's not always the case mind you, and I happily use those tools.
I can’t see myself using this. I have a mental map of my folder structure and am actually proud that I can navigate and type the structure fast enough to move around. I feel that remembering which directory was last and adding some matching overhead is too much for my use case. I also use the zfs history heavily with fzf. And similar to another comment I usually work from same paths, be it a project root or my home directory.
In any case I think it’s still valuable to break with the norm and explore other ways of using the shell. This time around it ain’t for me though.
I actually made a git worktree aware function called w that wraps zoxide and will basically switch to the main worktree, execute z, and then switch back to the worktree you came from. That way you don’t run into zoxide switching from one worktree to another annoyingly, and new worktrees immediately inherit your zoxide scores. You purge all other worktrees from the zoxide database and use w instead of z inside git repos.
I haven’t used it in a while though because I switched from git to jj.
Those kind of tools should have the same name as the command they replace, I don’t want to change my workflow with this or that. I think a simple wrapper over cd with fzf is good enough and much simpler. Claude can probably write it in a few minutes.
zoxide alongside fzf, eza, bat and starship were my killer CLI productivity tools I discovered after ditching Windows for Fedora. I have it aliased to `cd` so I don't really notice when I'm using it until moving to a Terminal that doesn't have it.
This is nice, but ... I wish the doc didn't introduce it as "a better cd command", that's very misleading.
With the exception of the fuzzy part, which can be added separately, Bash already has facilities for all of those functionalities.
The "remembering" part can be done via dirs/pushd/popd.
The "using the basename only" part can be done via CDPATH.
The "automated" remembering could be done via a super simple function wrapper over 'cd', adding things onto the the dirstack or the CDPATH respectively (and with a selection menu, possibly fuzzy, when clashes occur)
Putting it all together to achieve the same effect feels like a very simple 10-liner bash function at best.
Not dissing the author's work or idea, nor is there anything wrong with reinventing the wheel in another language for fun; but if you're going to claim that "z is a better cd" because it bundles all that other functionality for things that are unrelated to cd, and for which there are already pretty decent unix-philosophy-adhereing commands that you could be using effectively already, then if you fail to mention these things in your documentation, it feels a bit ignorant / disingenuous. I would have preferred examples with "without z you'd have to use CDPATH for this, and dirstack for that, and maybe this 5-line wrapper for the menu thing, etc".
Otherwise it feels a bit like saying "grep is a better ed" or something.
I think you're misunderstanding the authors intention. Fuzzy finding is the banner feature of zoxide, i.e., it's only a better cd because it includes that feature. Everything else is just the necessary table stakes features in order to make zoxide a viable cd alternative. E.g., it's not notable that zoxide copies the rest of the useful features from cd in Bash, because that's exactly what it would need to do in order to be a cd replacement.
What you could argue with is whether there's a point to replacing the cd built-in in order to include fizzy finding (personally I don't see the point of this either). But the reason all the other cd features are included is in order to make that viable.
I installed it as cd. I do not think I ever, even a single time, used it differently from normal cd. It turns out that my system is organized so that I know where everything is.
On the rare occasion something is lost, it's a file, not a directory. Then I Spotlight (Mac super find) because it also searches content
I tried some alternatives and while some works (bat, rg, fd) others don't (fzf and said `zoxide`)... most of the time they usually get in the way more…
I barely use pushd/popd, for me this would be overkill, and I dislike the side effects. I mostly use my history, so same command needs to reliably do same thing.
Workaround for cd-ing:
Does anyone know if zoxide has any fancy logic to ignore strings that appear in common prefixes?
For example I have a big ~/src dir where I keep all my code checkouts. If I type 'z src' intending to go to ~/src/foo/bar/src, will it be clever enough to realise that I am referring to the second instance of the string 'src'?
I currently use a Fish port of the original 'z'. It does ignore the common prefix of _all_ matches (so if I only ever used it within my ~/src tree, the problem would disappear) but after that binary exclusion it works exclusively on frecency.
Adding to the other answer: You can also pass multiple keywords to zoxide and they are expected to match in order. So in your example, `z foo src` would reliably cd into `~src/foo/bar/src` even if `src/foo` has a higher visit frequency.
As far as I'm aware it keeps a history of the frequency you visit each directory so yes it will select the one you've visited more often (assuming you don't always start at the base one and work your way down).
Interesting to read the different threads, it sounds like most of the shortcomings of this tool can be somehow "configured away", but at that point, just configure your terminal by adding suggestions, plugins, etc, then you'll have all what you need for every command, not just CD.
it's weird how most IDE's already figured out people want this. I can type in "New File" > "/foo/bar/baz" and it will work just fine. Is there any limitation as to why mkdir can't support recursive generation?
That is why I’m mostly using default tools. I like the idea of these tools, but autocomplete and fuzzy finding takes in aggregate more time than if I took a step back and actually configure my tools (shell alias and scripts, ssh config, git alias,…).
My current list of tools I install are:
- ripgrep. It’s just fast.
- fzf. For vim. I’ve never have a real use for it on the command.
- lf. Sometimes I want to quickly browse around a directory.
But more often than not, the core utilities work fine.
Ctrl+r in zsh already gives you fuzzy search. In the rare case I have to go to a long path that I can't remember I use that, but for most cases, cd <tab> [...] <tab> <enter> suffices if I am feeling really lazy
You can type any part of the path, not just the final part - so, from `/path/to/projects/go/github.com/me/my-cool-app/src`, you could type `cool-app/src` or `proj me cool src` or `cool s`.
Interactive selection is useful partly because you can search iteratively, for less-frequently accessed locations. E.g: I might type `go fwip` because I'm looking for my go projects, and then once I see it in the list and actually remember it's called "my-radical-app", I add `rad` (or use the arrow keys to select) and hit enter.
That sounds like you've already got it pretty well figured out, then. :)
If you did want to get fuzzy-matching in your workflow, I might recommend using building a small alias/function with fzf[1], rather than using zoxide.
Something like `dst=$(cat ~/myprojectlist.txt | fzf) && cd $dst && source sourceme`. You wouldn't get the list sorted by most-frequent/recent like you do with zoxide, but it sounds like that's not what you're looking for anyways.
There's a few different versions of this same idea, I think zoxide is the third one I've used. Years ago, I started using z.lua, then switched to a fish shell plugin implementation of z, finally to zoxide. I don't think there's any real difference between them, aside from their implementation language and the ease of installation.
Or the pure zsh version, which is superior, since it eliminates a ton of forking by eliminating calls to external tools (the original z script relies heavily on awk, sort, date, sed, mv, rm, and chown). There are also significant stability improvements to the database thanks to proper locking.
I tried using z for a while but I got mad every time I typoed, failed a tab-complete, and ended up in some random directory halfway across my disk instead of doing nothing.
Is it just me, or is it actually a new trend that the first thing on the README page is an advertisement? Could this perhaps even be related to the AI glut?
In any case, aberrations such as the excessive use of emojis and exaggeration are becoming increasingly common, which is yet another reason for me to distance myself from GitHub. For me, a README that more closely follows the conventions and minimalism of a classic man page is a sign of quality, and it could perhaps even be rendered in plain text to achieve a high signal-to-noise ratio.
I've always stayed clear of it because I don't like the idea of merging shell langauges with terminal interfaces, and also I'm not really a big fan of tools that inject AI into my workflow when I don't want them too. So I don't really have a take on it at all.
My whole experience of it is just seeing it's advertisements on every CLI tool page. Someone else here probably has something more meaningful to say about it though.
It definitely got more widespread, but I am not sure that it's related to AI. If it's a way for open source maintainers of awesome tools I use to fund their development, I am totally fine with that.
I think this is awesome tool, but somehow using fish shell does most of what I need and rarely I reach for it. This is not a critique of Zoxide, but just the fact that this is not as big of a problem once it was.
I tried zoxide for a while but I really disliked how it made things fuzzy, and most of the use cases for it I found were 90% solved by using ZSH's history search which I use routinely anyway.
It gives you this potentially constantly shifting set of shortcuts, essentially, and the problem is that means I have to constantly check I did get the result I wanted, and that I haven't accidentally gone to the wrong place. I found that more annoying to me than just using tab completions or history, which are much more predictable.
I can see how someone who has different workflows or environments might find it great though.
How are you using Zsh history to navigate to specific folders? E.g., does that mean you always start your `cd` from the home directory (e.g., `~`)? I'm asking because it's usually less key strokes to `cd` to a relative directory (assuming you're working in several related directories). But then the `cd` entry in your history would assume a specific starting path (and therefore wouldn't be universally helpful to recall from history)?
Also, re:
> the problem is that means I have to constantly check I did get the result I wanted, and that I haven't accidentally gone to the wrong place.
Is there a reason you don't add your current path to your prompt? I don't know how I'd work without that, never knowing which directory I'm in.
I like using fzf combined with zsh's shell history filtering.
You can type `cd ~` and press CTRL+r to immediately fuzzy match commands you've run with `cd ~`. fzf naturally ranks paths to cd into on top. If you find that too noisy you can just hit CTRL+r with an empty prompt and then search for `^cd ~` to only find cd commands.
I've written about filtering related history with zsh here: https://nickjanetakis.com/blog/hooking-up-fzf-with-zsh-tab-c...
If you want to go into ultra lazy mode you can also type `cd ` and spam the up / down arrows to only show commands from your history where you cd'd into a directory. That use case is also covered in the above post. I normally don't use this for changing directories but it can be done.
You don't even need history.
Just `find . -type d | fzf` to determine what dir to change to (or ~ for "anywhere else")
1. Make an alias fcd 2. Make a tab complete that does that for the command fcd
This is kind of 101 bash - just DIY.
Here's mine:
(2) is the hardest part - just write something that works with `complete` and fzf. Nowadays this is childs play for any AI to just spit out.
(1) is just a) set the new command b) make the completion call c) map that call to <TAB> completion. there you go.If I'm reading this correctly, this will pass all subdirectories to fzf, which is very different from only directories you've visited.
The premise is the same: Dump history into fzf and add a grep/awk.
My point was that requiring a new shell (or even history) is a limiting factor here, and either backwards search over commands (as suggested ITT), or just plan fzf directory changes are more functional and already integrated into bash.
`cd foo` is useless in history if you're not already in foo's parent directory. This is the problem Zoxide solves, `z foo` will still do something useful in that case. (Side note about fzf, recursively fuzzy finding subdirectories fine for some use cases, but it doesn't scale as well as Zoxide.)
Yes, the marginal improvements from changing your entire shell are not to be disregarded. I'll change to asserting that it's entirely possible to do a nice 80/20 without changing shells.
It's (likely) simpler with zoxide + fzf.
I use autojump, which is a lot like zoxide (possible predates it). It stores all the directories you've visited in an SQLite DB (along with the rank for each). I wrote a shell keybinding that presents me with fzf, along with the directories I've visited, in rank order.
With just a few keystrokes, I can visit any directory I've ever visited, really fast. It doesn't need to be the top ranking directory for my query.
I can't live without it now.
Yeah the problem with this approach to me is populating the history with a bunch of cd to absolute paths to begin with, which is not something I'd do natural (I have many ways I'm navigating the file system), and definitely wouldn't do manually. Not having to populate that list is the advantage of zoxide.
From 2024 when this was last posted: It appears to me that these could all be just tab complete, fzf, or similar. Some are just plain old bash.
The commands are written in rust, presumably because they're part of this warp shell advertised on readme?
I used to use this https://github.com/jodavaho/smartcd
'scd journal'
'scd logs personal'
Now I tab complete using fzf, but the above is what you want.
Bash is Turing complete. You "need" nothing else. You may want it for various reasons.
- Recursive directory tab completion isn't viable for many key use cases (it'll hang with both monorepos and networked volumes). The only thing that makes zoxide viable is that it only matches against visited directories. (Similarly fzf doesn't have much of a relation here besides encouraging recursive fuzzy matching, which I tend to avoid because it doesn't scale to complex directory structures.)
- Zoxide is probably sponsored by Warp, I doubt they have a relation beyond that, I'm not sure but they don't have much of a synergy beyond both being written in Rust. (The main point of relevance here is Warp is venture funded.)
- Zoxide having a database outside of the shell is actually a huge advantage to me, because it makes it easy to access your database outside of the shell (e.g., Zoxide integration in Vim).
I think that's ok if you have absolute paths mixed in.
If I have some long path like ~/src/open-source/dotfiles or /home/nick/src/open-source/dotfiles it all works with fuzzy matching. You can hit CTRL+r and then search for "cd dotfiles" and it finds it. The ~ isn't necessary, you can also do "^cd dotfiles"` for a tighter list of matches for paths that are more ambigious with other non-cd commands.
It's not that I always use absolute/home-relative paths, but I'm almost always working from the same folders for the context: if I'm working on a project, I'll be in the project directory and work relative to that the vast majority of the time, for example. I also use the substring history search which makes it more useful for the equivalent to zoxide's case.
And I do have my path in my prompt, I'm not talking about something that actually takes time, but more interrupts flow (for me, as I say, I get how for other people it'd work better).
The issue for me with this approach is one: It assumes a clear root for a project (e.g., your base you're cd-ing off of), I think that's only good assumption for small-scale projects? E.g., sufficient complexity, for programming at least, necessitates modularity which dilutes the concept of a "root".
The other issue is that it creates a separate "hop" which adds key strokes and cognitive load (i.e., I can't just jump directly to a subdirectory or related directory I first have to jump to a "junction" directory then to my destination).
In any event, I could see how that would be a reasonable approach in the absence zoxide, but those are the reasons I personally still prefer zoxide. (For the record, zoxide has some nice techniques for making a match more specific, e.g., `z foo bar` will hop to a dir containing `bar` only if it's in a subdirectory containing `foo`.
Mcfly[1] takes your working directory into account when searching shell history.
From the readme:
> The key feature of McFly is smart command prioritization powered by a small neural network that runs in real time. The goal is for the command you want to run to always be one of the top suggestions.
> When suggesting a command, McFly takes into consideration:
- The directory where you ran the command. You're likely to run that command in the same directory in the future.
- What commands you typed before the command (e.g., the command's execution context).
- How often you run the command.
- When you last ran the command.
- If you've selected the command in McFly before.
- The command's historical exit status. You probably don't want to run old failed commands.
[1] https://github.com/cantino/mcfly
> The directory where you ran the command.
I love that mode in Atuin. I can never remember which of the run commands to use between make/cmake/bazel/yarn/npm/uv and hitting ctrl-r twice and scrolling up is better than having to root around in a readme, which I may or may not have bothered to write for my future self.
http://atuin.sh/
McFly sounds interesting! Added it to my list of things to investigate. Does it do multi-machine syncing?
You can settle on make (or just) and have consistent targets like run, test, build, deploy, etc….
I use emacs, so I have compilation buffers for those.
Oh I forgot about "just"! (and I have Opinions about that name.) Add that to the list. Making order from chaos is not unfamiliar to me. Previously I standardized on make and was fastidious about making makefiles, but with atuin, command directory history the extra effort to create makefiles became superfluous.
I used to have this fantasy that after I die, someone will care enough to go through my ~/projects folder and go through everything I worked on, and all those makefiles and readmes were going to help them, but no one cares that much for me. I'm okay with that, depressing as it is.
> The command's historical exit status. You probably don't want to run old failed commands.
Yeah but the other commands I ran is so that one succeeds.
There's no need to type "cd", just the folder name and hit up until you get to the right command.
That's not default behavior in most shells (e.g., `autocd` in Zsh, and, for the record, that's also not default up arrow behavior in Bash or Zsh [it is in Fish]).
But my question is specifically about relative vs. absolute paths when recalling directory traversal from history. I'm still struggling to follow how you'd use Zsh history as a zoxide replacement without always using absolute paths.
I generally work in only a few folders (we'll call them project a, project b, project c), so once I taught it these are high priority, it just works when I type any part of their names
Same and I'm about to try the counter approach: just alias those few things
In Zsh with `setopt autocd cdablevars`, shell variables more or less work like cd-aliases. You could also just add `a=projectA; b=projectB; c=projectC` to your `~/.zprofile` and then at prompts type 1-letter commands `a`, `b`, `c`.
One added bonus of such file tree bookmarks via variables (over a similar `alias a='cd foo'`) is that if you get muscle-memory/active memory for those few abbreviations other use cases like `make -C $a` also work. I usually leave `$hb` & `$lb` set to `$HOME/bin` and `/usr/local/bin`, for example.
Interesting !
I have this alternative in my zsh config:
Then I can just use ~c, ~d or ~dd or anything temporary I want to put there`hash -d` (aka "named directories") is surely an alternative and if that suits you, by all means.
I see at least two downsides: 1) now you have to remember to say `make -C ~c` { not `make -C $c` which I think most would find more natural } 2) the hash cannot be exported to inheriting subprocesses like a regular scalar $var.
Of course, 1) is kind of weak since you can also use "~var" most places. It's just not as familiar to many as $var.
One notable equivalency is that Zsh prompt escapes for $PS1 and friends like %~ would treat both the same - converting them to a "~c" inside your prompt.
So, in terms of "looking like what you type", that maybe makes ~c better. Maybe there is some setopt to make Zsh expand %~ as $c or $dd? Not sure. There are a lot of setopts.
Anyway, I actually use the exporting feature to non-Zsh subprocesses myself. So, I'm pretty locked-in to vars not just hash entries.
The comment above is something I love about HN. I never knew this existed and would not have even thought to search for it but, but that’s something that I see me using everyday.
Glad to be appreciated. You might also enjoy this snippet from my zshrc:
It's vaguely related in that it lets you put a variable in the main Zsh Line Editor (ZLE) buffer (with the leading `$`) and then press Alt-E to expand it. ('e' for E)nvironment-E)xpand).As per the comment I did this so that I could have "history substitution expressions" stored in variables, then Alt-E - look (& maybe edit) & ENTER.
The history syntax itself is very cryptic, derived from early 1980s BSD csh. As with most such crypticnesses, figuring it out once, storing it somewhere you can remember, and expanding it on demand is a not awful way to learn it.
But besides cryptic history directives you could also use it for the `$a<Alt-E>` above to "see before you do". You need an extra `$`, though, as written. It would be pretty easy to auto-add a `$` prefix in `my-expand`, though (with Alt-E just becoming a sort of different kind of TAB for expansion rather than completion).
Yeah, I've been trying it recently and I'm not entirely convinced I want to keep using it.
My biggest annoyance at the moment (and this may be me missing something), is that I have two directories: "thing" and "thing-api". I'm doing work in "thing" much more often than in the "thing-api", but whenever I run "z thing", it takes me to "thing-api" first, and I have to "z thing" again to get to where I wanted to go. It ends up being more effort than if I'd just tab-completed or history searched a plain cd command.
Perhaps helpful: There's also a `zi` command, which prompts you with a list of all matches before changing directories. Personally there's only few directories where I need it, and I just memorize using zi instead of z for those.
However I agree z should ideally have some syntax like `thing$` to denote a full directory name instead.
Yeah I aliased zi to z for this reason. z feels too much like a lottery ticket.
> Yeah, I've been trying it recently and I'm not entirely convinced I want to keep using it.
> My biggest annoyance at the moment (and this may be me missing something), is that I have two directories: "thing" and "thing-api". I'm doing work in "thing" much more often than in the "thing-api", but whenever I run "z thing", it takes me to "thing-api" first, and I have to "z thing" again to get to where I wanted to go. It ends up being more effort than if I'd just tab-completed or history searched a plain cd command.
AFAIK the z command does take frequency into account (or was it most recent visit). However to avoid going into thing-api instead of thing I believe you just type thing/ i.e. At the slash and z will take you to thing (that obviously doesn't work with tab completion though).
I found that after some time I have gotten so used to z (which I aliases to cd) that I wouldn't want to live without it.
The aha moment for me was to type a space after the characters I'm searching for - then hit tab. You then get the list of options ranked (and a nice view showing the contents of each folder).
I wrote a shell keybinding that presents me with the candidates using fzf (in rank order). This way I can see which one it will go to and pick the "correct" one if need be. It's blazing fast.
zoxide stores a rank for each directory based on how often you visit it, but you can manually adjust the scores.
Run `zoxide query -ls thing` to see the scores, and `zoxide add thing -s AMOUNT` to increase the score.
That's good to know, when I needed to raise the score for a directory I just did a bunch of `cd .`
(⌐■_■)
If xkcd took comment form, this would be it.
You're right, "z d" to always to always go to your downloads folder if way more powerful than whatever fuzzy match can achieve, and unfortunately the project didn't want to add a predictable input mechanism, though maybe that'll change in the future...
I use fzf completitions
and the ** shortcut followed by <TAB>, e.g. along with cd - to change to the directory beforeThis will hang if you have too many subdirectories (e.g., a good-sized company's monorepo). A networked volumes will also often cause it to hang.
(PS: In Zsh `cd -<tab>` will show a pop up menu of all directories in the history stack.)
in my homedir
takes 5 seconds.Population doing `cd ~/**<tab>` takes way way longer than 5 seconds though.
But regardless of the speed up one could add to cd + fzf in this case, I will still be using zoxide:
and `zi` starts instantaneous if I need the dynamic filteringedit: layout
For me, this simple tools is the single best command line changer! Instead of a lot of commands to traverse the folder tree, I jump where and when I want.
Other nice tools I use: Fish for shell (https://fishshell.com/), Starship for prompt (https://starship.rs/), bat "a cat with wings" for file preview (https://github.com/sharkdp/bat).
I also like eza (https://github.com/eza-community/eza) which is a modern ls. Fish is the biggest game changer though.
True! lots of people sleeping on Fish. Probably because it's not POSIX compliant -- which is something I was hesitant about at first, too. My favorite features are: built-in vim mode, alt+s instead of sudo !!, backwards search with arrow-up, overall good default settings
Fish was weird at first, with it's insistence on `or` instead of `||`, and `and` instead of `&&`. Since they relented on this, there's not much non-POSIX weirdness for me. From time to time I'll try to `export` something, but fish just reminds you what to do instead. 3rd party integration is excellent now, too.
When I write scripts I'll just target /bin/sh, or /bin/bash if necessary. Never saw a reason to write zsh or fish scripts.
Note that `/bin/bash` isn't guaranteed to exist, even if Bash is installed. `/usr/bin/env` is guaranteed to exist on POSIX systems, so `/usr/bin/env bash` is the generally recommended way to invoke it in a shebang. `/bin/sh` is guaranteed to exist on a POSIX system, but `/usr/bin/env sh` can be used for consistency.
Export works now.
I write most of my local-use scripts in fish now. Having access to all the little ergonomics like outputting colored text is just more pleasant.
Note that `or/||` and `and/&&` are not interchangeable. They have different precedences.
This whole time I didn't know you're supposed to use alt+s. I just got mad sudo !! didn't work. Thanks!
100% this! I tried fish, loved it, then didn't use it for about a year becayse I'd convinced myself posix compliance was important. The truth is day to day I use 5 or less shell language primitives, mostly I run other programs from the shell, and pipe them into each other, so the shell language compliance doesn't matter.
I don't understand this reticence about POSIX compliance. When would you need your shell to be POSIX compliant? I've been using fish for ten years and I've never had an issue, even with scripting. If I need to run a script, it always has bash in the shebang line, so it runs normally.
Am I missing something useful?
Other people's scripts in projects where you can't or don't want to edit the scripts to add a shebang line.
You can only run those with `bash script` anyway, your shell doesn't matter in that case.
Oh-my-zsh adds most of that while still being POSIX compliant
In my personal experience, oh-my-zsh slows down things too much. You're better off just taking whatever you really like about oh-my-zsh and configure it yourself.
This is what I do: https://github.com/bbkane/dotfiles/tree/master/zsh
Works great!
"zsh is just fine, you just need to add a megabyte of scripts on top" is not a good advertisement :)
If you work with a lot of longer commands, you can press Alt+e to edit the current prompt in $EDITOR, so you can use your favorite editor to do whatever you'd like to the command.
I had no idea about that! You and other commenters taught me some great features! I so love fish!
Why eza not lsd? (honest question as an lsd user)
eza was the first one I tried - that's about it for me. see https://github.com/orgs/eza-community/discussions/679 also
Thanks
Eza is nice, but lsd is a step up imho, since it uses the same arguments as ls so that I don't get confused when I change systems.
And Atuin (https://atuin.sh/) for (optionally shared) shell history
I configured Atuin to use the hostname of the machine in the history file name, so when using distrobox I get separated histories even though my home directory is shared.
The folder-scoped and repo-scoped histories are great too.
It's really neat !
rg and fd are the ones I use the most, much improved user experiences over grep and find respectively
One useful thing I discovered recently about zoxide is that it has a basedir flag, so in theory you scan scope your query to the directory you’re in or based off some git root.
something like
alias zg=‘zoxide —basedir $(git rev-parse --show-toplevel)’
https://github.com/ajeetdsouza/zoxide/pull/1027
Wow! That's really useful!
Yes, thanks!
cd alternatives always give me PTSD from one of my earliest computer memories, aged 13 or so. That was on DOS 3.3 on my parents 286, and I had recently installed some Norton utilities, among them ncd (Norton Change Directory), which kept its own database of directories and allowed for fuzzy cd with regex.
It was quite cool to see the power of such a tool, until the day I wanted to ncd into a directory with some code experiments I had built and just delete them all. Unfortunately the completion sent me into another directory, which I noticed after deleting all my parents tax documents.
DOS 3.3 didn't have an undelete command like later versions of DOS, so a colleague of my dad had to spend an evening trying to restore some of that data with some external tools. On the positive side, he also installed DOS 6.22 (we were really behind).
I guess you learned what a FAT was that evening.
Among other things, yes.
The classic z [0] is a shell script and thus doesn't require any installation other than loading this script in your shell's rc file. I found Navita [1] an improvement over it that works more reliably on different shells such as Bash and ZSH, though.
0: https://github.com/rupa/z 1: https://github.com/CodesOfRishi/navita/
I have been testing this as a daily driver since the last big mention on HN to simplify my .bashrc file.
I use it with `eval "$(zoxide init bash --cmd cd)"` so I can continue to use CD due to muscle memory.
Not sure if I am officially sold, or I'll go back to aliases and simple tab completes forwards, and backwards (logic I use for tab complete backwards to exact directory name backwards from CWD instead of `cd ../../../etc` https://gist.github.com/GNOMES/6bf65926648e260d8023aebb9ede9...)I've been using autojump, called with 'j'. Any pros of zoxide over that? https://packages.debian.org/sid/autojump
Initial creator of autojump here: just use zoxide. I passed autojump mainternship to someone else a few years ago but it has now been abandoned. Rust is superior to python for this application anyway.
Oh wow. I feel like you should be royalty around here!
I've been a happy user of autojimp for many years, and just made the switch to Zoxide last week because it's maintained. I feel vindicated now :)
Switched as well, with "alias j='z'" seems to be 1:1 replacement so far. Also did 'zoxide import --from=autojump ".local/share/autojump/autojump.txt" --merge'.
Switched. For anyone else thinking of doing the same, zoxide can import your data from autojump: https://github.com/ajeetdsouza/zoxide#installation
Here's the command I ran on macOS:
Hey, thanks for creating it! I still use it and as a light user it fits my purpose just fine
I don't use autojump, but glancing over its readme it's missing nushell integration that zoxide provides.
It being a native binary instead of Python-based might also help it execute more instantaneously. Most Python-based CLI helpers that I tried add a slight but noticeable delay to simple commands, whereas zoxide is so quick it's easy to forget you even invoked a helper in the first place.
I was using autojump for years (on debian) until I lost my jump history several times in the past few months. Turns out it's a known race condition bug fixed in a newer version:
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1110899
Migrated to zoxide instead, seems to work fine! Only need to get used to using z instead of j, muscle memory hard to adjust, might set an alias :)
This has me lamenting just how fiddly it really is to implement a standalone "cd" that isn't a builtin.
I am certainly there are a whole host of security reasons not to, but it sure would be handy if a parent process could easily just read the final state of all environmental variables of a child process and possibly integrate them back into its own.
Shells could just have a syntax for accepting sub process environmental variables. I'd propose something easy like starting a line with = absorbing all set environmental variables.
We could build a custom cd tool, "custom-cd-bin" in this example and all that would need to do is change the PWD variable.
Maybe this will be something for my dream shell I'm never going to actually get around to building. It would take something gross like wrapping setenv thoughMmh, that's kinda... Exactly what `source` is.
It's just rarely used beyond dotfiles because... Well.. it inherits all variables etc
https://docs.vultr.com/how-to-use-the-source-command-in-bash
If you want to make the transition explicit at the end of the script, you can do what the sister comment did, essentially
"source <(bash the-script|grep -Pom "xx inherited variables\n(.*?)\n yy inherited variables")"
Not at all. The source command is basically just an include, executing shell commands in the current scope.
I'm talking about inheriting the environmental state of any subprocess. You can't source a binary.
Can you tell me an example where this has a difference in behaviour?
Basically somewhere that source would not behave as you wanted?
I'm also uncertain what you mean wrt a binary. A binary will only have the environment variables available to the shell it's executed in - or do you mean that you'd also want it to inherit variables the binary itself might read from disk via .env files or similar?
So you're thinking something like before the wait(2) reading /proc/$PID/environ ?
I see several comments saying they use fzf instead of zoxide.
You should use both in tandem!
Several years ago, I set up a keybinding that presents me all the directories stored in the zoxide[1] DB in fzf - in rank order. With just a few keystrokes, I get where I need to be, and I'm not presented with all possible directories - just ones I've visited in the past.
This solves the problem of "I want to go to a directory that's not the most ranked one for the string I typed, and zoxide keeps putting me in the other one".
Once you have this flow, there's no going back!
[1] Actually, I use autojump, but it should work with zoxide as well.
I used zoxide for a while before realizing that the `zi` command allows you to search for your desired destination before changing directories. I use it instead of the default `z` command the vast majority of the time now.
I had no idea about this. Super helpful. Thank you!
I love shell tools, and by no means disparage the use of zoxide, z, etc. But I find I get 90% of the usefulness of these tools using the native cd command and adding my most used directories to CDPATH.
This additionally is consistent and works without needing to “train” it first.
I agree, but I get 100% of the usefulness of these tools by installing them with one command and using them. Why settle for 90% when 100% takes a second?
Fair point. I don't disagree. I personally just like to stay as close to default GNU/Linux tooling as is practical. It's a matter of personal taste.
For me, this makes it so my expectations and muscle memory transfer cleanly between my workstation and other servers, devices, etc. I find the default tools are much more powerful than is often understood, and you can replicate most third party functionality fairly easily. That's not always the case mind you, and I happily use those tools.
I used to agree, but nowadays my thinking is "I use my own machines 99.99% of the time, why optimize for the 0.01%?".
I can’t see myself using this. I have a mental map of my folder structure and am actually proud that I can navigate and type the structure fast enough to move around. I feel that remembering which directory was last and adding some matching overhead is too much for my use case. I also use the zfs history heavily with fzf. And similar to another comment I usually work from same paths, be it a project root or my home directory. In any case I think it’s still valuable to break with the norm and explore other ways of using the shell. This time around it ain’t for me though.
It’s just so good.
fzf and zoxide are probably my two most game changing cli tools. They make the terminal feel so good.
I actually made a git worktree aware function called w that wraps zoxide and will basically switch to the main worktree, execute z, and then switch back to the worktree you came from. That way you don’t run into zoxide switching from one worktree to another annoyingly, and new worktrees immediately inherit your zoxide scores. You purge all other worktrees from the zoxide database and use w instead of z inside git repos.
I haven’t used it in a while though because I switched from git to jj.
care to share?
To know haw bad CD, is to realize it's the second time in half of a year that Zoxide is featured in the main page.
Those kind of tools should have the same name as the command they replace, I don’t want to change my workflow with this or that. I think a simple wrapper over cd with fzf is good enough and much simpler. Claude can probably write it in a few minutes.
Or you could use shell aliases.
zoxide alongside fzf, eza, bat and starship were my killer CLI productivity tools I discovered after ditching Windows for Fedora. I have it aliased to `cd` so I don't really notice when I'm using it until moving to a Terminal that doesn't have it.
Love starship but it may slow your first command @@
https://github.com/romkatv/zsh-bench?tab=readme-ov-file#prom...
This is nice, but ... I wish the doc didn't introduce it as "a better cd command", that's very misleading.
With the exception of the fuzzy part, which can be added separately, Bash already has facilities for all of those functionalities.
The "remembering" part can be done via dirs/pushd/popd. The "using the basename only" part can be done via CDPATH. The "automated" remembering could be done via a super simple function wrapper over 'cd', adding things onto the the dirstack or the CDPATH respectively (and with a selection menu, possibly fuzzy, when clashes occur)
Putting it all together to achieve the same effect feels like a very simple 10-liner bash function at best.
Not dissing the author's work or idea, nor is there anything wrong with reinventing the wheel in another language for fun; but if you're going to claim that "z is a better cd" because it bundles all that other functionality for things that are unrelated to cd, and for which there are already pretty decent unix-philosophy-adhereing commands that you could be using effectively already, then if you fail to mention these things in your documentation, it feels a bit ignorant / disingenuous. I would have preferred examples with "without z you'd have to use CDPATH for this, and dirstack for that, and maybe this 5-line wrapper for the menu thing, etc".
Otherwise it feels a bit like saying "grep is a better ed" or something.
I think you're misunderstanding the authors intention. Fuzzy finding is the banner feature of zoxide, i.e., it's only a better cd because it includes that feature. Everything else is just the necessary table stakes features in order to make zoxide a viable cd alternative. E.g., it's not notable that zoxide copies the rest of the useful features from cd in Bash, because that's exactly what it would need to do in order to be a cd replacement.
What you could argue with is whether there's a point to replacing the cd built-in in order to include fizzy finding (personally I don't see the point of this either). But the reason all the other cd features are included is in order to make that viable.
I tried zoxide, but at the end settled for using fzf to quickly find and enter directories:
There's a built-in shortcut with fzf to change directories I think it's alt+d
I installed it as cd. I do not think I ever, even a single time, used it differently from normal cd. It turns out that my system is organized so that I know where everything is.
On the rare occasion something is lost, it's a file, not a directory. Then I Spotlight (Mac super find) because it also searches content
Eh, all my stuff is, say, in Code/Projects/Django/deadmansswitch, but it's great to be able to do `z dead` instead of typing the whole thing out.
If you want a simpler and less invasive "improved cd", you can do this in ZSH:
It does what it says: after changing the cwd, list its contents. You can try other things, e.g. "[ -d .git ] && git status".I tried some alternatives and while some works (bat, rg, fd) others don't (fzf and said `zoxide`)... most of the time they usually get in the way more…
I barely use pushd/popd, for me this would be overkill, and I dislike the side effects. I mostly use my history, so same command needs to reliably do same thing. Workaround for cd-ing:
cd ~/foo; $COMMAND ; #optional cd ~ here
My history is full of this
Does anyone know, how this compares to [autojump](https://github.com/wting/autojump)?
Could not imagine using regular cd for navigating file systems anymore.
The creator is actually in this thread now [0] and said that they passed ownership and it now unmaintained and recommends using zoxide instead.
[0]: https://news.ycombinator.com/item?id=45346715
Does anyone know if zoxide has any fancy logic to ignore strings that appear in common prefixes?
For example I have a big ~/src dir where I keep all my code checkouts. If I type 'z src' intending to go to ~/src/foo/bar/src, will it be clever enough to realise that I am referring to the second instance of the string 'src'?
I currently use a Fish port of the original 'z'. It does ignore the common prefix of _all_ matches (so if I only ever used it within my ~/src tree, the problem would disappear) but after that binary exclusion it works exclusively on frecency.
Adding to the other answer: You can also pass multiple keywords to zoxide and they are expected to match in order. So in your example, `z foo src` would reliably cd into `~src/foo/bar/src` even if `src/foo` has a higher visit frequency.
Ok what about when I have this
~/src/linux/linux
~/src/linux/stable
And I want to go to the first one? It doesn't have a unique sequence of keywords in it :/
Unless it recognises that if I type "z lin lin" I want the one where it appears twice?
I'm beginning to think my directory structure might just be toxic haha
You can think of it as replacing the space between words as wildcards, so `z lin lin` would indeed work to select the first one!
> I'm beginning to think my directory structure might just be toxic haha
Some tools have a hard time solving some problems when the problem is us.
Here's an idea that might work for you. Rename ~/src/linux_/ or something like that.
lacy https://crates.io/crates/lacy might be what you want
As far as I'm aware it keeps a history of the frequency you visit each directory so yes it will select the one you've visited more often (assuming you don't always start at the base one and work your way down).
No the issue is that the one I want _isnt_ the most recent. Because 90% of the directories I visit contain the string 'src'
Interesting to read the different threads, it sounds like most of the shortcomings of this tool can be somehow "configured away", but at that point, just configure your terminal by adding suggestions, plugins, etc, then you'll have all what you need for every command, not just CD.
Zoxide is a bit more than just that, it creates and index as you are using it, the more you CD into dirs the higher priority they will be.
What i really want is a `mkdir --cd` - annoying to code without shadowing real mkdir though.
I have a shell function "md" that does a "mkdir -p" and then cds into it. In fish:
This works in sh, bash, zsh, etc:
it's weird how most IDE's already figured out people want this. I can type in "New File" > "/foo/bar/baz" and it will work just fine. Is there any limitation as to why mkdir can't support recursive generation?
mkdir -p
https://www.man7.org/linux/man-pages/man1/mkdir.1.html
I'm surprised people do not know about this.
Maybe that is why they love these CLI tools written by Rust. ;)
Learn your basic UNIX/POSIX utilities first, please, before you start preaching because of Rust. :D
> Learn your basic UNIX/POSIX utilities first, please, before you start preaching because of Rust. :D
Maybe, jus maybe the defaults for UNIX/POSIX was just not good and thats why they Rust based one?
Maybe, just maybe I could fork "mkdir" just for you and make "-p" the default, in C?
Or maybe, just maybe you could make an alias for it, i.e. "alias mkdir="mkdir -p" so then it becomes the default?
sighs.
That is why I’m mostly using default tools. I like the idea of these tools, but autocomplete and fuzzy finding takes in aggregate more time than if I took a step back and actually configure my tools (shell alias and scripts, ssh config, git alias,…).
My current list of tools I install are:
- ripgrep. It’s just fast.
- fzf. For vim. I’ve never have a real use for it on the command.
- lf. Sometimes I want to quickly browse around a directory.
But more often than not, the core utilities work fine.
Ctrl+r in zsh already gives you fuzzy search. In the rare case I have to go to a long path that I can't remember I use that, but for most cases, cd <tab> [...] <tab> <enter> suffices if I am feeling really lazy
zoxoide, rg, fd, jj and Nushell are my command line favorites.
I wouldnt say I cant live without it , but it saves me a few seconds here and there and its a great tool!
I have a lot of paths that end with ".../src"
Looks like the tool doesn't handle that case very well.
I would say it might, with the 'zi' variant (cd with interactive selection (using fzf)).
If I have 100 paths that end in ".../src", then interactive selection will, I suppose, make me go through that long list.
You can type any part of the path, not just the final part - so, from `/path/to/projects/go/github.com/me/my-cool-app/src`, you could type `cool-app/src` or `proj me cool src` or `cool s`.
Interactive selection is useful partly because you can search iteratively, for less-frequently accessed locations. E.g: I might type `go fwip` because I'm looking for my go projects, and then once I see it in the list and actually remember it's called "my-radical-app", I add `rad` (or use the arrow keys to select) and hit enter.
Ok. That's good to know. I currently use an alias per folder.
So I would type "cool-app", and it would go to the desired folder.
But, and here comes the point, the alias also runs "source sourceme" to put all the environment variables in place, start virtual environments, etc.
That sounds like you've already got it pretty well figured out, then. :)
If you did want to get fuzzy-matching in your workflow, I might recommend using building a small alias/function with fzf[1], rather than using zoxide.
Something like `dst=$(cat ~/myprojectlist.txt | fzf) && cd $dst && source sourceme`. You wouldn't get the list sorted by most-frequent/recent like you do with zoxide, but it sounds like that's not what you're looking for anyways.
[1] https://github.com/junegunn/fzf
How is this any different from z?
There's a few different versions of this same idea, I think zoxide is the third one I've used. Years ago, I started using z.lua, then switched to a fish shell plugin implementation of z, finally to zoxide. I don't think there's any real difference between them, aside from their implementation language and the ease of installation.
Not having heard of either of these before, but now seeing z linked by sibling comment too, I would guess 'z oxide' is a Rust re-implementation of z.
The main difference is speed (Rust vs shell script)
Zoxide normally aliases to z. Is this not z?
This is z, no?
There is a different project called z:
https://github.com/rupa/z
I’d never heard of it but it was the first search result for “z github”.
Or the pure zsh version, which is superior, since it eliminates a ton of forking by eliminating calls to external tools (the original z script relies heavily on awk, sort, date, sed, mv, rm, and chown). There are also significant stability improvements to the database thanks to proper locking.
https://github.com/agkozak/zsh-z
I tried using z for a while but I got mad every time I typoed, failed a tab-complete, and ended up in some random directory halfway across my disk instead of doing nothing.
Rust people trying to replace stuff that already works...
Seems like a bit fancier "cd -" to me.
love zoxide; been using it for a long while as a replacement for built-in `cd`
nushell supports going up arbitrary number of levels like so: cd ..../foo, and that's the only feature I've ever missed in cd, so I'm all set.
Zoxide is amazing and I can't live without it since I switched to it.
Is it just me, or is it actually a new trend that the first thing on the README page is an advertisement? Could this perhaps even be related to the AI glut?
In any case, aberrations such as the excessive use of emojis and exaggeration are becoming increasingly common, which is yet another reason for me to distance myself from GitHub. For me, a README that more closely follows the conventions and minimalism of a classic man page is a sign of quality, and it could perhaps even be rendered in plain text to achieve a high signal-to-noise ratio.
Warp seems to have sponsored more or less every CLI tool these days- I don't think I've ever seen other companies advertised though somehow?
Do you have any real experience with Warp? Just curious to learn
I've always stayed clear of it because I don't like the idea of merging shell langauges with terminal interfaces, and also I'm not really a big fan of tools that inject AI into my workflow when I don't want them too. So I don't really have a take on it at all.
My whole experience of it is just seeing it's advertisements on every CLI tool page. Someone else here probably has something more meaningful to say about it though.
It definitely got more widespread, but I am not sure that it's related to AI. If it's a way for open source maintainers of awesome tools I use to fund their development, I am totally fine with that.
It's older than AI, I think people do it because it seems to work - where 'work' means builds hype and 'community' and GitHub stars fast.
One of those fundamental work-changing tools. I use it dozens of times a day. It's fantastic
Please replace CD with cd in the title, it's misleading.
Is it just me or does this description sound like an improved hammer?
I mean, it's a hammer. It's fine. You can hit things. And pull out nails.
This thing runs on batteries and needs syncing and it's a hammer. It didn't need improving. It's an improvement on a rock, and it's all improved out.
this feels like a hundred accidents waiting to happen.
there are CLI tools that are combining most of these package in one go, like Yazi, that are gaining popularity in the TUI community.
super happy to be a super of such an amazing tool, thank you!
I couldn’t live without it now. Very simple yet effective tool
I think this is awesome tool, but somehow using fish shell does most of what I need and rarely I reach for it. This is not a critique of Zoxide, but just the fact that this is not as big of a problem once it was.
I was going to say that. Gratitude to the creators of fish shell. The history-based rank is immediate as you type. Lately I've simply done:
abbr --add c cd
And then the "most used paths" are just present in the history filtered by the prefix I already typed.
huh, I don't seem to have this enabled at all. My cd in fish only shows the current directory choices
Honestly, love seeing some cool Rust written memory safe CLIs at HN at the same time as blog post of zig fanboys who couldn't.
Way to go.