Выполнять команду каждые X секунд

10

Я хочу выполнять команду каждые 10 секунд, и выполняется ли она в фоновом режиме (тем самым исключая watch?). Все ответы показывают что-то вроде следующего, но это будет выполняться каждые 11-14 секунд. Как это можно сделать?

while true; do
    # perform command that takes between 1 and 4 seconds
    sleep 10
done
user1032531
источник
возможно с сигналами в реальном времени. и правильная обработка високосных секунд. удачи.
mikeserv
@mikeserv Так, не пытайтесь? Меня не волнует точность до миллисекунды, просто скажите полсекунды.
user1032531 10.10.15
Что вы пытаетесь достичь? Я считаю, что задача, которая должна выполняться с такой частотой, обычно является обходным решением для другой проблемы.
Sobrique
@ Sobrique Точно. Я делаю доказательство концепции регистратора данных опроса. На самом деле, он будет запрограммирован на каком-то другом языке, но пока это работает.
user1032531

Ответы:

16

Как насчет:

( # In a subshell, for isolation, protecting $!
  while true; do
    perform-command & # in the background
    sleep 10 ;
    ### If you want to wait for a perform-command
    ### that happens to run for more than ten seconds,
    ### uncomment the following line:
    # wait $! ;
    ### If you prefer to kill a perform-command
    ### that happens to run for more than ten seconds,
    ### uncomment the following line instead:
    # kill $! ;
    ### (If you prefer to ignore it, uncomment neither.)
  done
)

ETA: Со всеми этими комментариями, альтернативами и вспомогательной оболочкой для дополнительной защиты это выглядит намного сложнее, чем казалось. Итак, для сравнения, вот как это выглядело до того, как я начал беспокоиться о waitили killс их $!потребностью в изоляции:

while true; do perform-command & sleep 10 ; done

Остальное действительно только тогда, когда вам это нужно.

Сидхекин
источник
Можете ли вы объяснить причину, стоящую за дампом кода?
Исмаэль Мигель
2
Кроме того, чтобы указать, для какой оболочки работает ваш код (например, ответ mikeserv), чтобы люди не запутались, когда другая оболочка ведет себя по-разному :)
ash
@IsmaelMiguel Я полагал, что комментарии объяснят причину. Я не уверен, что неясно.
Сидхекин
1
В руководстве по bash говорится $!: «Расширяется до идентификатора процесса задания, которое было недавно помещено в фон», и sleepоно не помещается в фон, поэтому нет, не будет. :)
Сидхекин
1
Вау. неправильно в обоих случаях. Когда это произошло?
mikeserv
9

Вы можете сделать что - то вроде следующего в bash, zshили ksh:

SECONDS=0
while   command
do      sleep "$((10-(SECONDS%10)))"
done

Вот что bashговорится в руководстве $SECONDS:

$SECONDS

  • При каждом обращении к этому параметру возвращается количество секунд с момента вызова оболочки. Если значение присваивается $SECONDS, значением, возвращаемым при последующих ссылках, является количество секунд с момента назначения плюс назначенное значение. Если $SECONDSэто так unset, он теряет свои специальные свойства, даже если впоследствии он сбрасывается.

Вот рабочий пример:

(   SECONDS=0
    while   sleep   "$((RANDOM%10))"
    do      sleep   "$((10-(SECONDS%10)))"
            echo    "$SECONDS"
    done
)

10
20
30
40
50
60
70
80
90
100
mikeserv
источник
2
Чтобы избежать сброса SECONDSсделайте sleep "$((10-(($SECONDS-$start_time)%10)))". Вам придется сделать start_time=$SECONDSдо цикла.
Ctrl-Alt-Delor
@ Richard - зачем этого избегать?
mikeserv
потому что однажды у вас будет это в скрипте, который вы вызываете из-за такого цикла. И вы проведете весь день, задаваясь вопросом, почему он не работает должным образом. Короче не гнездится, если сбросить SECONDS. Создание суб-оболочки, как в вашем последнем редактировании, также должно избежать этой проблемы.
Ctrl-Alt-Delor
1
@ Richard - поскольку вопрос касался while trueцикла, я не предполагал, что какое-либо состояние оболочки должно было его пережить. В любом случае, использование подобного цикла в собственном контексте обычно является наилучшей практикой. В такой ситуации я бы больше беспокоился о том, чтобы делать это trapради, а не $SECONDSради.
mikeserv
Спасибо Майк, я думал сделать что-то подобное (но еще не знал, как именно). Но решение Сидекина, кажется, делает то же самое, не так ли? Я не квалифицирован, чтобы судить о лучшем ответе, но чувствовал себя обязанным выбрать что-то.
user1032531
3

Только BASH - вы также можете вычислить время, затраченное вашей командой, и вычесть из 10:

TIMEFORMAT=$'%0R'
while true; do
    T=$({ time command; } 2>&1)
    sleep $(( 10-T ))

От BASH мужчина:

TIMEFORMAT Значение этого параметра используется в качестве строки формата, определяющей, как должна отображаться информация о синхронизации для конвейеров с префиксом слова, зарезервированного во времени. Символ% вводит escape-последовательность, которая расширяется до значения времени или другой информации. Escape-последовательности и их значения следующие; фигурные скобки обозначают необязательные части.
%% буквальный%.
% [p] [l] R Истекшее время в секундах.
% [p] [l] U Количество секунд работы процессора в пользовательском режиме.
% [p] [l] S Количество секунд работы процессора в системном режиме.
% P Процент CPU, рассчитанный как (% U +% S) /% R.

Необязательный p - это цифра, указывающая точность, число дробных цифр после десятичной точки. Значение 0 не приводит к выводу десятичной точки или дроби.

Dani_l
источник