How to Quickly Replace a Parameter in a Piped Command Chain
That's exactly what history expansion is for:
$ command-producing-multi-line-output | grep -i "needle" | wc -l
...
$ ^needle^nipple
command-producing-multi-line-output | grep -i "nipple" | wc -l
...
I've run into a similar situation and present my solution here just in case it's a useful pattern.
Once I realize that I'm repeatedly changing one piece of data that's annoying to replace interactively, I'll stop and write a little while
loop:
$ command-producing-multi-line-output | grep -i "needle" | wc -l
0
$ command-producing-multi-line-output | grep -i "haystack" | wc -l
0
$ while read needle; do
Up-Arrow to the previous command-producing-multi-line-output
line and replace "haystack"
with "$needle"
command-producing-multi-line-output | grep -i "$needle" | wc -l; done
something
0
something else
0
gold
1
(ending with Control+D)
Or the slightly fancier variation:
$ while read needle; do
printf 'For: %s\n' "$needle"
command-producing-multi-line-output | grep -i "$needle" | wc -l; done
If I see a future for this command-line beyond "today", I'll write it into a function or script.
A useful alias in bash
is
alias r='fc -s'
The r
command is often found in other shells by default1, and repeats the most recent command in history. The bash
manual even refers to this as a useful alias to define:
[...] A useful alias to use with this is ``r="fc -s"'',
so that typing ``r cc'' runs the last command beginning with
``cc'' and typing ``r'' re-executes the last command.
It will also allow you to do replacements in the text of the last command.
Here I'm running your command, then I use the above alias to replace the word needle
with the word haystack
:
$ command-producing-multi-line-output | grep -i "needle" | wc -l
bash: command-producing-multi-line-output: command not found
0
$ r needle=haystack
command-producing-multi-line-output | grep -i "haystack" | wc -l
bash: command-producing-multi-line-output: command not found
0
When I use r needle=haystack
on the command line, the shell print out the command it's going to run and then immediately runs it. As you can see, it also replaces the word.
The errors are obviously due to me not having a command-producing-multi-line-output
command, but that's not important in this exercise.
The fc
command won't get saved to your history, but you can make it get saved by creating it as a shell function like this:
fc() {
command fc "$@"
history -s fc "$@" # append the given fc command to history
}
You may then do
$ command-producing-multi-line-output | grep -i "needle" | wc -l
bash: command-producing-multi-line-output: command not found
0
Rerun the most recent command starting with comm
and replace needle
using r needle=haystack comm
:
$ r needle=haystack comm
command-producing-multi-line-output | grep -i "haystack" | wc -l
bash: command-producing-multi-line-output: command not found
0
Rerun the most recent command, but replace haystack
, using r haystack=beeswax
:
$ r haystack=beeswax
fc -s needle=beeswax comm
command-producing-multi-line-output | grep -i "beeswax" | wc -l
bash: command-producing-multi-line-output: command not found
0
Note the double call that gets made to fc
in that last line, first through our alias r
and then through our function fc
.
Obviously, saving the fc
command to history makes you run the risk of accidentally calling fc -s
recursively.
1In zsh
it's a built-in command, in OpenBSD ksh
it's a default alias for fc -s
, in ksh93
it a default alias for hist -s
, etc.