Bash Quick Substitutions
What I like about Linux, it doesn’t matter how many years of experience you have, you always can suddenly discover new and amazing things right under your nose.
Once, just ust a few years ago I causally entered sudo !!
and my ex-boss (CTO, Linux geek with Xmonad as WM and vim
as editor) who was sitting next to me exclaimed: “Wow?! How did you do that? Is it some hack?!” 🤯
Are you already laughing and feeling smarter than him? 😄 Well, well, don’t hurry. Have you heard, for example, about Bash quick substitution syntax ^Foo^Bar
?
I, until very recently, haven’t and now can’t stop making facepalms 🤦 every time when I remember how clumsy I was with the command line.
Okay, what’s the matter?
Entering ^Foo^Bar
in the Bash shell repeats the last entered line, but replaces in it the first occurrence of Foo
with Bar
.
It’s a short form of ^Foo^Bar^
(trailing ^
is in most cases optional, but see later), which is in order a short form of !!:s^Foo^Bar^
(here instead of ^
you can use other symbols, e.g. !!:s/Foo/Bar/
a good old sed
style).
It’s a very simple but very powerful trick that I use every day in many different scenarios.
Fixing Typos
The most obvious use case is fixing typos:
docker-compose log -f --tail=100
no such command, it should be “logs”:
^log^logs
Changing the Command
It’s often necessary to invoke different command with the same arguments. I use it all the time when navigating file hierarchy with ls
and cd
ls /etc/systemd/system
yeah, it’s the right directory let’s step it:
^ls^cd
Changing Arguments
It’s often necessary to invoke the same command with slightly different arguments. Consider something like:
docker inspect --size backend -f '{{ .SizeRootFs }}'
okay, now let’s see the size of database
^backend^database
In this example it might not be obvious that typing is faster than moving the cursor, but wait until you need to fix something in the middle of a wall of text that spans dozens of lines. The trick works also inside the quoted arguments, btw.
Replace All
The first limitation that you most probably face when you keep up using this syntax will be that it replaces only the first occurrence. For me it’s often necessary to replace all occurrences. Fortunately it’s easy. There are two ways to do it:
a) Add g
before s
in the “sed syntax”:
!!:gs^Foo^Bar^
(Note: Trailing ^
here is necessary)
b) Add :g&
after the “short” syntax (again, the trailing ^
is necessary):
^Foo^Bar^:g&
Dry Run
Another nice trick is :p
modifier. Invoking !!:p
will print the last command, but do not execute it. The best thing is that it doesn’t change the last command itself, so after inspecting it you can still execute it with !!
or edit with ^Foo^Bar
. The same modifier can be applied to a substitution command itself, i.e. to do a “dry-run” edit ^Foo^Bar^:p
.
Further Reading
If you are interested in the “theory” behind this syntax, open man bash
and search for “Event Designators”. There are more curious things that you can do, but as it often happens the most basic syntax covers 95% of all use cases and the remaining 5% are too complicated for practical use.
Anyway, I hope it was useful and you learned something new! 🙏