7

Short of writing a loop, is there a way to repeat the last command N times.

For example, I can repeat the last command once by using a double bang (!!), but how do I repeat it say 30 times?

9

With zsh, and provided the last command line was only one command or pipeline or and-or list (that is for instance echo x, echo x | tr x y, echo x && echo y, even compound commands like { x; y; } or for/while loops but not echo x; echo y):

repeat 30 !!

To repeat the previous command line even if it contained several commands, use:

repeat 30 do !!; done

Or:

repeat 30 {!!}

With bash and for simple-commands only (among the examples above, only the echo x case), you could define a helper function like:

repeat() {
  local n="$1"
  shift
  while ((n-- > 0)); do
    "$@"
  done
}

(and use repeat 30 !! like above). A side effect is that because the code will be running in a function, it will see different "$@", "$#" and things like typeset will work differently, so you can't do things like:

eval 'echo "$1"'
repeat 30 !!

Another approach to emulate zsh's repeat 30 {!!} would be to declare an alias like:

alias repeat='for i in $(seq'

(assuming an unmodified $IFS)

And then use:

repeat 30); { !!; }
7

The shortest I can come up with is:

date # or whatever command
for i in {1..30}; do !!; done
  • 2
    That has a loop. – Tyler Durden Jun 21 '17 at 12:54
  • 4
    You can shorten it to for i in {1..30};{ !!;} – Stéphane Chazelas Jun 21 '17 at 13:05
3

One approach could be to use the line editor to insert !!; 30 times.

Like with readline (bash's line editor) in vi mode:

!!;Escdd30p

The emacs mode equivalent does work with the zsh line editor but apparently not with bash's readline. However you could use readline kbd macros instead which apparently can be repeated:

Define the kbd macro as !!;:

Ctrl+X(!!;Ctrl+X)

Which you can later invoke 30 times as:

Alt+3Alt+0Ctrl+Xe
1

This is a bit ugly, but:

 eval "`fc -ln -1`;: "{1..10}\;

The leading space is not strictly necessary, but is useful to suppress entering the eval command into history if $HISTCONTROL contains ignorespace (or ignoreboth).

Alternatively:

eval "fc -s $((HISTCMD-2)) "{1..10}\;

And:

eval 'history -s '{1..10}';fc -s -2;'
0

The seq command is part of standard *nix and therefore not dependent on your shell. Using it and your shell's loop construct you can do things like this:

for i in $(seq 30); do !!; done

or

for i in `seq 30`; do !!; done

0

Another loop in bash (e.g. after this: https://unix.stackexchange.com/a/372487/9689 )

Make it a executable file somewhere in your $PATH named repeat to achieve similar effect like in zsh (https://unix.stackexchange.com/a/372484/9689):

#!/bin/bash

n=$1
shift 1

for ((x=0; x<$n; ++x)); do
  "$@"
done

so later you can:

$ repeat 3 date

gist: https://gist.github.com/gwpl/26fefe3a165304f3dedb21009506107f

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.