A time-saving way to re-run your last terminal command but with substituted parts
Abstract
Introducing the shell command fc
and its option -s
.
I already wrote two articles about the Z shell’s built-in r
(with no or one argument and with two arguments) which really only is an alias for a particular use case of the command fc
. So today, let’s stop scratching the surface that is the command r
and dig deeper by examining the command fc
. There’s a lot more to discover about that fc
command.
Introducing the command fc
#
The name of the command fc
is “fix command”.
Imagine the command you just executed contained a typing mistake. For example, you wanted to update pip
, but instead of executing pip install --upgrade pip
you executed pip install --update pip
. Sure, you could fix your mistake how you perhaps currently do it, by
- pressing the Up arrow key on your keyboard to retrieve the previous command from your history,
- then using the Left arrow key and the Backspace key a couple of times, and
- then pressing Enter again.
A much more efficient way to change update
to upgrade
would be to simply type fc
into the terminal. By executing just fc
, the text editor configured in your configuration files or by your system will open to let you conveniently edit the previous command and fix your mistake. This would be especially convenient, and more efficient than the methodology I just described, if you knew how to use Vim. As soon as you save and close that text editor, the corrected command will be executed. Of course, you can choose to not edit the command and simply close the text editor. Then that previous command would be re-run as is.
Specifying the editor to be used by fc
#
You can configure which text editor should open when you execute fc
. The way it works is, as is usual with Unix-like systems, by checking a series of options:
- First, the shell (
bash
orzsh
) looks if a variable namedFCEDIT
is configured. If you want to edit the command in Visual Studio Code, you can setFCEDIT='code --wait'
somewhere in your.zshrc
(or similar) file. If you wanted to use Nano, Vim or Neovim, you would setFCEDIT=nano
,FCEDIT=vim
orFCEDIT=nvim
. - In case
FCEDIT
isn’t set, the shell then looks if the variableEDITOR
is set. I recommend you definitely set thisEDITOR
variable (the way I just described it). SettingsFCEDIT
makes sense only if you expressly want to use a different editor forfc
than, say, for editinggit
commit messages or the editor you normally use for the other things. - If neither
FCEDIT
norEDITOR
is set, then the editor that is set by your operating system will be used forfc
. This could bevi
ornano
ored
.
In case you just once want to use a different editor than the one you specified in any of these variables, you could also pass that editor to the fc
command via the command-line flag -e
and thereby claim precedence over the value of the variable. For example, fc -e nano
if you normally use Neovim but want to teach this command to a co-worker who would be more comfortable with nano
than with nvim
.
Forgoing the opportunity to modify the command before executing it#
All these ways I described up until now have in common that they provide you the opportunity to modify the previous command before you execute it. They all open a text editor where you can make your changes to “fix” the last command in your history. But what if you don’t need to make any modifications and just want to re-run the previous command as is? Then you can simply pass -
as your desired editor. In other words, you’d call fc -e -
. That would immediately re-run the previous command without opening a text editor first or giving you the opportunity to modify (or “fix”) the previous command. And that is exactly what the zsh
built-in r
does (see my other article). The built-in r
is simply an alias for fc -e -
that is pre-configured by zsh
but not by bash
.
Introducing the option -s
#
Up until now, we only recapped what the zsh
built-in r
does and learned about what happens (or could happen) in the background when you use the command that underlies r
.
Now I want to come to the main part of this article: the thing I learned today. Everything you’ve learned so far is only one of the use cases of the command fc
. Now I want to talk about the other main use case of fc
: substituting occurrences of a given string within a previous command by using the option -s
.
Example#
Imagine you ran the command apt update && apt upgrade
on your Debian/Ubuntu-based machine and then realized that your forgot sudo
for both apt
commands.
The previous articles in this series have been about how you can use the command r apt
to re-run the last command that starts with apt
as is (or simply r
if you want to re-run the very last command you ran), but that won’t help in this case. For the same reason, fc -e - apt
won’t do, since that is exactly the same as r apt
. Using the knowledge you just gained in the previous section, you could use just fc
or fc apt
to open the last command that starts with apt
in the text editor you assigned to the variable EDITOR
. Then, you could manually put sudo
before the two occurrences of apt
(remember: we want to fix apt update && apt upgrade
). But there’s a more efficient way.
One way to phrase our problem is that we want to replace all occurrences of apt
in the last command that starts with apt
, with sudo apt
. The command fc
can be called with the option flag -s
which stands for substitution. To actually substitute something, you need to provide fc
with an optional substitution definition (of the form old=new
). If you don’t tell fc
what you want to replace by what, then fc
will just re-run the command unmodified.
This means you can tell fc
to fix the command apt update && apt upgrade
with this command:
fc -s apt="sudo apt" apt
That would immediately execute sudo apt update && sudo apt upgrade
without an editor even opening and the need to manually do the substitution. You could of course also call just fc -s apt="sudo apt"
if the command you want to fix is the last command in your history.
As I said, the substitution definition is optional. You could also run fc -s apt
or just fc -s
, which would be equivalent to calling the Z shell’s built-in r
with no or one argument. The two commands fc -e - apt
and fc -s apt
are therefore equivalent (same as fc -e -
and fc -s
).
Another example#
Going back to the very first example (mistakenly using --update
rather than --upgrade
with pip install
to update a Python package), you could very efficiently execute the correct command with:
fc -s update=upgrade pip
In case you didn’t run another command after you botched up updating the Python package, this very elegant command would even suffice already:
fc -s date=grade
Final words#
Today I learned that there’s an option flag -s
for the command fc
that efficiently substitutes all occurrences of a given string in the previous command with the other given string. Today I also gained a bit more background knowledge about the zsh
built-in r
. Hopefully, you could learn something from this post too. In any case, be well.