Shell Tricks That Actually Make Life Easier (And Save Your Sanity)
66 points by wezm
66 points by wezm
History expansion like !! caused me enough footgun incidents that I disabled it over 25 years ago. It takes effect at a separate layer before normal shell language parsing happens, so it interacts badly with things like quoting and escaping. And it interacts weirdly with history: the command is recorded after expansion, which annoyed me because I want shell history to record what I wrote. I prefer using readline editing commands like M-. (last arg of previous command) because they give me interactive confirmation of what I’m about to do, unlike ! runes which require a tedious amount of error-prone counting and a leap of faith when pressing enter.
I don't know what config option I'm using for this (if I am any), but on ZSH, history expansion expands after pressing enter, and I get a chance to double check what it expands to before pressing enter again.
It's HIST_VERIFY. I was just about to post this re "!$ expands blindly at the exact moment you hit enter." It protects you when using !!, !$, etc.
HIST_VERIFY
Whenever the user enters a line with history expansion, don’t execute the line directly; instead, perform history expansion and reload the line into the editing buffer.
I know this already, I know that already, I know- Egads!
Esc + .
?! This is why I read these shell/editor tip listing posts. There's always a new trick to learn.
If you want to learn more about the hotkeys, they are from GNU Readline / Emacs and almost every input supports it
I should use crtl+u/y more often My goto for the same use case is ESC + #, which comments the current line and drop you at a new prompt. You can then recall the previous command from history and remove the # manually.
ZSH has C-q (push-line). It stores the current command, lets you execute something, and restores the command afterwards.
So instead of a C-u / C-y dance, I just C-q, do whatever and it restores my old command on its own. I can always C-q it again if I need to do something else.
Got some new ones! Thanks :D
I was combining pushd with - to get pushd - in some specific scenarios.
I'd love a shell that has all that interactive functionality present just like in classical shells, but where the scripting language is more modern.
Something like bash or zsh, but where arrays and hashmaps and functions feel like in any modern programming language. But without having to give up the former's interactive behaviour.
Does it exist?
There's elvish and ysh - both have their plus points over bash (but both can be thoroughly infuriating re: documentation and consistency.)
https://oils.pub/ specifically their YSH variant. It's bash backwards compatible in it's OSH variant. So all of your historical bash keeps working, but you can have YSH with nice for new stuff.
I get Alt-Left/Right working for moving words but I didn't know the cut to beginning and paste.
suddenly realize you need to check if the destination directory actually exists first
Generally I'd leave the command there and open a new shell window which defaults to the same working directory to check it. Then close the shell window after the check is done. This is as quick but works with stacks of explicit windows instead of implicit buffers.
I use vi mode in zsh. The mode used to be very limited but now it even has visual selection. This means that I get the niceness of all the movements I'm used to and I just checked that stuff like di) works (I had to check because I'm not thinking about it anymore).
Apparently, some of that is due to the following in my .zshrc:
# Add vim's text objects
autoload -Uz select-bracketed select-quoted
zle -N select-quoted
zle -N select-bracketed
for km in viopp visual; do
bindkey -M $km -- '-' vi-up-line-or-history
for c in {a,i}${(s..)^:-\'\"\`\|,./:;=+@}; do
bindkey -M $km $c select-quoted
done
for c in {a,i}${(s..)^:-'()[]{}<>bB'}; do
bindkey -M $km $c select-bracketed
done
done
And while we're on this topic, my t function that shows me the times and runtime of the 16 most recent commands I ran:
t() {
fc -l -d -D
}
The nice thing is that I don't have to prepend the command with time. The shell history simply records how long commands take and when they ran; this accesses it.
This depends on some options too:
HISTFILE=~/.histfile
HISTSIZE=100000
SAVEHIST=100000
setopt appendhistory extendedhistory
(well, appendhistory isn't required for this: it's for sanity)
Oh, and autocd so I type a directory name to cd to it without putting cd first.
And one last thing I've started using recently: directory hashes:
hash -d p=~/projects
Then, I can use 'p/foo' instead of '/projects/foo'. Of course, this ~p is pretty simple but you're free to have more complex ones (which I do but some are more personnal).