3

I have the following pipeline:

➜ echo ,cats,and,dogs, | sed -e 's/,[^,]*,[^,]*,/,,,/'
,,,dogs,

I know that I could run a command like !! to "run the last command" or !:1 to "get the last arguments" but I'm wondering is there some command that I can run that will let me "get the kth command+args from a pipeline"

So in this example if I wanted to pipe some other output into the sed utility I could do something like this right after running the above pipeline:

$ echo ,foo,bar,baz, | %:2

where %:2 is some maybe-fictional command that I don't know, that "runs the kth command in a pipeline" Does this command exist?

Gilles 'SO- stop being evil'
865k204 gold badges1.8k silver badges2.3k bronze badges
asked Oct 12, 2016 at 6:46
4
  • Why the bash tag if you're using zsh? There are plenty ways to address it better than with history expansion (IMO, I'm not a big fan of history expansion) with zsh. Commented Oct 12, 2016 at 7:56
  • Just updated tags! What other more zsh specific ways are you thinking of? Commented Oct 12, 2016 at 8:10
  • For instance a widget with numerical argument bound to some key. So that you'd type Alt+2 Alt+Shif+P for instance to insert the last 2 commands from the last pipeline, similar to Alt+_ Commented Oct 12, 2016 at 8:30
  • You might just want to use fc, which can open a requested command in your favorite text editor for editing before executing it. Commented Oct 13, 2016 at 2:04

2 Answers 2

3

History expansion was great in the 80s (when introduced by csh) when terminals were very slow and limited. The problem though is that they're not WYSIWYG. You don't see what it expands to until it's too late (the command is already started) (though some shell allow to perform the expansion before pressing Enter).

Here, I'd create a widget that inserts the last pipeline element at the cursor. With zsh (add it to ~/.zshrc):

last-pipe-elements() {
 emulate -L zsh
 if [[ $WIDGET = $LASTWIDGET ]]; then
 # Invoking the widget several times retrieves pipeline
 # elements for older command lines
 ((last_pipe_elements_iteration++))
 LBUFFER=$last_pipe_elements_saved_LBUFFER
 else
 last_pipe_elements_iteration=1
 fi
 last_pipe_elements_saved_LBUFFER=$LBUFFER
 local -a words
 words=(${(z)${history[@]}[last_pipe_elements_iteration]})
 local nth_pipe=${words[(In:NUMERIC:)\|]}
 ((nth_pipe)) || return
 LBUFFER+=${(j: :)words[nth_pipe,-1]}
}
zle -N last-pipe-elements
bindkey '\eP' last-pipe-elements

That way, you enter Alt+Shift+P (here assumed to send the ESC and P character sequence, you may need to adapt it for your terminal that may send \xd0 ($'\MP') or even \xf0 ($'\Mp') or something completely different, check with Ctrl+V) to insert the last pipeline element (including the |) of the last command, press it again for the command before that, and use Alt+2Alt+Shift+P for the last 2 pipe elements instead.

(z) invokes the shell parser to split the command lines into words. We find the | we want and then join the words on the right of that with one space. That means that |tr a b will become | tr a b, but that shouldn't affect the meaning of the command as long as you don't use complex multi-line constructs (sed 's/ /x/' will not be changed to sed 's/ /x/' for instance).

If you don't want to define a new widget, another thing you can do is search back (with Ctrl+R assuming emacs mode, though you can do the equivalent in vi mode) for the previous |, delete to the end of the line with Ctrl+K and get back to your command (Down) to paste it with Ctrl+Y. That would still be quicker than using history expansion and would still be visual (you can better visually select which | you want to cut from).

answered Oct 12, 2016 at 9:00
4
  • Just set shopt -s histverify in bash and always see the history expansion before execution (that will change readline behavior) Commented Oct 12, 2016 at 11:18
  • In zsh the same utility is set by HIST_VERIFY. Commented Oct 12, 2016 at 11:45
  • Thanks you @Stephane! I'm having trouble getting my bindkey to work. I'm using OSX 10.11 which I think may be the problem I'm prety sure \M is the "option" or "meta" key but neither your version nor bindkey '\MP' last-pipe-elements seems to be working, any thoughts? Commented Oct 13, 2016 at 9:04
  • @mbigras, Yes, that \eP is for terminals that send ESC P upon Alt+Shift+P. Check what character or character sequence your terminal sends. It might be that it sends the same as for Alt+P (one of the most useful widgets in tcsh/zsh) in which case you may want to use a different key or key combination. Commented Oct 13, 2016 at 9:18
3

Don't know how to get "after the pipe" but "after the third white space" may help:

$ echo ,cats,and,dogs, | sed -e 's/,[^,]*,[^,]*,/,,,/'
,,,dogs,
$ echo ,cat,and,mouse, | !:3-$
,,,mouse,

Workaround for "after pipe":

$ echo ,cats,and,dogs, | sed -e 's/,[^,]*,[^,]*,/,,,/'
,,,dogs,
$ after_pipe="$(cut -d "|" -f 2- <<<"!!" )"
$ echo ,cat,and,mouse, | eval "$after_pipe"
$ ,,,mouse,

(cut would need to be something better if there are quoted pipes "|" in the first command.)

answered Oct 12, 2016 at 7:08
6
  • wow, this seems like a great option so far. Kind of a bummer because by default my pipes get|stuck|togeher|like|this but I think it's a setting that oh-my-zsh did that I'm sure I could change. What is !:3-$ called? I've been googling around for bash history expansion but I couldn't find. Commented Oct 12, 2016 at 7:18
  • 1
    @mbigras, it's still called history expansion. You won't find it online very much because it's not well known. Look at the man page instead: LESS='+/^HISTORY EXPANSION' man bash Commented Oct 12, 2016 at 7:24
  • @Wildcard thanks for that, I also found a thegeekstuff tutorial that looks pretty good Commented Oct 12, 2016 at 7:43
  • 1
    See also !:3* as a shortcut for !:3-$ Commented Oct 12, 2016 at 7:52
  • It is a good idea to set shopt -s histverify in bash to always see the history expansion before execution (that will change readline behavior). Commented Oct 12, 2016 at 11:19

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.