I wrote a simple zsh function which allows me to select from the dirs-stack via fzf.

My .zshrc looks like


# change to directory from the dirs stack
fzf-change-dirstack () {
    cd "$(dirs -lv | cut -f2 | fzf )" 

zle -N fzf-change-dirstack
bindkey '^[p' fzf-change-dirstack   # shortcut ALT+P

It works fine even some improvements have to be done. The only thing which is very annoying for me is that when I use the keybinding I have to type Enter twice to change to the directory.

How can I modify the script to cd immediately without typing twice Enter again?

1 Answer 1

fzf-change-dirstack () {
  local dir
  print -rNC1 -- $dirstack |
    fzf --read0 --print0 |
    IFS= read -rd '' dir &&
    cd -- $dir &&
    zle -I

zle -N fzf-change-dirstack
bindkey '^[p' fzf-change-dirstack  # shortcut ALT+P

The main point being zle -I to Invalidate the prompt when the current working directory has changed, the rest fixing a few other issues in your code so it can work with arbitrary directory names.

In particular:

  • dirs -lv | cut -f2 would fail if any of those directory names contained newline or tab characters. More generally, file paths can be any sequence of non-null bytes. So to pass to fzf, you want to use its --read0 and provided a null-delimited list (here printing raw Null-delimited on 1 Column).
  • If you forget the --, then any directory starting with - would be a problem (wouldn't happen normally if not for the previous bug¹). Forgetting it for print even introduces command injection vulnerabilities.
  • don't forget error handling!

Also note that zsh already has its own dirstack completer when you complete cd +Tab or just ~+Tab to complete dirstack elements as argument to any command, not just cd/pushd.

For instance, try cd +TabTab after:

zstyle ':completion:*' format 'Completing %d'
zstyle ':completion:*' menu select=2
autoload compinit

And navigate with the arrow keys.

(this kind of setting you generally tune with compinstall)

¹ For cd, unfortunately, -- doesn't fix all the problems. -, +1, -1 for instance are still treated specially by cd. Those wouldn't occur as elements of $dirstack but may occur as the second TAB-delimited field of some line in the output of dirs -lv like after cd $'foo\nx\t+1\twhatever'.


You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .