..

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! 🙏