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.

  • 9
    Is !77 ; !78 ; !79 okay? – thrig Oct 23 '17 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 '17 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 '17 at 1:50
  • It's magical! :) – MrDuk Oct 24 '17 at 1:52
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:

  • 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. – G-Man Oct 23 '17 at 18:55
  • 3
    eval "$(history -p \!{77..79})" should work. – Michael Hoffman Oct 24 '17 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 '17 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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.