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.