Есть ли лучший способ выполнить команду N раз в bash?

Я иногда запускаю командную строку bash следующим образом:

n=0; while [[ $n -lt 10 ]]; do some_command; n=$((n+1)); done

Чтобы запустить some_command несколько раз подряд - 10 раз в этом случае.

Часто some_command - это действительно цепочка команд или конвейер.

Есть ли более краткий способ сделать это?

Ответ 1

for run in {1..10}
do
  command
done

Или в качестве одной строки для тех, кто хочет легко копировать и вставлять:

for run in {1..10}; do command; done

Ответ 2

Используя константу:

for ((n=0;n<10;n++)); do some_command; done

Использование переменной (может включать математические выражения):

x=10 for ((n=0; n < (x / 2); n++)); do some_command; done

Ответ 3

Еще один простой способ взломать его:

seq 20 | xargs -Iz echo "Hi there"

выполните эхо 20 раз.


Обратите внимание, что seq 20 | xargs -Iz echo "Hi there z" будет выводить:

Привет там 1
Привет там 2
...

Ответ 4

Если вы используете оболочку zsh:

repeat 10 { echo 'Hello' }

Где 10 - количество повторений команды.

Ответ 5

Используя GNU Parallel, вы можете:

parallel some_command ::: {1..1000}

Если вы не хотите, чтобы число как аргумент и выполнялось только одно задание за раз:

parallel -j1 -N0 some_command ::: {1..1000}

Смотрите видеоролик для быстрого введения: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1

Пройдите через учебник (http://www.gnu.org/software/parallel/parallel_tutorial.html). Вы в командной строке с любовью к вам за это.

Ответ 6

Простая функция в конфигурационном файле bash (часто ~/.bashrc) может работать хорошо.

function runx() {
  for ((n=0;n<$1;n++))
    do ${*:2}
  done
}

Назови это так.

$ runx 3 echo 'Hello world'
Hello world
Hello world
Hello world

Ответ 7

Другая форма вашего примера:

n=0; while (( n++ < 10 )); do some_command; done

Ответ 8

xargs быстро:

#!/usr/bin/bash
echo "while loop:"
n=0; time while (( n++ < 10000 )); do /usr/bin/true ; done

echo -e "\nfor loop:"
time for ((n=0;n<10000;n++)); do /usr/bin/true ; done

echo -e "\nseq,xargs:"
time seq 10000 | xargs -I{} -P1 -n1 /usr/bin/true

echo -e "\nyes,xargs:"
time yes x | head -n10000 |  xargs -I{} -P1 -n1 /usr/bin/true

echo -e "\nparallel:"
time parallel --will-cite -j1 -N0 /usr/bin/true ::: {1..10000}

На современном 64-битном Linux дает:

while loop:

real    0m2.282s
user    0m0.177s
sys     0m0.413s

for loop:

real    0m2.559s
user    0m0.393s
sys     0m0.500s

seq,xargs:

real    0m1.728s
user    0m0.013s
sys     0m0.217s

yes,xargs:

real    0m1.723s
user    0m0.013s
sys     0m0.223s

parallel:

real    0m26.271s
user    0m4.943s
sys     0m3.533s

Это имеет смысл, так как команда xargs - это единственный собственный процесс, который вызывает команду /usr/bin/true несколько раз, вместо циклов for и while, которые все интерпретируются в Bash. Конечно, это работает только для одной команды; если вам нужно выполнить несколько команд в каждой итерации цикла, это будет так же быстро или, может быть, быстрее, чем передача sh -c 'command1; command2; ...' в xargs

-P1 также можно изменить, скажем, на -P8, чтобы вызвать 8 процессов параллельно, чтобы получить еще один большой импульс в скорости.

Я не знаю, почему параллель GNU настолько медленная. Я бы подумал, что это будет сопоставимо с xargs.

Ответ 9

xargs и seq помогут

function __run_times { seq 1 $1| { shift; xargs -i -- "[email protected]"; } }

вид:

[email protected]:~$ __run_times 3  echo hello world
hello world
hello world
hello world

Ответ 10

for _ in {1..10}; do command; done   

Обратите внимание на подчеркивание вместо использования переменной.

Ответ 11

Во-первых, вы можете обернуть его функцией:

function manytimes {
    n=0
    times=$1
    shift
    while [[ $n -lt $times ]]; do
        [email protected]
        n=$((n+1))
    done
}

Назовите его так:

$ manytimes 3 echo "test" | tr 'e' 'E'
tEst
tEst
tEst

Ответ 12

Если вы в порядке делаете это периодически, вы можете запустить следующую команду, чтобы запускать ее каждые 1 сек бесконечно. Вы можете поместить другие пользовательские проверки, чтобы запустить его n раз.

watch -n 1 some_command

Если вы хотите визуально подтвердить изменения, добавьте --differences до команды ls.

В соответствии с man-страницей OSX также есть

Параметр --кумулятивный делает выделение "липким", представляя показывая все позиции, которые когда-либо менялись. -t или --no-title отключает заголовок, показывающий интервал, команды и текущее время в верхней части дисплея, а также после пустой строки.

Страница руководства Linux/Unix находится здесь

Ответ 13

Все существующие ответы, по-видимому, требуют bash и не работают со стандартным BSD UNIX /bin/sh (например, ksh в OpenBSD).

Следующий код должен работать на любом BSD:

$ echo {1..4}
{1..4}
$ seq 4
sh: seq: not found
$ for i in $(jot 4); do echo e$i; done
e1
e2
e3
e4
$

Ответ 14

Еще один ответ: используйте расширение параметра для пустых параметров:

# calls curl 4 times 
curl -s -w "\n" -X GET "http:{,,,}//www.google.com"

Протестировано на Centos 7 и MacOS.

Ответ 15

Я решил с этим циклом, где repeat - целое число, представляющее число циклов

repeat=10
for n in $(seq $repeat); 
    do
        command1
        command2
    done

Ответ 17

Для петель, вероятно, правильный способ сделать это, но вот забавная альтернатива:

echo -e {1..10}"\n" |xargs -n1 some_command

Если вам нужен номер итерации в качестве параметра для вашего вызова, используйте:

echo -e {1..10}"\n" |xargs [email protected] echo now I am running iteration @

Изменить:. Было справедливо прокомментировано, что приведенное выше решение будет работать гладко только с простыми запусками команд (без труб и т.д.). вы всегда можете использовать sh -c, чтобы делать более сложные вещи, но не стоит того.

Другим методом, который я использую, является следующая функция:

rep() { s=$1;shift;e=$1;shift; for x in `seq $s $e`; do c=${@//@/$x};sh -c "$c"; done;}

теперь вы можете назвать его как:

rep 3 10 echo iteration @

Первые два числа задают диапазон. @ будет переведен на номер итерации. Теперь вы можете использовать это и для труб:

rep 1 10 "ls [email protected]/|wc -l"

с указанием количества файлов в каталогах R1.. R10.

Ответ 18

Файл script

bash-3.2$ cat test.sh 
#!/bin/bash

echo "The argument is  arg: $1"

for ((n=0;n<$1;n++));
do
  echo "Hi"
done

и выход ниже

bash-3.2$  ./test.sh 3
The argument is  arg: 3
Hi
Hi
Hi
bash-3.2$

Ответ 19

Немного наивно, но это то, что я обычно помню с головы:

for i in 1 2 3; do
  some commands
done

Очень похоже на @joe-koberg ответ. Его лучше, особенно если вам нужно много повторений, мне сложнее вспомнить другой синтаксис, потому что в последние годы я много не использую bash. Я имею в виду не для скриптов, по крайней мере.