bash Command Line – Quickly Replace Parameter in Piped Command Chain

bashcommand historycommand line

Question

Let's say I just entered this command to get a count of how many lines contain a particular string:

me@machine $ command-producing-multi-line-output | grep -i "needle" | wc -l

Now how could I quickly replace "needle" with some other word?


Current Inefficient Solution

Right now I:

  1. Press Up on the keyboard to load the last command.
  2. Press Left or Ctrl+Left until I reach "needle".
  3. Press Backspace or Ctrl+w to delete the word.
  4. Type or paste in the new word.
  5. Hit Enter.

This seems pretty inefficient.


Attempted Non-working Solutions

I've tried to research history shortcuts like !!:sg/needle/new-needle; but there are a few problems with this:

  1. You can't press Up to put !!:sg/needle/new-needle back on the command line. Doing this will just show what it expands to (running right back into the original problem).
  2. Repeating this with yet another new needle requires you to replace both needle and new-needle (i.e. !!:sg/new-needle-from-before/yet-another-new-needle).
  3. You need to type the entire needle instead of using something like !:4 or $4 (i.e. !!:sg/!:4/new-needle or !!:sg/$4/new-needle) to save time/keystrokes on however long the needle was.

I've also found things like !:^-3 "new-needle" !:5-$ but that also has issues:

  1. It expands in history so it can't be re-used quickly.
  2. Even if it didn't expand, you run into the original problem of needing to replace a word in the middle of a command chain.

I believe there has to be some super fast way to do what I want to do, and that some linux gurus out there know it. I would be very grateful for any input or suggestions on this.


EDIT:

Background

Just a bit of background, I work a lot with OpenStack on the command line and I find myself often needing to replace a parameter in several places within a long command chain of piped commands.

The way our OpenStack environments are configured, several engineers share a single stack user on any number of servers, and there are several clusters within multiple environments. So a function declared within .bashrc or .profile file isn't really possible.

I'm hoping for something portable that can be used quickly with no or very minimal setup required. Otherwise I may just need to resort to using a solution outside of the shell entirely (such as clipboard replacement in AutoHotKey/Keyboard Maestro/AutoKey, etc.).

Best Answer

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.

Related Question