Try ctrl-x ctrl-e to edit the command in your choice of editor ($EDITOR).
Editing commands is not hard with a few keys. At least learn alt-b and alt-f to go a word back or forward. Often ctrl-left and ctrl-right will do the same thing easier (depends on your terminal). Ctrl-w deletes back to the previous space, alt-backspace and alt-d delete the previous and next word respectively. There are many more keys in the manual, but those cover most things.
Same with Ctrl-C - once I got used to navigating by word instead of by character to correct things, it became much faster than starting over.
As suggested in the FAQ at the bottom, instead of !… you can use ctrl-r. Or better, optionally type the first letters then alt-p or "up". It's more powerful and more extensible. Other shortcuts, like alt-., allow navigating into the past parameters.
With up, alt-p, alt-. and such, the command line displays the real command. The !… magic is more error-prone and ambiguous; with zsh, I'd suggest hitting TAB to expand the magic into a static text.
I would add other entries to the FAQ: When I enter `sudo !!` will it be stored unchanged in the shell history, or stored after interpolation of `!!`? When I start a command line with a space, so that zsh does not put it in the shell history, will !… also ignore that line? Is `!$` recursive?
--
> When I enter `sudo !!` will it be stored unchanged in the shell history, or stored after interpolation of `!!`?
event-designators themselves do not end up in history, it will be the expanded contents:
$ echo "hello world"
hello world
$ echo !$:s/world/idoubtit/
echo "hello idoubtit"
hello idoubtit
$ history
1 echo "hello world"
2 echo "hello idoubtit"
3 history
The above is ironically enough quite annoying when writing a post about event-designators, as you'd want to reach for previous pre-interpolation commands.> When I start a command line with a space, so that zsh does not put it in the shell history, will !… also ignore that line?
leading space to ignore adding things to the history is not default behavior, but event designators will still be able to capture their contents. Notice how "echo 321" is _not_ part of the history, but our usage of !$ is:
% setopt HIST_IGNORE_SPACE
% echo "abc"
abc
% echo "321"
321
% echo last-argument-was !$
echo last-argument-was "321"
last-argument-was 321
% history
22092 setopt HIST_IGNORE_SPACE
22093 echo "abc"
22094 echo last-argument-was "321"
> Is `!$` recursive?I think this is indirectly answered by the above, if not let me know and I will see if I can answer.
---
Will try to update the post within the next few hours with your additional questions — much much appreciate the feedback, and thanks for reading!
really appreciate the feedback, thanks a million!
!! you can do just as easily with up-arrow enter, which is just as quick. !ssh you can do with ctrl+r, which actually shows you what it's going to run and whether the last ssh is to where you think it is. sudo !! is up arrow, ctrl+a, 'sudo ', one more keystroke but it's natural if you know your readline. The rest are too complex and not worth learning. !$ is the sweet spot.
!$:h is new to me. directory name of last argument is nice. I can imagine reaching for that. paired with alt-^ to replace the magic with the actual expanded text before hitting enter
I've been liking Esc+. for this. It doesn't usually work in browser sessions though, have to do Ctrl+[+. which is a strange feeling 3 finger salute. For some reason I'm able to remember that one rather than !$
Edit: apparently Alt+. does the same, wow.
sudo !!Those people want to use:
Alt-f move cursor forward one word
Alt-b move cursor back one word
Alt-d delete to the end of the next word
Alt-Del delete backwards to the start of the previous word
Ctrl-_ undo
Here's a cheatsheet: https://readline.kablamo.org/emacs.htmlThis also adds an intriguing new dimensions to reviewing/verifying shell commands as benign and correct... How long until LLMs start (ab)using the trick in little requests they want me to approve?
There's enough complexity in the language spec that most people stick with a (fairly shared) subset.
Bash can be very powerful, but IMHO there's a certain point where I find it saner to accomplish the same thing via Python.
In as most sensible people use vi, don't taint yourself by touching this foul and evil emacs magic. Being known as an emacs user could cost you a job, shorten your career, or even cause rifts with family and friends.
Instead my friends, if you must use such bash shenanigans, switch it to vi extensions. You'll feel better about yourself, stand taller, and be a better human being as a result.
Be safe.
You are a soothsayer!
No, it isn't limited to just that. Alt-0 alt-. will give you the zeroth "argument" (the program), alt-1 alt-. will get you the next (the program's first argument), etc. Negative arguments count back from the end: alt-- 1 alt-. will get you the argument before the last one (note that alt is held for the "-" but not the digit).
When you do use event designators, ctrl-alt-e (or alt-^) is critical. It expands all the ! codes inline to what they refer to, so you can see what you're doing before you hit enter, and as you're constructing the command too.
---
You are very much correct that `Alt-.` with modifiers can do more than what has been described, it is however important to note that the behavior of `Alt-<key>` changes heavily both between zsh and bash, but also between `vi-mode` and `emacs-mode`.
For instance, zsh will switch to `vi-mode` depending on env variables such as `$EDITOR`, making `Alt-<key>` unavailable in that regard.
---
It's also worth noting that `ctrl-alt-e` is _not_ a zsh default, though it is a fair point that it could have been added in the below linked section:
https://refp.se/articles/your-shell-and-the-lazy-exclamation...
For what it's worth, I had troubles finding a proper scope for the article contents — and with that the lines became a bit blurry in terms of what to include, and what not to include.
---
I really appreciate constructive comments like yours, and hope it together with the article can help future shell ninjas!
This will expand the event designator after you type space to end it. So you type “!” “$” “space” and the designated argument expands in-place.
And yeah, I've done editing with ed; it definitely beats "cat >file.txt" and copying parts around with head/tail and retyping the corrections manually, sure, and it works in every environment that can take line-oriented input from the user (so, literally everywhere), but that's about as much praise as I can give it.
Right, as a matter of (relatively trivial) cybernetics between man and machine, it rests on the weaker parts of the human.
TBF, it'd make a lot more sense if every command on screen (or given the timescale perhaps teletype paper prinout) was already labeled with the necessary number.
From personal experience I rarely reach for event-designators beyond the current viewport of the terminal. Referring to a command beyond that is, as you very much correctly point out, an easy way to get behavior you don't want. However, if you are always referring to things that are near-memory or even still directly visible in your terminal buffer — the gun-to-foot ratio becomes manageable. I use many (many) of these daily without issues, but it does require some additional discipline.
Also, and this I find important to note, `!!` and `!$` sit outside of the memory argument as they always refer to the last command and last-command's last-argument respectively — as such there shouldn't be any recall related problems (unless you are context switching and come back to a shell in a state you don't remember).
A common occurrence when my posts acquire attention is that I retroactively realize that some things could have been conveyed better; I read every comment and yours is a blessing in disguise to improve future writing.
Thanks for reading the article, and thanks for indirectly making me a better writer!
---
EDIT:
by the way, for what it's worth, the below linked section describes how to effectively explicitly confirm what you would run as a safety measure if unsure:
https://refp.se/articles/your-shell-and-the-lazy-exclamation...
P.S. The alt+s shortcut is much quicker than writing sudo !!
trying to write shell commands on a hypervisor console window within a remote session on a jump box is excruciatingly laggy and annoying, and that's assuming your remote session connection to the client is behaving well in the first place
I'm way too paranoid to ever use "!". I want to see the command I'm sending.
!! is handy for the sudo example, but beyond that, I tend to prefer pressing the up arrow to cycle through the history rather than picking the first item. Eg:
!ssh
vs ssh[up]
It’s the same number of key strokes but less error prone> I've been burnt before using ! and forgetting what command I typed previously
If you want to make sure you do not execute commands you did not intend to, the section linked below is a great start:
- https://refp.se/articles/your-shell-and-the-lazy-exclamation...
You may also look into `magic-space` which makes the expansion of event-designators happen inline, I thought about including it but the article felt long even without it.
Thanks for reading the article, I hope it was a worthwhile read even with alternate workflows elsewhere!
bind space:magic-spaceand yeah ^r works, though i often type a few chars and ^p till i get the match. history-search-backward in your inputrc, iirc
edit: on a computer now :) my `if emacs` block, allowing arrows or ^n/^p to run through history. so for e.g., type "tar" and now as you shift through history with :binding:, it only shows history that began with "tar"
$if mode=emacs
"\e[A": history-search-backward
"\e[B": history-search-forward
"\C-p": history-search-backward
"\C-n": history-search-forward
...
$endif``` mkcd() { mkdir -p -- "$*" && cd -- "$*" } ```
That's called a shell script. I don't know what others thresholds are but mine is about three pipelines deep(or 80 characters whatever comes first) and I am going to write that son of a gun as a script. Mainly to have something named in the filesystem I can edit, same with my sql queries, I have a whole directory of long awkward mostly one off queries, psql has the \e command which helps but again I would rather have something named.
just turn on the thing where a leading space doesn't commit to history and you can do this already.
For this reason I recommend avoiding using history expansion features with any commands in history that might destructively modify file contents, such as `find ... -delete` in GNU find.
In the default Bash Emacs readline bindings, you can type Alt/Meta-^ to expand history on the current line without executing. You can also set `shopt -s histverify` to expand the history substitution on the following line after pressing enter/submitting the history substitution and it also does not execute the command.