Get the top HN stories in your inbox every day.
Latty
robenkleene
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.
nickjj
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.
jvanderbot
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.
fz_comp()
{
COMPREPLY=()
local cur="${COMP_WORDS[COMP_CWORD]}"
local prev="${COMP_WORDS[COMP_CWORD-1]}"
local opts="none"
if [ -z "$cur" ];then
COMPREPLY=($(find $1 -type d | fzf --preview="ls {} -l"))
return
fi
COMPREPLY=($(find $1 -type d | grep $cur | fzf --preview="ls {} -l"))
}
(1) is just a) set the new command b) make the completion call c) map that call to <TAB> completion. alias fcd=cd
_fcd(){ fz_comp $(pwd) }
complete -F _fcd fcd
there you go.BeetleB
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.
robenkleene
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.
Latty
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).
robenkleene
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`.
alright2565
There's no need to type "cd", just the folder name and hit up until you get to the right command.
robenkleene
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.
yonatan8070
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
Perz1val
Same and I'm about to try the counter approach: just alias those few things
cb321
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.
mrcarrot
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.
neobrain
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.
dicytea
Yeah I aliased zi to z for this reason. z feels too much like a lottery ticket.
cycomanic
> 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.
keybits
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).
paholg
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.
yonatan8070
That's good to know, when I needed to raise the score for a directory I just did a bunch of `cd .`
BeetleB
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.
eviks
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...
sandreas
I use fzf completitions
source <(fzf --zsh) > /dev/null
and the ** shortcut followed by <TAB>, e.g. cd ~/** # <TAB>
along with cd - to change to the directory before cd -robenkleene
This 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.)
sandreas
Seems to be some kind of oh-my-zsh thing... cd - <TAB> does not work on my zsh config.
jalk
in my homedir
> fd -t d | wc -l
213842
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:
> zoxide query -l | wc -l
1294
and `zi` starts instantaneous if I need the dynamic filteringedit: layout
stared
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).
hannesfur
I also like eza (https://github.com/eza-community/eza) which is a modern ls. Fish is the biggest game changer though.
nkydr0i0
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
cedilla
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.
benrutter
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.
stavros
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?
yonatan8070
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.
hyperhopper
Oh-my-zsh adds most of that while still being POSIX compliant
warwren
This whole time I didn't know you're supposed to use alt+s. I just got mad sudo !! didn't work. Thanks!
KingOfCoders
Why eza not lsd? (honest question as an lsd user)
drewbitt
eza was the first one I tried - that's about it for me. see https://github.com/orgs/eza-community/discussions/679 also
capitol_
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.
theshrike79
And Atuin (https://atuin.sh/) for (optionally shared) shell history
LelouBil
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 !
zem
rg and fd are the ones I use the most, much improved user experiences over grep and find respectively
kritr
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)’
elcapitan
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).
brontosaurusrex
I've been using autojump, called with 'j'. Any pros of zoxide over that? https://packages.debian.org/sid/autojump
joelthelion
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.
brontosaurusrex
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'.
edanm
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 :)
nusaru
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:
zoxide import --from=autojump ~/Library/autojump/autojump.txtotikik
Hey, thanks for creating it! I still use it and as a light user it fits my purpose just fine
neobrain
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.
jgb1984
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 :)
donatj
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 thoughffsm8
Mmh, 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")"
donatj
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.
ffsm8
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?
undefined
linsomniac
So you're thinking something like before the wait(2) reading /proc/$PID/environ ?
tcoff91
It’s just so good.
fzf and zoxide are probably my two most game changing cli tools. They make the terminal feel so good.
tcoff91
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.
0x008
care to share?
tcoff91
#!ruby
if ARGV.size < 1
puts "Usage: wz path"
exit
end
# Get current working directory
current_dir = "#{Dir.pwd}/"
# puts "Current directory: #{current_dir}"
# Run git worktree list and capture the output
worktree_output = `git worktree list`
# Split output into lines and process
worktrees = worktree_output.split("\n")
# Extract all worktree paths
worktree_paths = worktrees.map { |wt| "#{wt.split.first}/" }
# puts "Worktree paths: #{worktree_paths}"
# First path is always the root worktree
root_wt_path = worktree_paths[0]
# Find current worktree by comparing with pwd
current_wt_path = worktree_paths.find do |path|
# puts "Path: #{path}"
current_dir.start_with?(path)
end
if current_wt_path == root_wt_path
zoxide_destination = `zoxide query --exclude "#{Dir.pwd}" "#{ARGV[0]}"`.strip
puts zoxide_destination
exit 0
end
current_dir_in_root_wt = current_dir.sub(current_wt_path, root_wt_path)
Dir.chdir(current_dir_in_root_wt)
current_dir = "#{Dir.pwd}/"
# puts "Current directory: #{current_dir}"
# puts "Querying zoxide for #{ARGV[0]}"
zoxide_destination = `zoxide query --exclude "#{Dir.pwd}" "#{ARGV[0]}"`.strip
# puts "zoxide destination: #{zoxide_destination}"
Dir.chdir(zoxide_destination)
current_dir = "#{Dir.pwd}/"
if current_dir.start_with?(root_wt_path)
target_dir = current_dir.sub(root_wt_path, current_wt_path)
puts target_dir
exit 0
end
puts Dir.pwd
exit 0
Then put this function in your .zshrc: # Worktree aware form of zoxide's z command.
function w() {
cd $(wz $@)
}konfekt
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/
GNOMES
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...)jmarchello
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.
stavros
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?
jmarchello
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.
stavros
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%?".
achristmascarl
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.
xwowsersx
I had no idea about this. Super helpful. Thank you!
BeetleB
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.
Get the top HN stories in your inbox every day.
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.