Повторите команду Unix каждые x секунд навсегда

480

Есть встроенная команда Unix repeat, первым аргументом которой является количество повторений команды, где команда (с любыми аргументами) определяется оставшимися аргументами repeat.

Например,

% repeat 100 echo "I will not automate this punishment."

повторяет данную строку 100 раз и затем останавливается.

Я хотел бы подобную команду - давайте назовем ее forever- которая работает аналогично, за исключением того, что первый аргумент - это количество секунд для паузы между повторениями, и это повторяется навсегда. Например,

% forever 5 echo "This will get echoed every 5 seconds forever and ever."

Я думал, что спрошу, существует ли такая вещь, прежде чем я напишу это. Я знаю, что это как двухстрочный скрипт на Perl или Python, но, возможно, есть более стандартный способ сделать это. Если нет, не стесняйтесь опубликовать решение на вашем любимом языке сценариев, стиле Rosetta Stone .

PS: Возможно, лучшим способом сделать это было бы обобщение, repeatчтобы взять как количество повторений (с -1 означает бесконечность), так и количество секунд для сна между повторениями. Приведенные выше примеры станут:

% repeat 100 0 echo "I will not automate this punishment."
% repeat -1 5 echo "This will get echoed every 5 seconds forever."
dreeves
источник
1
Почему бы не повторить команду [-t time] [-n number] [args ...]?
Джонатан Леффлер
17
Здорово. К сожалению , как на моем Ubuntu и мой AIX повторить это команда не найдено . Можете ли вы сделать, type repeatи дайте мне знать, откуда приходит?
3
Я также на Ubuntu и у меня не установлен повтор. Любая информация о том, как установить это было бы полезно.
Рори
7
repeatвстроенная команда в csh и tcsh
Кит Томпсон
2
@KeithThompson, csh, tcsh и zsh . Хотя это больше ключевое слово, чем встроенный
Стефан Шазелас

Ответы:

615

Попробуйте watchкоманду.

Usage: watch [-dhntv] [--differences[=cumulative]] [--help] [--interval=<n>] 
             [--no-title] [--version] <command>`

Так что:

watch -n1  command

будет выполнить команду каждый второй (ну, технически, каждый второй плюс время, необходимое для commandбежать как watch(по крайней мере, procpsи busyboxреализации) просто спит одна секунда между двумя отрезками command), навсегда.

Если вы хотите передать команду execвместо sh -c, используйте -xопцию:

watch -n1 -x command

На macOS вы можете получить watchиз Mac Ports :

port install watch

Или вы можете получить его от Homebrew :

brew install watch
Грегор Брандт
источник
2
Вы можете использовать MacPorts для его установки. смотреть порт sudo
10
Также доступно в доморощенных (варево установить часы).
Лайл
28
+1 За новый мощный инструмент. Я привык while true; do X; sleep Y; done- это намного лучше.
Адам Матан
4
Проблема с этим инструментом (по крайней мере в Ubuntu) заключается в том, что он запускает полноэкранный режим, поэтому, если я сбрасываю некоторую периодическую информацию, я теряю предыдущую информацию.
scorpiodawg
4
Он вызывается cmdwatchво FreeBSD (из портов:) sysutils/cmdwatch.
Ouki
243

удар

while+ sleep:

while true
do 
    echo "Hi"
    sleep 1
done

Вот то же самое, что сокращенная однострочная (из комментариев ниже):

while sleep 1; do echo "Hi"; done

Использует ;для разделения команд и использует sleep 1для whileтеста, так как он всегда возвращает true. Вы можете поместить больше команд в цикл - просто разделите их с помощью;

Ошибка синтаксиса
источник
2
Я считаю, что это работает так же, как написано в универсальной оболочке Bourne.
dmckee
5
@dreeves, используя ";" в качестве разделителя команды могут быть указаны в одной строке. Посмотрите на ответ Toony в качестве примера
bbaja42
56
sleep 1 всегда возвращает true, поэтому вы можете использовать его как условие while. Не самая читаемая вещь в мире, но абсолютно законная: во время сна 1; сделать эхо "Привет"; сделано
Дэвид Коста
3
@ Дэвид Коста: Вы сделали разумное замечание, но sleepне всегда возвращаете правду. Ī̲ использовал такие циклы (хотя и с более длительной задержкой) именно потому, что есть способ изящно завершить его, убив процесс сна.
Incnis Mrsi
2
@IncnisMrsi Я не думал об этом, на самом деле он возвращает ноль только по прошествии времени и ненулевой, когда он завершается сигналом. Спасибо за указание на это
Дэвид Коста
71

Это просто более короткая версия других while+sleepответов, если вы выполняете такие задачи часто, как свою повседневную жизнь, использование этого избавляет вас от ненужных нажатий клавиш, и если ваша командная строка начинает становиться длиннее, ее понимание будет немного проще. Но этот начинается со сна в первую очередь.

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

while sleep 1; do uptime; done
Бекир Доган
источник
Да. Это должно быть задокументировано рядом с UUOC.
Йохан
10
И если вы хотите, чтобы он работал как «часы», вы можете сделатьwhile clear; do date; command;sleep 5; done
Johan
Ничего себе, спасибо за while sleepкомбинацию, экономит нажатия клавиш!
Джордж Ю.
45

Одна из проблем, которые есть во всех ответах, опубликованных до сих пор, заключается в том, что время выполнения команды может измениться. Например, если вы выполняете sleep 10команды между командами, а выполнение команды занимает 2 секунды, то она будет выполняться каждые 12 секунд; если для запуска требуется переменное количество времени, то в течение длительного времени время его выполнения может быть непредсказуемым.

Это может быть именно то, что вы хотите; если это так, используйте одно из других решений или используйте это, но упростите sleepвызов.

Для разрешения в одну минуту задания cron будут выполняться в указанное время, независимо от того, сколько времени занимает каждая команда. (На самом деле новое задание cron будет запущено, даже если предыдущее еще выполняется.)

Вот простой Perl-скрипт, который спит до следующего интервала, поэтому, например, с интервалом в 10 секунд команда может выполняться в 12:34:00, 12:34:10, 12:34:20 и т. Д., Даже если Сама команда занимает несколько секунд. Если команда выполняется более intervalсекунд, следующий интервал будет пропущен (в отличие от cron). Интервал вычисляется относительно эпохи, поэтому интервал 86400 секунд (1 день) будет выполняться в полночь по UTC.

#!/usr/bin/perl

use strict;
use warnings;

if (scalar @ARGV < 2) {
    die "Usage: $0 seconds command [args...]\n";
}

$| = 1;  # Ensure output appears

my($interval, @command) = @ARGV;

# print ">>> interval=$interval command=(@command)\n";

while (1) {
    print "sleep ", $interval - time % $interval, "\n";
    sleep $interval - time % $interval;
    system @command; # TODO: Handle errors (how?)
}
Кит Томпсон
источник
Смотрите также этот другой вопрос
Стефан Chazelas
1
в bash проще, чтобы минимизировать дрейф (хотя он может дрейфовать в течение очень длинных окон использования из-за команды sleep и самого bash, накапливая свои крошечные времена выполнения): while :; do sleep 1m & command; wait; done
математика,
1
@math: умный Это не работает, если у вас есть другие фоновые процессы, работающие в текущей оболочке ( waitбудут ждать их всех). Это поправимо путем изменения waitв wait $!, где $!это PID от sleepпроцесса. Опубликуйте это как ответ, и я проголосую за это. Но это действительно производит некоторый посторонний вывод в интерактивной оболочке.
Кит Томпсон
29

Я думаю, что все ответы здесь либо слишком запутаны, либо вместо этого отвечают на другой вопрос:

  • Msgstr "Как запустить программу несколько раз, чтобы между завершением программы и следующим запуском была задержка X секунд" .

Настоящий вопрос был:

  • «Как запускать программу каждые X секунд»

Это две совершенно разные вещи, когда команде требуется время, чтобы закончить.

Возьмем, к примеру, сценарий foo.sh(представьте, что это программа, выполнение которой занимает несколько секунд).

#!/bin/bash
# foo.sh
echo `date +"%H:%M:%S"` >> output.txt;
sleep 2.5;
# ---

Вы хотите запускать это каждую секунду, и большинство предложит watch -n1 ./foo.sh, или while sleep 1; do ./foo.sh; done. Тем не менее, это дает вывод:

15:21:20
15:21:23
15:21:27
15:21:30
15:21:34

Который точно не запускается каждую секунду. Даже с -pфлажком, как man watchпредлагает страница, может решить эту проблему, результат тот же.

Простой способ выполнить желаемую задачу, к которой некоторые прикасаются, - запустить команду в фоновом режиме. Другими словами:

while sleep 1; do (./foo.sh &) ; done

И это все, что нужно сделать.

Вы можете запускать его каждые 500 мс sleep 0.5, или что у вас есть.

swalog
источник
1
Те, кто голосовал по разным ответам, не понимают изящества вашего ответа ...
Серж Штробандт
Это хорошо, если ваша программа не имеет побочных эффектов. Если программа занимает больше времени, чем интервал, побочные эффекты могут помешать (например, перезаписать файл). Если программа ожидает чего-то другого, а это что-то еще занимает вечно, то вы начинаете все больше и больше фоновых заданий на неопределенный срок. Используйте с осторожностью.
Джон Мёллер
4
может инвертировать порядок фоновой обработки, чтобы гарантировать, что одновременно запускается не более одного ./foo.sh, но не быстрее, чем 1 в секунду: while :; do sleep 1 & ./foo.sh; wait; done
математика,
Да, это будет дрейфовать, несколько миллисекунд на каждом цикле, но это будет. Сделайте date +"%H:%M:%S.%N"в foo.sh, чтобы увидеть, как доля будет медленно увеличиваться в каждом цикле.
Исаак
Да, это правда, что большинство ответов буквально не касаются вопроса. Но это почти безопасная ставка, что они отвечают на то, что хотел ОП. Было бы безопаснее сделать ставку, если ОП действительно принял ответ ... Но это хорошо, если вы знаете о возможных побочных эффектах и уверены, что среднее время выполнения команды не превысит время цикла.
Auspex
17

В bash:

bash -c 'while [ 0 ]; do echo "I will not automate this punishment in absurdum."; done'

( echoможно заменить любой командой ...

Или в perl:

perl -e 'for (;1;) {print "I will not automate this punishment in absurdum.\n"}'

Где print "I will not automate this punishment in absurdum.\n"можно заменить на «любую» команду, заключенную в обратные кавычки (`).

И для паузы добавьте sleepоператор внутри цикла for:

bash -c 'while [ 0 ]; do echo "I will not automate this punishment in absurdum."; sleep 1; done'

а также

perl -e 'for (;1;) {print "I will not automate this punishment in absurdum.\n"; sleep 1}'
Kevin
источник
16
while [ 0 ]плохой способ написать это. Это означает 0, что строка с длиной> 0. while [ 1 ]или while [ jeff ]будет делать то же самое. Лучше написать while true.
Микель
5
@Mikel: Илиwhile :
Кит Томпсон
10

Недавний bash> = 4.2 под последним ядром Linux, основанный ответ.

Для того, чтобы ограничить время выполнения, нет вилок ! Используются только встроенные.

Для этого я использую readвстроенную функцию вместо sleep. К сожалению, это не будет работать с нотными сессиями.

Функция быстрого " repeat" по запросу:

repeat () {
   local repeat_times=$1 repeat_delay=$2 repeat_foo repeat_sleep
   read -t .0001 repeat_foo
   if [ $? = 1 ] ;then
       repeat_sleep() { sleep $1 ;}
   else
       repeat_sleep() { read -t $1 repeat_foo; }
   fi
   shift 2
   while ((repeat_times)); do
        ((repeat_times=repeat_times>0?repeat_times-1:repeat_times))
        "${@}"
        ((repeat_times))&& ((10#${repeat_delay//.})) &&
            repeat_sleep $repeat_delay
   done
}

Небольшой тест с цитируемыми строками:

repeat 3 0 printf "Now: %(%T)T, Hello %s.\n" -1 Guy
Now: 15:13:43, Hello Guy.
Now: 15:13:43, Hello Guy.
Now: 15:13:43, Hello Guy.

repeat -1 .5 printf "Now: %(%T)T, Hello %s.\n" -1 Guy
Now: 15:14:14, Hello Guy.
Now: 15:14:14, Hello Guy.
Now: 15:14:15, Hello Guy.
Now: 15:14:15, Hello Guy.
Now: 15:14:16, Hello Guy.
Now: 15:14:16, Hello Guy.
Now: 15:14:17, Hello Guy.
Now: 15:14:17, Hello Guy.
Now: 15:14:18, Hello Guy.
Now: 15:14:18, Hello Guy.
^C

В зависимости от степени детализации и продолжительности поданной команды ...

Под последними ядрами Linux существует профайл, /proc/timer_listсодержащий информацию о времени в наносекундах.

Если вы хотите выполнить команду ровно один раз в секунду, ваша команда должна завершиться менее чем за секунду! И оттуда вам остаётся sleepтолько остаток текущей секунды.

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

command=(echo 'Hello world.')
delay=10
while :;do
    printf -v now "%(%s)T" -1
    read -t $(( delay-(now%delay) )) foo
    ${command[@]}
  done.

Но если ваша цель - получить более тонкую гранулярность, вам необходимо:

Используйте информацию наносекунд, чтобы подождать до начала секунды ...

Для этого я написал небольшую функцию bash:

# bash source file for nano wait-until-next-second

mapfile  </proc/timer_list _timer_list
for ((_i=0;_i<${#_timer_list[@]};_i++));do
    ((_c+=${#_timer_list[_i]}))
    [[ ${_timer_list[_i]} =~ ^now ]] && TIMER_LIST_READ=$_c
    [[ ${_timer_list[_i]} =~ offset:.*[1-9] ]] && \
        TIMER_LIST_OFFSET=${_timer_list[_i]//[a-z.: ]} && \
        break
done
unset _i _timer_list _c
readonly TIMER_LIST_OFFSET TIMER_LIST_READ
waitNextSecondHires() {
    local nsnow nsslp
    read -N$TIMER_LIST_READ nsnow </proc/timer_list
    nsnow=${nsnow%% nsecs*}
    nsnow=$((${nsnow##* }+TIMER_LIST_OFFSET))
    nsslp=$((2000000000-10#${nsnow:${#nsnow}-9}))
    read -t .${nsslp:1} foo
}

Получив их, вы можете:

command=(echo 'Hello world.')
while :;do
    waitNextSecondHires
    ${command[@]}
  done.

запустить ${command[@]}непосредственно в командной строке, чем сравнить с

command=(eval "echo 'Hello world.';sleep .3")
while :;do
    waitNextSecondHires
    ${command[@]}
  done.

это должно дать точно такой же результат.

Нанимает функцию " repeat" по запросу:

Вы можете получить это:

mapfile  </proc/timer_list _timer_list
for ((_i=0;_i<${#_timer_list[@]};_i++));do
    ((_c+=${#_timer_list[_i]}))
    [[ ${_timer_list[_i]} =~ ^now ]] && TIMER_LIST_READ=$_c
    [[ ${_timer_list[_i]} =~ offset:.*[1-9] ]] && \
        TIMER_LIST_OFFSET=${_timer_list[_i]//[a-z.: ]} && \
        break
done
unset _i _timer_list _c
readonly TIMER_LIST_OFFSET TIMER_LIST_READ

repeat_hires () {
    local repeat_times=$1 repeat_delay=$2 repeat_foo repeat_sleep repeat_count
    read -t .0001 repeat_foo
    if [ $? = 1 ] ;then
        repeat_sleep() { sleep $1 ;}
    else
        repeat_sleep() { read -t $1 repeat_foo; }
    fi
    shift 2
    printf -v repeat_delay "%.9f" $repeat_delay
    repeat_delay=${repeat_delay//.}
    read -N$TIMER_LIST_READ nsnow </proc/timer_list
    nsnow=${nsnow%% nsec*}
    started=${nsnow##* }
    while ((repeat_times)); do
        ((repeat_times=repeat_times>0?repeat_times-1:repeat_times))
        "${@}"
        ((repeat_times)) && ((10#$repeat_delay)) && {
            read -N$TIMER_LIST_READ nsnow </proc/timer_list
            nsnow=${nsnow%% nsec*}
            nsnow=${nsnow##* }
            (( (nsnow - started) / 10#$repeat_delay - repeat_count++ )) &&
                printf >&2 "WARNING: Command '%s' too long for %f delay.\n" \
                           "${*}" ${repeat_delay:0:${#repeat_delay}-9
                           }.${repeat_delay:${#repeat_delay}-9}
            printf -v sleep "%010d" $((
                10#$repeat_delay - ( ( nsnow - started ) % 10#$repeat_delay ) ))
            repeat_sleep ${sleep:0:${#sleep}-9}.${sleep:${#sleep}-9}
        }
    done
}

Тогда попробуйте это:

time repeat_hires 21 .05 sh -c 'date +%s.%N;sleep .01'
1480867565.152022457
1480867565.201249108
1480867565.251333284
1480867565.301224905
1480867565.351236725
1480867565.400930482
1480867565.451207075
1480867565.501212329
1480867565.550927738
1480867565.601199721
1480867565.651500618
1480867565.700889792
1480867565.750963074
1480867565.800987954
1480867565.853671458
1480867565.901232296
1480867565.951171898
1480867566.000917199
1480867566.050942638
1480867566.101171249
1480867566.150913407

real    0m1.013s
user    0m0.000s
sys     0m0.016s

time repeat_hires 3 .05 sh -c 'date +%s.%N;sleep .05'
1480867635.380561067
WARNING: Command 'sh -c date +%s.%N;sleep .05' too long for 0.050000 delay.
1480867635.486503367
WARNING: Command 'sh -c date +%s.%N;sleep .05' too long for 0.050000 delay.
1480867635.582332617

real    0m0.257s
user    0m0.000s
sys     0m0.004s
Ф. Хаури
источник
read -tэто встроенный , пока sleepнет. Это может иметь граничный эффект (т. Е. Нажатие клавиши [key: return] прерывает сон ), но это можно исправить путем тестирования$foo
F. Hauri
Очень впечатляющий ответ
gena2x
4

Хочешь попробовать это (Баш)?

forever ()   {
    TIMES=shift;
    SLEEP=shift;
    if [ "$TIMES" = "-1" ]; then  
        while true;
        do 
            $@
            sleep $SLEEP
        done
    else
        repeat "$TIMES" $@ 
    fi; }
manatwork
источник
Я не в восторге от оболочки, поэтому улучшения приветствуются. И у меня, похоже, нет команды «повторить» в моем дистрибутиве.
2
repeatспецифично для csh и tcsh.
Кит Томпсон
2
@KeithThompson: и Zsh
Thor
4

Perl

#!/usr/bin/env perl
# First argument is number of seconds to sleep between repeats, remaining
# arguments give the command to repeat forever.

$sleep = shift;
$cmd = join(' ', @ARGV);

while(1) {
  system($cmd);
  sleep($sleep); 
}
dreeves
источник
4

Bash и некоторые из его родственных оболочек имеют удобное (( ... ))обозначение, в котором могут быть вычислены арифметические выражения.

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

repeat=10
delay=1

i=0
while (( i++ < repeat )); do
  echo Repetition $i
  sleep $delay
done

Этот ответ также страдает от смещения времени, описанного в ответе Кейта .

Тор
источник
Это был лучший.
Ахмад Авайс
3

Как упомянуто gbrandt, если watchкоманда доступна, обязательно используйте ее. Однако в некоторых системах Unix она не установлена ​​по умолчанию (по крайней мере, там, где я работаю).

Вот еще одно решение с немного отличающимся синтаксисом и выводом (работает в BASH и SH):

while [ 1 ] ; do
    <cmd>
    sleep <x>
    echo ">>>>>>>>>>>>>" `date` ">>>>>>>>>>>>>>"
done

Изменить: я удалил некоторые "." в последнем эхо-заявлении ... пережиток моих дней Perl;)

Kevin
источник
3
while [ 1 ]работает только потому, что 1 рассматривается как строка, как и while [ -n 1 ]. while [ 0 ]или while [ jeff ]сделал бы то же самое. while trueимеет гораздо больше смысла.
Микель
3

Если вы не хотите отображать сообщение на экране и можете позволить себе повторить работу в течение нескольких минут , возможно, crontab будет вашим лучшим инструментом. Например, если вы хотите выполнять свою команду каждую минуту, вы должны написать что-то вроде этого в вашем crontabфайле:

* * * * * my_precious_command

Пожалуйста, ознакомьтесь с руководством для дальнейшего примера. Кроме того, вы можете легко установить время с помощью Crontab Code Generator .

Барун
источник
3

Что если бы у нас были оба?

Вот идея: только с «интервалом», это повторяется навсегда. С «интервалом» и «разом» он повторяет это количество раз, разделенное «интервалом».

Использование :

$ loop [interval [times]] command

Итак, алгоритм будет:

  • Элемент списка
  • если $ 1 содержит только цифры, это интервал (по умолчанию 2)
  • если $ 2 содержит только цифры, это количество раз (по умолчанию бесконечно)
  • цикл while с этими параметрами
    • сон "интервал"
    • если задано несколько раз, уменьшайте переменную, пока она не будет достигнута

Следовательно :

loop() {
    local i=2 t=1 cond

    [ -z ${1//[0-9]/} ] && i=$1 && shift
    [ -z ${1//[0-9]/} ] && t=$1 && shift && cond=1
    while [ $t -gt 0 ]; do 
        sleep $i
        [ $cond ] && : $[--t]
        $@
    done
}
Baronsed
источник
Примечание: он не работает с поплавками, хотя sleepпринимает их.
Баронизирован
2

Я закончил, создавая вариант ответа swalog. С его вы должны были ждать X секунд для первой итерации, я также запускаю мой на переднем плане, так что ..

./foo.sh;while sleep 1; do (./foo.sh) ; done
Дуэйн Лорти
источник
1

Быстро, грязно и, вероятно, опасно для загрузки, но если вы любите приключения и знаете, что делаете, вставьте это в repeat.shи chmod 755это,

while true
do 
    eval $1 
    sleep $2 
done

Вызвать его с ./repeat.sh <command> <interval>

Мое чувство пауков говорит, что это, вероятно, злой способ сделать это, верно ли мое чувство пауков?

mallyone
источник
8
Eval !? Зачем? sleep $1; shift; "$@"или подобное было бы намного лучше.
Микель
Не знаю, почему это -2. Eval может быть излишним ... кроме случаев, когда вам это нужно. Eval позволяет вам получать такие вещи, как перенаправление в командную строку.
Йохан
1

Вы можете запустить скрипт из init (добавив строку в / etc / inittab). Этот скрипт должен выполнить вашу команду, подождать, пока вы не захотите снова запустить скрипт, и выйти из него. Init снова запустит ваш скрипт после выхода.

Роберто Пас
источник
1

Простой способ повторить задание из crontab с интервалом менее одной минуты (пример 20 секунд):

crontab: * * * * * script.sh

script.sh:

#!/bin/bash
>>type your commands here.

sleep 20
>>retype your commands here.

sleep 20
>>retype your commands here.
Чарльз Нахель
источник
1

Вы можете получить мощность от интерпретатора Python из оболочки.

python3 -c "import time, os
while True:
    os.system('your command')
    time.sleep(5)

замените пять в любое время (сек), которое вам нравится.

Внимание: для возврата используйте SHIFT + ENTER для возврата ввода в оболочке. И не забудьте ввести 4 пробела отступа в каждой строке цикла.

pah8J
источник
Обратите внимание , что python«s os.system()запускает оболочку интерпретировать командную строку, поэтому применение pythonдля запуска снарядов в цикле запустить команду периодически немного избыточна. Вы могли бы также сделать все в оболочке.
Стефан Шазелас
Я согласен. Тем не менее, есть 5 секундный сон, как вы описали в каждом цикле. Таким образом, дополнительные затраты на запуск новой оболочки могут быть проигнорированы. Если эта стоимость сделает период длиннее, чем ожидалось, вы можете сократить время сна по мере необходимости.
pah8J
0

Это решение работает в MacOSX 10.7. Это работает чудесно.

bash -c 'while [ 0 ]; do \
      echo "I will not automate this punishment in absurdum."; done'

В моем случае

bash -c 'while [ 0 ]; do ls; done'

или же

bash -c 'while [ 0 ]; do mv "Desktop/* Documents/Cleanup"; done'

убирать мой рабочий стол постоянно.

Дэн Руис
источник
1
Вы хотели иметь там sleepкоманду?
Кит Томпсон
4
while [ 0 ]странный способ написать бесконечный цикл; это работает, потому что testкоманда (также известная как [) обрабатывает непустую строку как истину. while : ; do ... ; doneявляется более идиоматическим, или, если вы предпочитаете, вы можете использовать while true ; do ... ; done.
Кит Томпсон
0

Вы можете получить эту рекурсивную функцию:

#!/bin/bash
ininterval () {
    delay=$1
    shift
    $*
    sleep $delay
    ininterval $delay $*
}

или добавьте:

ininterval $*

и вызвать скрипт.

неизвестный пользователь
источник
0

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

while true; do (run command here); done

Это также работает для нескольких команд, например, для отображения постоянно обновляемых часов в окне консоли:

while true; do clear; date; sleep 1; done

Томас Братт
источник
Почему отрицательный голос? Вы можете увидеть, что это рабочее решение, скопировав и вставив пример в окно терминала.
Томас Братт
Я думаю, что вы проголосовали, потому что это немного опасно и потребляет процессор.
ojblass
0
#! /bin/sh

# Run all programs in a directory in parallel
# Usage: run-parallel directory delay
# Copyright 2013 by Marc Perkel
# docs at http://wiki.junkemailfilter.com/index.php/How_to_run_a_Linux_script_every_few_seconds_under_cron"
# Free to use with attribution

if [ $# -eq 0 ]
then
   echo
   echo "run-parallel by Marc Perkel"
   echo
   echo "This program is used to run all programs in a directory in parallel" 
   echo "or to rerun them every X seconds for one minute."
   echo "Think of this program as cron with seconds resolution."
   echo
   echo "Usage: run-parallel [directory] [delay]"
   echo
   echo "Examples:"
   echo "   run-parallel /etc/cron.20sec 20"
   echo "   run-parallel 20"
   echo "   # Runs all executable files in /etc/cron.20sec every 20 seconds or 3 times a minute."
   echo 
   echo "If delay parameter is missing it runs everything once and exits."
   echo "If only delay is passed then the directory /etc/cron.[delay]sec is assumed."
   echo
   echo 'if "cronsec" is passed then it runs all of these delays 2 3 4 5 6 10 12 15 20 30'
   echo "resulting in 30 20 15 12 10 6 5 4 3 2 executions per minute." 
   echo
   exit
fi

# If "cronsec" is passed as a parameter then run all the delays in parallel

if [ $1 = cronsec ]
then
   $0 2 &
   $0 3 &
   $0 4 &
   $0 5 &
   $0 6 &
   $0 10 &
   $0 12 &
   $0 15 &
   $0 20 &
   $0 30 &
   exit
fi

# Set the directory to first prameter and delay to second parameter

dir=$1
delay=$2

# If only parameter is 2,3,4,5,6,10,12,15,20,30 then automatically calculate 
# the standard directory name /etc/cron.[delay]sec

if [[ "$1" =~ ^(2|3|4|5|6|10|12|15|20|30)$ ]]
then
   dir="/etc/cron.$1sec"
   delay=$1
fi

# Exit if directory doesn't exist or has no files

if [ ! "$(ls -A $dir/)" ]
then
   exit
fi

# Sleep if both $delay and $counter are set

if [ ! -z $delay ] && [ ! -z $counter ]
then
   sleep $delay
fi

# Set counter to 0 if not set

if [ -z $counter ]
then
   counter=0
fi

# Run all the programs in the directory in parallel
# Use of timeout ensures that the processes are killed if they run too long

for program in $dir/* ; do
   if [ -x $program ] 
   then
      if [ "0$delay" -gt 1 ] 
      then
         timeout $delay $program &> /dev/null &
      else
         $program &> /dev/null &
      fi
   fi
done

# If delay not set then we're done

if [ -z $delay ]
then
   exit
fi

# Add delay to counter

counter=$(( $counter + $delay ))

# If minute is not up - call self recursively

if [ $counter -lt 60 ]
then
   . $0 $dir $delay &
fi

# Otherwise we're done
user56318
источник
0

С zshи sleepреализация, которая принимает аргументы с плавающей точкой:

typeset -F SECONDS=0 n=0
repeat 100 {cmd; sleep $(((n+=3) - SECONDS))}

Или для forever:

for ((;;)) {cmd; sleep $(((n+=3) - SECONDS))}

Если ваш sleepне поддерживает поплавки, вы всегда можете переопределить его как обертка вокруг zsh«ы zselectвстроено:

zmodload zsh/zselect
sleep() zselect -t $((($1 * 100) | 0))
Стефан Шазелас
источник
-1

... Интересно, сколько сложных решений можно создать при решении этой проблемы.

Это может быть так просто ...

open /etc/crontab 

поместите туда 1 следующую строку в конец файла, например:

*/NumberOfSeconds * * * * user /path/to/file.sh

Если вы хотите что-то запускать каждую секунду, просто добавьте туда:

*/60 * * * * root /path/to/file.sh 

where that file.sh could be chmod 750 /path/to/file.sh

и внутри этого файла .sh должно быть:

#!/bin/bash 
#What does it do
#What is it runned by

your code or commands

и это все!

НАСЛАЖДАТЬСЯ!

Мирра
источник
6
Это не правильно. * / 60 будет запускаться каждые 60 минут, а * / 5, например, каждые 5 минут.
Мика
1
секунд не разрешено на crontab
GAD3R
проверить свои вещи, прежде чем ответить с неправильным дерьмом.
sjas
-1
until ! sleep 60; do echo $(date); command; command; command; done

работает для меня.

  1. Мне это не нужно ровно каждые 60 секунд
  2. «watch» не смотрит команды на всех системах
  3. «Наблюдать» может взять только одну команду (или файл скрипта)
  4. перевод сна в состояние вместо тела делает цикл лучше прерывистым (так что утверждение в ответе на похожий вопрос по stackoverflow. к сожалению, я могу найти его в данный момент)
  5. Я хочу выполнить его один раз до задержки, поэтому я использую до
Titus
источник
Я только что заметил, что «пока», по-видимому, тоже выполняет сон перед первой итерацией. Есть ли способ положить условие в конце цикла в Bash?
Тит