20

Suppose I have the following trivial example in my history:

...
76 cd ~
77 ./generator.sh out.file
78 cp out.file ~/out/
79 ./out/cleaner.sh .
80 ls -alnh /out
...

If I wanted to execute commands 77, 78, and 79 in one command, does there exist a shortcut for this? I've tried !77 !78 !79, which will simply place them all on a single line to execute.

4
  • 9
    Is !77 ; !78 ; !79 okay?
    – thrig
    Oct 23, 2017 at 18:33
  • 1
    I guess anything short of typing line by line is ok, but was hoping for something a bit more concise (along the lines of !77-79)
    – MrDuk
    Oct 23, 2017 at 18:34
  • 1
    @MrDuk, boy did that take me a while to remember. See my updated answer for the very concise solution! :)
    – Wildcard
    Oct 24, 2017 at 1:50
  • It's magical! :)
    – MrDuk
    Oct 24, 2017 at 1:52

2 Answers 2

28

EDIT: You can do this in POSIX-compliant fashion with the fix command tool fc:

fc 77 79

This will open your editor (probably vi) with commands 77 through 79 in the buffer. When you save and exit (:x), the commands will be run.


If you don't want to edit them and you're VERY SURE you know which commands you're calling, you can use:

fc -e true 77 79

This uses true as an "editor" to edit the commands with, so it just exits without making any changes and the commands are run as-is.


ORIGINAL ANSWER:

You can use:

history -p \!{77..79} | bash

This assumes that you're not using any aliases or functions or any variables that are only present in the current execution environment, as of course those won't be available in the new shell being started.


A better solution (thanks to Michael Hoffman for reminding me in the comments) is:

eval "$(history -p \!{77..79})"

One of the very, very few cases where eval is actually appropriate!


Also see:

3
  • 2
    This will not work correctly if any of the commands changes the current execution environment, e.g., by changing a variable or doing a cd, but that is an edge case. Oct 23, 2017 at 18:55
  • 3
    eval "$(history -p \!{77..79})" should work. Oct 24, 2017 at 1:29
  • 1
    @MichaelHoffman, I had thought of that and thought I had tried it, but I just realized I left out quoting! D'oh! No wonder it didn't work...editing answer now.
    – Wildcard
    Oct 24, 2017 at 1:38
14

Simple answer: instead of !77 !78 !79,

  • type !77; !78; !79 to do what you seem to be trying to do — execute command 77, and then command 78, and then command 79, unconditionally, or
  • type !77 && !78 && !79 to execute command 77 and check whether it succeeded.  And then, if command 77 succeeded, execute command 78.  And then, if command 78 succeeded, execute command 79.

Slightly cleverer answer (if you are in a terminal-type window):

  • Figure out what command number the next command you type will be.
    • It is possible to include this in your prompt; I believe it’s by including \! in your PS1.
    • Or look at your history listing.  If the last entry is 82 history, then your next command is 83.
  • Subtract 83−77=6.
  • Type !-6.  This will re-execute command 77.
  • Select the !-6, copy it, and paste it.  Since your re-execution of command 77 (./generator.sh) was command 83, you are now on command 84, so !-6will re-execute command 78.
  • Repeat pasting !-6 as desired.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .