Skip to content

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 or zsh) looks if a variable named FCEDIT is configured. If you want to edit the command in Visual Studio Code, you can set FCEDIT='code --wait' somewhere in your .zshrc (or similar) file. If you wanted to use Nano, Vim or Neovim, you would set FCEDIT=nano, FCEDIT=vim or FCEDIT=nvim.
  • In case FCEDIT isn’t set, the shell then looks if the variable EDITOR is set. I recommend you definitely set this EDITOR variable (the way I just described it). Settings FCEDIT makes sense only if you expressly want to use a different editor for fc than, say, for editing git commit messages or the editor you normally use for the other things.
  • If neither FCEDIT nor EDITOR is set, then the editor that is set by your operating system will be used for fc. This could be vi or nano or ed.

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.

Buy Me A Coffee

Comments