Repita um comando Unix a cada x segundos para sempre


480

Há um comando interno do Unix, repeatcujo primeiro argumento é o número de vezes para repetir um comando, em que o comando (com qualquer argumento) é especificado pelos argumentos restantes para repeat.

Por exemplo,

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

ecoará a sequência especificada 100 vezes e depois parará.

Eu gostaria de um comando semelhante - vamos chamá-lo forever- que funciona da mesma forma, exceto que o primeiro argumento é o número de segundos para pausar entre repetições e repete para sempre. Por exemplo,

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

Pensei em perguntar se existe algo assim antes de escrevê-lo. Eu sei que é como um script Perl ou Python de 2 linhas, mas talvez haja uma maneira mais padrão de fazer isso. Caso contrário, fique à vontade para publicar uma solução em sua linguagem de script favorita, estilo Rosetta Stone .

PS: Talvez uma maneira melhor de fazer isso seja generalizar repeatpara levar o número de vezes a repetir (com -1 significa infinito) e o número de segundos para dormir entre as repetições. Os exemplos acima se tornariam:

% repeat 100 0 echo "I will not automate this punishment."
% repeat -1 5 echo "This will get echoed every 5 seconds forever."

1
Por que não: repita o comando [-t time] [-n number] [args ...]?
23611 Jonathan Leffler

17
Legal. Infelizmente tanto no meu Ubuntu e meu AIX repetição é comando não encontrado . Você pode fazer um type repeate me avise de onde vem?

3
Também estou no Ubuntu e não tenho repetição instalada. Qualquer informação sobre como instalar isso seria útil.
Rory

7
repeaté um comando interno no csh e no tcsh.
9788 Keith Thompson

2
@KeithThompson, csh, tcsh e zsh . Embora seja mais uma palavra-chave do que um
built-in

Respostas:


615

Experimente o watchcomando

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

De modo a:

watch -n1  command

executará o comando a cada segundo (bem, tecnicamente, a cada segundo mais o tempo necessário para commandexecutar como watch(pelo menos as implementações procpse busybox) apenas dorme um segundo entre duas execuções de command), para sempre.

Deseja passar o comando para, em execvez de sh -c, use a -xopção:

watch -n1 -x command

No macOS, você pode obter watchdas portas Mac :

port install watch

Ou você pode obtê-lo no Homebrew :

brew install watch

2
você pode usar o MacPorts para instalá-lo. sudo port install watch

10
Também disponível em homebrew (relógio de instalação de fermentação).
Lyle

28
+1 Para uma nova ferramenta poderosa. Eu costumava while true; do X; sleep Y; done- isso é muito melhor.
Adam Matan

4
O problema com esta ferramenta (pelo menos no Ubuntu) é que ela força a tela cheia; portanto, se eu estiver descartando algumas informações periódicas, perco as informações anteriores.
Scorpiodawg

4
É chamado cmdwatchno FreeBSD (a partir das portas :) sysutils/cmdwatch.
Ouki 25/03

243

Bater

while+ sleep:

while true
do 
    echo "Hi"
    sleep 1
done

Aqui está a mesma coisa que uma linha de taquigrafia (dos comentários abaixo):

while sleep 1; do echo "Hi"; done

Usa ;para separar comandos e usos sleep 1para o whileteste, pois sempre retorna true. Você pode colocar mais comandos no loop - basta separá-los com;


2
Eu acredito que funciona como escrito em um shell Bourne genérico também.
#

5
@dreeves, usando ";" como separador, os comandos podem ser executados em uma única linha. Veja a resposta de Toony como um exemplo
bbaja42 12/12/12

56
sleep 1 sempre retorna true para que você possa usá-lo como a condição while. Não é a coisa mais legível do mundo, mas é absolutamente legítima: enquanto dorme 1; faça eco "Oi"; feito
David Costa

3
@ David Costa: Você fez um argumento razoável, mas sleepnem sempre retorna verdadeiro. Ī̲ usei esses loops (embora com um atraso maior), ou seja, porque existe uma maneira de finalizá-lo normalmente com a interrupção do processo de suspensão.
Incnis MRSI

2
@IncnisMrsi Eu não pensei nisso, na verdade, ele retorna zero apenas quando o tempo passou e diferente de zero quando é finalizado por um sinal. Obrigado por apontar
David Costa

71

Esta é apenas uma versão mais curta de outras while+sleeprespostas, se você estiver executando esse tipo de tarefa frequentemente como sua rotina diária, usar isso poupará o pressionamento desnecessário de teclas e se sua linha de comando começar a entender mais, será mais fácil. Mas este começa com o sono primeiro.

Isso geralmente é útil se você precisar seguir algo com saída em uma linha, como carga da máquina:

while sleep 1; do uptime; done

Sim. Isso deve ser documentado ao lado de UUOC.
Johan

10
E se você quer que ele funcione como "assistir" você pode fazerwhile clear; do date; command;sleep 5; done
Johan

Uau, obrigado pela while sleepcombinação, economiza keytokes!
George Y.

45

Um problema que todas as respostas postadas até agora têm é que o tempo em que o comando é executado pode ser desviado. Por exemplo, se você sleep 10executar um comando entre, e o comando demorar 2 segundos para ser executado, será executado a cada 12 segundos; se leva um tempo variável para ser executado, a longo prazo, o tempo em que é executado pode ser imprevisível.

Isso pode ser exatamente o que você deseja; Nesse caso, use uma das outras soluções ou use essa, mas simplifique a sleepchamada.

Para resolução de um minuto, os trabalhos cron serão executados no horário especificado, independentemente do tempo que cada comando leve. (De fato, um novo trabalho cron será iniciado, mesmo que o anterior ainda esteja em execução.)

Aqui está um script Perl simples que dorme até o próximo intervalo. Por exemplo, com um intervalo de 10 segundos, o comando pode ser executado às 12:34:00, 12:34:10, 12:34:20, etc., mesmo se o o próprio comando leva vários segundos. Se o comando for executado por mais de intervalsegundos, o próximo intervalo será ignorado (diferente do cron). O intervalo é calculado em relação à época, portanto, um intervalo de 86400 segundos (1 dia) será executado à meia-noite 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?)
}


1
mais simples em bash para minimizar a deriva (embora ele pode variar ao longo janelas de uso muito longas devido ao comando de sono e bater-se acumulando as suas pequenas tempos de execução): while :; do sleep 1m & command; wait; done
matemática

1
@math: inteligente. Não funciona se você tiver outros processos em segundo plano em execução no shell atual ( waitesperará por todos eles). Isso é corrigível mudando waitpara wait $!, onde $!está o PID do sleepprocesso. Poste isso como uma resposta e eu vou votar novamente. Mas produz alguma saída estranha em um shell interativo.
21417 Keith Thompson

29

Acho que todas as respostas aqui até agora são muito complicadas ou, em vez disso, respondem a uma pergunta diferente:

  • "Como executar um programa repetidamente para que haja um atraso de X segundos entre o término do programa e o próximo início" .

A verdadeira questão era:

  • "Como executar um programa a cada X segundos"

Essas são duas coisas muito diferentes quando o comando leva algum tempo para terminar.

Tomemos, por exemplo, o script foo.sh(finja que este é um programa que leva alguns segundos para ser concluído).

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

Você deseja executar isso a cada segundo, e a maioria sugere watch -n1 ./foo.sh, ou while sleep 1; do ./foo.sh; done. No entanto, isso fornece a saída:

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

O que não é exatamente executado a cada segundo. Mesmo com a -pbandeira, como a man watchpágina sugere, isso pode resolver o resultado.

Uma maneira fácil de realizar a tarefa desejada, na qual alguns tocam, é executar o comando em segundo plano. Em outras palavras:

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

E isso é tudo o que existe.

Você pode executá-lo a cada 500 ms com sleep 0.5o que você tem.


1
Aqueles que votaram em diferentes respostas, não entendo a elegância de sua resposta ...
Serge Stroobandt

Isso é bom, a menos que seu programa tenha efeitos colaterais. Se o programa demorar mais que o intervalo, os efeitos colaterais podem interferir (como sobrescrever um arquivo). Se o programa esperar por outra coisa, e essa outra coisa demorar para sempre, você estará iniciando cada vez mais trabalhos em segundo plano indefinidamente. Use com cuidado.
John Moeller

4
poderia inverter a ordem do backgrounding para garantir que não mais do que um ./foo.sh é executado em um momento, mas não mais do que 1 por segundo: while :; do sleep 1 & ./foo.sh; wait; done
matemática

Sim, ele será desviado, alguns milissegundos em cada loop, mas será. Faça date +"%H:%M:%S.%N"em foo.sh para ver como a fração aumentará lentamente em cada loop.
Isaac

Sim, é verdade que a maioria das respostas não aborda literalmente a questão. Mas é quase seguro que eles respondam o que o OP queria. Seria uma aposta mais segura se o OP tivesse realmente aceitado uma resposta ... Mas isso é bom, desde que você esteja ciente dos possíveis efeitos colaterais e tenha certeza de que o tempo médio de execução do comando não excederá o tempo do ciclo
Auspex

17

Em bash:

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

( echopode ser substituído por qualquer comando ...

Ou em perl:

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

Onde print "I will not automate this punishment in absurdum.\n"poderia ser substituído por "qualquer" comando cercado por reticulares (`).

E, para uma pausa, adicione uma sleepinstrução dentro do loop for:

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

e

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

16
while [ 0 ]é uma maneira ruim de escrever. Isso significa que 0é uma string com comprimento> 0 while [ 1 ]ou while [ jeff ]faria a mesma coisa. Melhor escrever while true.
11117 Mikel

5
@Mikel: Orwhile :
Keith Thompson

10

Recent bash> = 4.2 no kernel recente do Linux, com base em respostas.

Para limitar o tempo de execução, não há garfos ! Somente interno é usado.

Para isso, eu uso a readfunção embutida em vez de sleep. Unfortunely isso não vai funcionar com Notty sessões.

Função rápida " repeat" conforme solicitado:

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
}

Pouco teste com seqüências de caracteres citadas:

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

Dependendo da granularidade e duração do comando enviado ...

Nos kernels recentes do Linux, há um arquivo /proc/timer_listcontendo informações de tempo em nanossegundos.

Se você deseja executar um comando exatamente uma vez por segundo, seu comando deve terminar em menos de um segundo! E a partir daí, você precisa sleepapenas o restante do segundo atual.

Se o atraso for mais importante e seu comando não exigir um tempo significativo, você poderá:

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

Mas se seu objetivo é obter granularidade mais fina, você deve:

Use as informações em nanossegundos para esperar até o início de um segundo ...

Para isso, escrevi uma pequena função 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
}

Depois de adquiri-los, você pode:

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

executar ${command[@]}diretamente na linha de comando, do que comparar com

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

isso deve dar exatamente o mesmo resultado.

Contrata a função " repeat" conforme solicitado:

Você pode obter isso:

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
}

Então tente:

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é um built-in , enquanto sleepnão é. Isto pode ter efeito fronteira (ou seja, atingir [chave: retorno] interromper calmamente o sono ), mas esta poderia ser tratada por meio de testes$foo
F. Hauri

Resposta muito impressionante
gena2x:

4

Gostaria de experimentar isso (Bash)?

forever ()   {
    TIMES=shift;
    SLEEP=shift;
    if [ "$TIMES" = "-1" ]; then  
        while true;
        do 
            $@
            sleep $SLEEP
        done
    else
        repeat "$TIMES" $@ 
    fi; }

Eu não sou muito gostosa, então as melhorias são bem-vindas. E eu não pareço ter um comando "repetir" na minha distribuição.

2
repeaté específico para csh e tcsh.
9788 Keith Thompson

2
@KeithThompson: e 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); 
}

4

O Bash e algumas de suas conchas semelhantes têm a (( ... ))notação conveniente em que expressões aritméticas podem ser avaliadas.

Portanto, como resposta ao seu terceiro desafio, em que a contagem de repetições e o atraso entre cada repetição devem ser configuráveis, aqui está uma maneira de fazer isso:

repeat=10
delay=1

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

Essa resposta também sofre com o desvio de tempo coberto na resposta de Keith .


Este foi o melhor.
Ahmad Awais 17/07/19

3

Conforme mencionado por gbrandt, se o watchcomando estiver disponível, use-o definitivamente. Alguns sistemas Unix, no entanto, não o instalam por padrão (pelo menos não onde eu trabalho).

Aqui está outra solução com sintaxe e saída ligeiramente diferentes (funciona em BASH e SH):

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

Editar: removi alguns "." na última declaração de eco ... ressaca dos meus dias Perl;)


3
while [ 1 ]só funciona porque 1 é tratado como uma string, assim como while [ -n 1 ]. while [ 0 ]ou while [ jeff ]faria a mesma coisa. while truefaz muito mais sentido.
11117 Mikel

3

Se sua intenção não é exibir uma mensagem na tela e se você puder repetir o trabalho em minutos, o crontab talvez seja sua melhor ferramenta. Por exemplo, se você deseja executar seu comando a cada minuto, escreva algo como isto em seu crontabarquivo:

* * * * * my_precious_command

Confira o tutorial para mais exemplos. Além disso, você pode definir os horários facilmente usando o Crontab Code Generator .


3

E se tivéssemos os dois?

Aqui está a idéia: com apenas "intervalo", ele se repete para sempre. Com "intervalo" e "tempos", repete esse número de vezes, separado por "intervalo".

O uso :

$ loop [interval [times]] command

Então, o algoritmo será:

  • Item da lista
  • se $ 1 contiver apenas dígitos, será o intervalo (padrão 2)
  • se $ 2 contiver apenas dígitos, é o número de vezes (infinito padrão)
  • loop while com esses parâmetros
    • intervalo de sono
    • se um número de vezes foi dado, diminua um var até que seja atingido

Portanto :

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
}

Nota: não funciona com carros alegóricos, apesar de sleepaceitá-los.
Baronado em

2

Acabei criando uma variante da resposta do swalog. Com o dele, você teve que esperar X segundos pela primeira iteração, também estou executando o meu em primeiro plano, então ..

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

1

Rápido, sujo e provavelmente perigoso para boot, mas se você é aventureiro e saber o que você está fazendo, colocar isso em repeat.she chmod 755-lo,

while true
do 
    eval $1 
    sleep $2 
done

Invoque-o com ./repeat.sh <command> <interval>

Meu senso de aranha diz que essa é provavelmente uma maneira maligna de fazer isso, meu senso de aranha está certo?


8
Eval !? Pelo que? sleep $1; shift; "$@"ou similar seria muito melhor.
22612 Mikel

Não sei por que isso é -2. eval pode ser um exagero ... exceto quando você precisar. O Eval permite obter coisas como redirecionamento na linha de comando.
Johan

1

Você pode executar um script a partir do init (adicionando uma linha ao / etc / inittab). Este script deve executar seu comando, dormir pelo tempo que você deseja esperar até executar o script novamente e sair dele. O Init iniciará seu script novamente após a saída.


1

Maneira fácil de repetir um trabalho do crontab com um intervalo inferior a um minuto (exemplo de 20 segundos):

crontab: * * * * * script.sh

script.sh:

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

sleep 20
>>retype your commands here.

sleep 20
>>retype your commands here.

1

Você pode obter o poder do interpretador python do shell.

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

substitua cinco a qualquer momento (s) que desejar.

Atenção: o retorno use SHIFT + ENTER para retornar a entrada no shell. E não esqueça de digitar 4 recuo de ESPAÇO em cada linha no loop while.


Note-se que python's os.system()executa um shell para interpretar a linha de comando, então invocando pythonpara executar conchas em um loop para executar um comando é periodicamente um pouco exagero. Você também pode fazer tudo no shell.
Stéphane Chazelas

Eu concordo com você. No entanto, há um sono de 5 segundos, conforme descrito em cada loop. Portanto, o custo extra ao iniciar um novo shell pode ser ignorado. Se esse custo tornar o período mais longo do que o esperado, você poderá reduzir o tempo de sono conforme necessário.
pah8J

0

Esta solução funciona no MacOSX 10.7. Funciona maravilhosamente.

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

No meu caso

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

ou

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

para limpar minha área de trabalho constantemente.


1
Você queria ter um sleepcomando lá?
9788 Keith Thompson

4
while [ 0 ]é uma maneira estranha de escrever um loop infinito; funciona porque o testcomando (também conhecido como [) trata uma string não vazia como verdadeira. while : ; do ... ; doneé mais idiomático ou, se você preferir, pode usar while true ; do ... ; done.
perfil completo de Keith Thompson

0

Você pode obter esta função recursiva:

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

ou adicione um:

ininterval $*

e chame o script.


0

Para executar repetidamente um comando em uma janela do console, geralmente executo algo como isto:

while true; do (run command here); done

Isso funciona para vários comandos, por exemplo, para exibir um relógio de atualização contínua em uma janela do console:

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


Por que o voto negativo? Você pode ver que essa é uma solução funcional copiando e colando o exemplo em uma janela de terminal.
Thomas Bratt

Eu acho que você foi votado porque é um pouco perigoso e consome a CPU.
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

0

Com zshe uma sleepimplementação que aceita argumentos de ponto flutuante:

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

Ou para forever:

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

Se o seu sleepnão suportar flutuadores, você sempre poderá redefini-lo como um invólucro em torno zshdo zselectinterno:

zmodload zsh/zselect
sleep() zselect -t $((($1 * 100) | 0))

-1

... Gostaria de saber quantas soluções complicadas podem ser criadas ao resolver esse problema.

Pode ser tão fácil ...

open /etc/crontab 

coloque lá 1 linha ao final do arquivo como:

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

Se você deseja executar algo a cada 1 segundo, basta colocar lá:

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

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

e dentro desse arquivo.sh deve estar:

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

your code or commands

e isso é tudo!

APROVEITAR!


6
Isso não está correto. * / 60 será executado a cada 60 minutos e * / 5, por exemplo, a cada 5 minutos.
mika

1
segundos não são permitidos no crontab
GAD3R 30/11

teste suas coisas antes de responder com merda errada.
sjas 19/08

-1
until ! sleep 60; do echo $(date); command; command; command; done

funciona para mim.

  1. Não preciso exatamente a cada 60 segundos
  2. "watch" não assiste comandos em todos os sistemas
  3. "watch" pode receber apenas um comando (ou um arquivo de script)
  4. colocar o sono na condição em vez do corpo torna o loop melhor interrompível (para que uma reivindicação em resposta a uma pergunta semelhante sobre o stackoverflow. infelizmente eu possa encontrá-lo no momento)
  5. Quero executá-lo uma vez antes do atraso, então uso até em vez de enquanto

Acabei de perceber que "até" aparentemente também executa o sono antes da primeira iteração. existe alguma maneira de colocar a condição no final do loop no bash?
Titus
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.