I’m not a huge fan of cdpath myself; I tried it for a while a few years ago and found it too “magic”, especially once you start adding a whole bunch of directories in there. Same with autocd btw; I toggled between having that on and off way back in my tcsh days, and eventually decided that explicitly typing commands isn’t too much of a burden. But yeah, if that works for you: 👍 One of the nice things of zsh is that it gives you plenty of tools to do things the way you want; these are just some things I find useful personally but hadn’t seen much in other articles (which is why it excludes e.g. “show git branch in prompt”, as there are already 600 articles on that).
Personally I find “start with ” almost always a much more useful way to search. Searching for ls also matches alsamixer, tools, tls, totals, docker ls, and probably a bunch of other things. Usually I know what I want (history for a specific command), and the computer is generally dumber than me for these kind of things on account of not being able to read my mind. I never quite understood how anyone can deal with things like “fuzzy searches” btw.
What does that (N) do? I can’t seem to find any docs on it, and it doesn’t seem to behave quite the same?
I’ve got a few random things that might be interesting that I’ve learned in my time maintaining prezto and zsh-utils.
Exists could also be written with the plus operator, the commands array, and the arithmetic parens. This should be slightly faster as it doesn’t have to match the given arg against a pattern - it just uses the associative array that’s already there. Note that there are also arrays for $functions and I thought there was one that was a combined array of functions, commands, and aliases but I can’t seem to find it.
_exists() { (( $+commands[$1] )) }
This is a modification to _postpath which uses a zsh builtin to handle the readlink portion which allows it to work on more platforms (it’s especially nice because it should work with both macOS, BSD, and Linux). It could obviously be used for _prepath as well.
Ah, thanks; I’ll update the _exists and _{pre,post}path functions.
To be honest I never really looked much at zle, and always found it a bit confusing. I need to sit down and properly look at it one day. There’s some other useful stuff in that zshrc as well btw (like using terminfo instead of bindkey '^[OA').
Yeah there’s a ton in zle. It’s super powerful but there are also a lot of pitfalls. If you’re looking for something simpler than presto or oh-my-zsh take a look at zsh-utils or zsh4humans. They’re both really solid places to start.
I’ll give all the zsh users my favorite global aliases i’ve used for years:
alias -g silent="> /dev/null 2>&1"
alias -g noerr="2> /dev/null"
alias -g stdboth="2>&1"
# mainly for firefox, because it can suck up the cycles, but not when
# in a SIGSTOP state you can't use any power then firefox.
alias -g sigcont='kill -CONT '
alias -g sigstop='kill -STOP '
And my fave keybind to save what I typed and get it back after i run the thing i just forgot:
bindkey '^P' push-line # Saves the current line and returns to it afterwards.
I use them ALL THE TIME, aka: running a chatty command that you don’t want output for:
terraform apply -auto-approve silent
Or just filter out stderr for reasons:
chatty_stderr_command noerr
Or snagging stdout/err to a file:
docker build -t zomg . stdboth | tee whatever.log
Most of my zsh shenanigans are for just getting in/out of the shell as fast as I can and back into the warm embrace of emacs, so my shell stuff is pretty boring.
push-line is pretty neat and didn’t know about it. I ended up doing it like this:
remember() {
# Nothing in buffer: get previous command.
if [[ $#BUFFER -eq 0 ]]; then
print -ln "${stored:-}"
stored=
# Store current input.
else
stored=$BUFFER
BUFFER=
fi
}
zle -N remember
bindkey '^Q' remember
It’s essentially the same, except that you need to explicitly put the pushed value back with a second ^Q press, which seems a bit nicer to me.
A related thing I found/added btw:
# Run command but don't clear commandline and preserve cursor position.
bindkey '^\' accept-and-hold
Which seems pretty useful when mucking about with longer curl command etc.
Great post. I picked up a few news zsh configs. Thanks!
A sidenote: From what I understand, readlink -f is not entirely portable. For example, on macos readlink does not support the -f flag, but on FreeBSD (as well are gnu/coreutils) it does.
Oh yeah, you’re right. readlink is not always installed by default on Linux either (on some distros it’s a separate package, I forgot which one, and perhaps that changed now since I encountered this >5 years ago). It’s worked on all systems I’ve used in the last ~2 years though (including busybox), so it’s “good enough” for me 😅
My zsh tip to share.. on macos, if using the os provided zsh (and not homebrew), make sure to set HELPDIR! The default helpdir is pretty wonky in the build for some reason.
~% grep -m 1 HELPDIR /usr/share/zsh/${ZSH_VERSION}/functions/run-help
local HELPDIR="${HELPDIR:-/AppleInternal/BuildRoot/Library/Caches/com.apple.xbs/Binaries/zsh/install/TempContent/Root/usr/share/zsh/5.7.1/help}"
Setting HELPDIR to /usr/share/zsh/${ZSH_VERSION}/help fixed a frustration with nonfunctional run-help I was struggling with.
And as you stated in your article, the short form can also be displayed in the prompt easily. Zsh is quite powerful here. In addition to the static named directories feature you describe, there is a dynamic one. An old post of mine around that: https://vincent.bernat.ch/en/blog/2015-zsh-directory-bookmarks.
I’ll add something from my old zshrc – build the missing vim text objects when
bind -v
is turned on (stuff likeca"
):link to all my zshrc vim stuff here (note: cursorStyle is a function for setting the cursor display kind, here)
Nowadays I get my vim bindings in a shell context with emacs
shell-mode
(so I get all my vim/evil plugins there as well).select-quoted
is also useful with a number of other characters, all of the following are useful{a,i}"${(s..):-\'\"\
|,./:;-=+@}`There’s also a
select-bracketed
for things likei(
,i{
,i<
which you don’t seem to use. Andsurround
is also available.ooo nice, thanks for the tips
I just have
cdpath=(~/src/github.com/myfreeweb)
so I can just type e.g.dotf<Tab><Enter>
to cd to~/src/github.com/myfreeweb
:)(not having to type
cd
issetopt auto_cd
)https://github.com/zsh-users/zsh-history-substring-search — why limit yourself to “entries that start with ls” when you can search for “entries that contain ls” this way!
noooooo this functionality is built in!! Just use
(N)
when adding paths:P.S. my config: https://github.com/myfreeweb/dotfiles/tree/master/zsh
I’m not a huge fan of
cdpath
myself; I tried it for a while a few years ago and found it too “magic”, especially once you start adding a whole bunch of directories in there. Same withautocd
btw; I toggled between having that on and off way back in my tcsh days, and eventually decided that explicitly typing commands isn’t too much of a burden. But yeah, if that works for you: 👍 One of the nice things of zsh is that it gives you plenty of tools to do things the way you want; these are just some things I find useful personally but hadn’t seen much in other articles (which is why it excludes e.g. “show git branch in prompt”, as there are already 600 articles on that).Personally I find “start with ” almost always a much more useful way to search. Searching for
ls
also matchesalsamixer
,tools
,tls
,totals
,docker ls
, and probably a bunch of other things. Usually I know what I want (history for a specific command), and the computer is generally dumber than me for these kind of things on account of not being able to read my mind. I never quite understood how anyone can deal with things like “fuzzy searches” btw.What does that
(N)
do? I can’t seem to find any docs on it, and it doesn’t seem to behave quite the same?I haven’t bothered to actually read the docs before :) turns out this is called glob qualifiers and you can use them just normally in shell expansion:
The documentation is not really discoverable: you’d never find “sets the NULL_GLOB option for the current pattern” with Ctrl-F “exist” :D
If you combine
/
(directories) withN
(existing), looks like that does what you want:Turns out this feature is very powerful:
(yes, that’s files last written over 365 days ago)
When using
history-incremental-search-backward
(bound to^R
in emacs mode), note that you can start the pattern with^
to get an anchored search.Nice post, there’s some great stuff here!
I’ve got a few random things that might be interesting that I’ve learned in my time maintaining prezto and zsh-utils.
Exists could also be written with the plus operator, the commands array, and the arithmetic parens. This should be slightly faster as it doesn’t have to match the given arg against a pattern - it just uses the associative array that’s already there. Note that there are also arrays for
$functions
and I thought there was one that was a combined array of functions, commands, and aliases but I can’t seem to find it.This is a modification to _postpath which uses a zsh builtin to handle the readlink portion which allows it to work on more platforms (it’s especially nice because it should work with both macOS, BSD, and Linux). It could obviously be used for _prepath as well.
http://zsh.sourceforge.net/Doc/Release/Expansion.html#Modifiers
One really good example of where ZLE can be useful is prepending
sudo
on a keybind. https://github.com/sorin-ionescu/prezto/blob/master/modules/editor/init.zsh#L232I assume you could do this with
zle -U
, but the nice thing about this is that it doesn’t move the cursor.Ah, thanks; I’ll update the
_exists
and_{pre,post}path
functions.To be honest I never really looked much at
zle
, and always found it a bit confusing. I need to sit down and properly look at it one day. There’s some other useful stuff in that zshrc as well btw (like using terminfo instead ofbindkey '^[OA'
).Yeah there’s a ton in zle. It’s super powerful but there are also a lot of pitfalls. If you’re looking for something simpler than presto or oh-my-zsh take a look at zsh-utils or zsh4humans. They’re both really solid places to start.
I love zsh and all but confused why you made the _exists function? Is command -v not enough? Thats also portable to any bourne shell as well.
It’s just so much work to type!
For sudo, I am using this version:
For
_postpath
, it could be further simplified to (relying on Zsh not doing word-splitting by default and arrays are not nested:I don’t rely on
typeset -U
myself as it can be fooled with symlinks (and I want to keep symlinks in path, notably for the path to Nix profile). So, I do the dupe checks manually: https://github.com/vincentbernat/zshrc/blob/master/zshenv#L28Cc @arp242 this looks like it 404s?
Oops, should be fixed now; forgot to add a line when linking to this thread. “I’ll just make this simple change real quick, no need to test locally”.
Lol, the best kind of bug
I’ll give all the zsh users my favorite global aliases i’ve used for years:
And my fave keybind to save what I typed and get it back after i run the thing i just forgot:
I use them ALL THE TIME, aka: running a chatty command that you don’t want output for:
Or just filter out stderr for reasons:
Or snagging stdout/err to a file:
Most of my zsh shenanigans are for just getting in/out of the shell as fast as I can and back into the warm embrace of emacs, so my shell stuff is pretty boring.
push-line
is pretty neat and didn’t know about it. I ended up doing it like this:It’s essentially the same, except that you need to explicitly put the pushed value back with a second
^Q
press, which seems a bit nicer to me.A related thing I found/added btw:
Which seems pretty useful when mucking about with longer
curl
command etc.Note that
push-line
is already mapped to M-q by default.Great post. I picked up a few news zsh configs. Thanks!
A sidenote: From what I understand,
readlink -f
is not entirely portable. For example, on macos readlink does not support the-f
flag, but on FreeBSD (as well are gnu/coreutils) it does.Oh yeah, you’re right.
readlink
is not always installed by default on Linux either (on some distros it’s a separate package, I forgot which one, and perhaps that changed now since I encountered this >5 years ago). It’s worked on all systems I’ve used in the last ~2 years though (including busybox), so it’s “good enough” for me 😅Yeah, if it works, it works! Good enough. :)
My zsh tip to share.. on macos, if using the os provided zsh (and not homebrew), make sure to set
HELPDIR
! The default helpdir is pretty wonky in the build for some reason.Setting
HELPDIR
to/usr/share/zsh/${ZSH_VERSION}/help
fixed a frustration with nonfunctionalrun-help
I was struggling with.Ah, this is great. I can finally stop using oh-my-zsh now that I know how to replicate the features I care about.
could someone explain any preference over the wd plugin in contrast to the hash -d functionality ?
As I understand it,
wd
is a “super-chargedcd
”, whereas withhash -d
it works everywhere and you can do stuff likevim ~x/foo
andcp ~x/foo .
etc.And as you stated in your article, the short form can also be displayed in the prompt easily. Zsh is quite powerful here. In addition to the static named directories feature you describe, there is a dynamic one. An old post of mine around that: https://vincent.bernat.ch/en/blog/2015-zsh-directory-bookmarks.