Repita un comando de Unix cada x segundos para siempre

481

Hay un comando incorporado de Unix repeatcuyo primer argumento es la cantidad de veces que se repite un comando, donde el comando (con cualquier argumento) se especifica mediante los argumentos restantes repeat.

Por ejemplo,

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

hará eco de la cadena dada 100 veces y luego se detendrá.

Me gustaría un comando similar, llamémoslo, foreverque funcione de manera similar, excepto que el primer argumento es el número de segundos para pausar entre repeticiones, y se repite para siempre. Por ejemplo,

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

Pensé en preguntar si existe tal cosa antes de escribirla. Sé que es como un script Perl o Python de 2 líneas, pero tal vez haya una forma más estándar de hacerlo. Si no es así, no dude en publicar una solución en su lenguaje de secuencias de comandos favorito, al estilo de Rosetta Stone .

PD: Quizás una mejor manera de hacer esto sería generalizar repeatpara tomar tanto el número de veces que se repite (con -1 que significa infinito) como el número de segundos para dormir entre repeticiones. Los ejemplos anteriores se convertirían en:

% repeat 100 0 echo "I will not automate this punishment."
% repeat -1 5 echo "This will get echoed every 5 seconds forever."
dreeves
fuente
1
¿Por qué no: repetir [-t time] [-n número] comando [args ...]?
Jonathan Leffler
17
Bueno. Por desgracia, tanto en mi Ubuntu y mi AIX repetición se command not found . ¿Puedes hacer una type repeaty decirme de dónde viene?
3
También estoy en Ubuntu y no tengo repetido instalado. Cualquier información sobre cómo instalar esto sería útil.
Rory
77
repeates un comando incorporado en csh y tcsh.
Keith Thompson el
2
@KeithThompson, csh, tcsh y zsh . Aunque se trata más de una palabra clave que de una construcción
Stéphane Chazelas

Respuestas:

618

Prueba el watchcomando.

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

Así que eso:

watch -n1  command

ejecutará el comando cada segundo (bueno, técnicamente, cada segundo más el tiempo que tarda commanden ejecutarse ya que watch(al menos las implementaciones procpsy busybox) solo duerme un segundo entre dos ejecuciones de command), para siempre.

¿Desea pasar el comando a en execlugar de sh -cusar la -xopción:

watch -n1 -x command

En macOS, puede obtener watchdesde puertos Mac :

port install watch

O puede obtenerlo de Homebrew :

brew install watch
Gregor Brandt
fuente
2
puedes usar MacPorts para instalarlo. sudo port install watch
10
También disponible en homebrew (brew install watch).
Lyle
28
+1 Para una nueva herramienta poderosa. Solía ​​hacerlo while true; do X; sleep Y; done. Esto es mucho mejor.
Adam Matan
44
El problema con esta herramienta (al menos en Ubuntu) es que fuerza la pantalla completa, por lo que si estoy volcando información periódica, pierdo la información anterior.
scorpiodawg
44
Se llama cmdwatchen FreeBSD (desde los puertos:) sysutils/cmdwatch.
Ouki
244

Golpetazo

while+ sleep:

while true
do 
    echo "Hi"
    sleep 1
done

Esto es lo mismo que una línea abreviada (De los comentarios a continuación):

while sleep 1; do echo "Hi"; done

Se utiliza ;para separar comandos y usos sleep 1para la whileprueba, ya que siempre devuelve verdadero. Puede poner más comandos en el bucle, solo sepárelos con;

Error de sintaxis
fuente
2
Creo que también funciona como está escrito en un shell Bourne genérico.
dmckee
55
@dreeves, usando ";" como separador, los comandos se pueden ejecutar en una sola línea. Mire la respuesta de Toony como ejemplo
bbaja42
57
sleep 1 siempre devuelve true para que pueda usarlo como la condición while. No es la cosa más legible del mundo pero es absolutamente legítima: mientras duerme 1; hacer eco "Hola"; hecho
David Costa
3
@David Costa: Usted hizo un punto razonable, pero sleepno siempre es cierto. Ī̲ usé dichos bucles (aunque con un retraso mayor), principalmente porque hay una manera de terminarlo con gracia matando el proceso de sueño.
Incnis Mrsi
2
@IncnisMrsi No lo pensé, en realidad devuelve cero solo cuando ha pasado el tiempo y no es cero cuando termina una señal. Gracias por señalarlo
David Costa
72

Esta es solo una versión más corta de otras while+sleeprespuestas, si está ejecutando este tipo de tareas a menudo como su rutina diaria, usar esto lo salva de presionar teclas innecesarias, y si su línea de comando comienza a ser más larga, comprender esto es un poco más fácil. Pero este comienza con dormir primero.

Esto generalmente es útil si necesita seguir algo que tiene una salida de una línea como la carga de la máquina:

while sleep 1; do uptime; done
Bekir Dogan
fuente
Si. Esto debe documentarse junto a UUOC.
Johan
10
Y si quieres que funcione como "reloj", puedes hacerlowhile clear; do date; command;sleep 5; done
Johan
¡Guau, gracias por la while sleepcombinación, ahorra keytokes!
George Y.
45

Un problema que tienen todas las respuestas publicadas hasta ahora es que el tiempo de ejecución del comando puede derivar. Por ejemplo, si realiza un sleep 10comando entre comandos y el comando tarda 2 segundos en ejecutarse, se ejecutará cada 12 segundos; si tarda una cantidad variable de tiempo en ejecutarse, a largo plazo el tiempo en que se ejecuta puede ser impredecible.

Esto podría ser justo lo que quieres; si es así, use una de las otras soluciones, o use esta pero simplifique la sleepllamada.

Para una resolución de un minuto, los trabajos cron se ejecutarán a la hora especificada, independientemente de cuánto tiempo tome cada comando. (De hecho, se iniciará un nuevo trabajo cron incluso si el anterior todavía se está ejecutando).

Aquí hay una secuencia de comandos Perl simple que duerme hasta el siguiente intervalo, por ejemplo, con un intervalo de 10 segundos, el comando podría ejecutarse a las 12:34:00, 12:34:10, 12:34:20, etc., incluso si el El comando en sí toma varios segundos. Si el comando se ejecuta más de intervalsegundos, se omitirá el siguiente intervalo (a diferencia de cron). El intervalo se calcula en relación con la época, por lo que se ejecutará un intervalo de 86400 segundos (1 día) a la medianoche 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?)
}
Keith Thompson
fuente
Ver también esta otra pregunta
Stéphane Chazelas
1
más simple en bash para minimizar la deriva (aunque puede derivar en ventanas de uso muy largas debido al comando de suspensión y bash en sí mismo acumulando sus pequeños tiempos de ejecución): while :; do sleep 1m & command; wait; done
matemática
1
@math: inteligente. No funciona si tiene otros procesos en segundo plano ejecutándose en el shell actual ( waitlos esperará a todos). Eso se puede solucionar cambiando waita wait $!, dónde $!está el PID del sleepproceso. Publique esto como respuesta y lo votaré. Pero sí produce una salida extraña en un shell interactivo.
Keith Thompson el
29

Creo que todas las respuestas aquí hasta ahora son demasiado complicadas, o en su lugar responden una pregunta diferente:

  • "Cómo ejecutar un programa repetidamente para que haya un retraso de X segundos entre el momento en que finalizó el programa y el siguiente inicio" .

La verdadera pregunta era:

  • "Cómo ejecutar un programa cada X segundos"

Estas son dos cosas muy diferentes cuando el comando tarda en terminar.

Tomemos como ejemplo el script foo.sh(imagine que este es un programa que demora unos segundos en completarse).

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

Desea ejecutar esto cada segundo, y la mayoría sugeriría watch -n1 ./foo.sh, o while sleep 1; do ./foo.sh; done. Sin embargo, esto da la salida:

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

Que no se ejecuta exactamente cada segundo. Incluso con la -pbandera, como man watchsugiere la página podría resolver esto, el resultado es el mismo.

Una manera fácil de lograr la tarea deseada, que algunos tocan, es ejecutar el comando en segundo plano. En otras palabras:

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

Y eso es todo lo que hay que hacer.

Podrías ejecutarlo cada 500 ms con sleep 0.5, o lo que tengas.

swalog
fuente
1
Quienes votaron por diferentes respuestas, no entienden la elegancia de su respuesta ...
Serge Stroobandt
Esto es bueno a menos que su programa tenga efectos secundarios. Si el programa lleva más tiempo que el intervalo, los efectos secundarios pueden interferir (como sobrescribir un archivo). Si el programa espera otra cosa, y esa otra cosa está tomando una eternidad, entonces está comenzando más y más trabajos en segundo plano indefinidamente. Usar con precaución.
John Moeller
44
podría invertir el orden del fondo para asegurarse de que no se ejecute más de un ./foo.sh a la vez, pero no más rápido que 1 por segundo: while :; do sleep 1 & ./foo.sh; wait; done
matemática
Sí, se desplazará, unos pocos milisegundos en cada bucle, pero lo hará. Haga date +"%H:%M:%S.%N"en foo.sh para ver cómo la fracción aumentará lentamente en cada ciclo.
Isaac
Sí, es cierto que la mayoría de las respuestas no abordan literalmente la pregunta. Pero es una apuesta casi segura que respondan lo que el OP quería. Sería una apuesta más segura si el OP realmente hubiera aceptado una respuesta ... Pero esto es bueno, siempre que conozca los posibles efectos secundarios y esté seguro de que el tiempo de ejecución promedio del comando no excederá el tiempo del ciclo
Auspex
17

En bash:

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

( echopodría ser reemplazado por cualquier comando ...

O en perl:

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

Donde print "I will not automate this punishment in absurdum.\n"podría ser reemplazado con "cualquier" comando rodeado de backticks (`).

Y para una pausa, agregue una sleepdeclaración dentro del ciclo for:

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

y

perl -e 'for (;1;) {print "I will not automate this punishment in absurdum.\n"; sleep 1}'
Kevin
fuente
16
while [ 0 ]Es una mala forma de escribirlo. Significa que 0es una cadena con longitud> 0. while [ 1 ]o while [ jeff ]haría lo mismo. Mejor escribir while true.
Mikel
55
@Mikel: Owhile :
Keith Thompson
10

Recent bash> = 4.2 bajo el núcleo reciente de Linux, respuesta basada.

Para limitar el tiempo de ejecución, ¡no hay horquillas ! Solo se utilizan incorporados.

Para esto, uso la readfunción incorporada en lugar de sleep. Desafortunadamente, esto no funcionará con sesiones notorias .

Función de rápida " repeat" según lo 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
}

Pequeña prueba con cadenas 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

Dependiendo de la granularidad y la duración del comando enviado ...

Bajo los últimos núcleos de Linux, hay un archivo de perfil que /proc/timer_listcontiene información de tiempo en nanosegundos.

Si desea ejecutar un comando exactamente una vez por segundo, ¡su comando debe terminar en menos de un segundo! Y a partir de ahí, sleepsolo tienes el resto del segundo actual.

Si la demora es más importante y su comando no requiere un tiempo significativo, podría:

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

Pero si su objetivo es obtener una granularidad más fina, debe:

Use la información de nanosegundos para esperar hasta el comienzo de un segundo ...

Para esto, escribí una pequeña función 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
}

Después de buscarlos, podrías:

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

ejecutar ${command[@]}directamente en la línea de comando, que comparar con

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

Esto debe dar exactamente el mismo resultado.

Contrata la función " repeat" según lo solicitado:

Podrías obtener esto:

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
}

Entonces pruébalo:

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
F. Hauri
fuente
read -tes un incorporado , mientras sleepque no lo es. Esto podría tener un efecto de borde (es decir, presionar [tecla: retorno] interrumpir silenciosamente el sueño ), pero esto podría tratarse mediante pruebas$foo
F. Hauri
Respuesta muy impresionante
gena2x
4

¿Te gustaría probar esto (Bash)?

forever ()   {
    TIMES=shift;
    SLEEP=shift;
    if [ "$TIMES" = "-1" ]; then  
        while true;
        do 
            $@
            sleep $SLEEP
        done
    else
        repeat "$TIMES" $@ 
    fi; }
hombre trabajando
fuente
No estoy muy interesado en shell, así que las mejoras son bienvenidas. Y parece que no tengo un comando "repetir" en mi distribución.
2
repeates específico de csh y tcsh.
Keith Thompson
2
@KeithThompson: y 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
fuente
4

Bash y algunos de sus shells afines tienen la conveniente (( ... ))notación en la que se pueden evaluar las expresiones aritméticas.

Entonces, como respuesta a su tercer desafío, donde tanto el recuento de repeticiones como el retraso entre cada repetición deben ser configurables, aquí hay una forma de hacerlo:

repeat=10
delay=1

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

Esta respuesta también sufre la deriva de tiempo cubierta en la respuesta de Keith .

Thor
fuente
Este fue el mejor.
Ahmad Awais
3

Como mencionó gbrandt, si el watchcomando está disponible, definitivamente úselo . Sin embargo, algunos sistemas Unix no lo tienen instalado de manera predeterminada (al menos no donde yo trabajo).

Aquí hay otra solución con una sintaxis y salida ligeramente diferentes (funciona en BASH y SH):

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

Editar: eliminé algunos "." en la última declaración de eco ... remanente de mis días en Perl;)

Kevin
fuente
3
while [ 1 ]solo funciona porque 1 se trata como una cadena, al igual que while [ -n 1 ]. while [ 0 ]o while [ jeff ]haría lo mismo. while trueTiene mucho más sentido.
Mikel
3

Si su intención no es mostrar un mensaje en su pantalla, y si pudiera permitirse el lujo de repetir el trabajo en términos de minutos, crontab , tal vez, sería su mejor herramienta. Por ejemplo, si desea ejecutar su comando cada minuto, escribiría algo como esto en su crontabarchivo:

* * * * * my_precious_command

Consulte el tutorial para obtener más ejemplos. Además, puede configurar los tiempos fácilmente usando Crontab Code Generator .

Barun
fuente
3

¿Y si tuviéramos los dos?

Aquí está la idea: con solo "intervalo", se repite para siempre. Con "intervalo" y "tiempos", se repite este número de veces, separados por "intervalo".

El uso :

$ loop [interval [times]] command

Entonces, el algoritmo será:

  • Elemento de la lista
  • si $ 1 contiene solo dígitos, es el intervalo (predeterminado 2)
  • si $ 2 contiene solo dígitos, es el número de veces (predeterminado infinito)
  • Mientras bucle con estos parámetros
    • dormir "intervalo"
    • si se han dado varias veces, disminuya una var hasta que se alcance

Por lo tanto :

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
}
Baroneso
fuente
Nota: no funciona con flotadores, a pesar de sleepque los acepta.
Baronsed
2

Terminé creando una variante en la respuesta de swalog. Con la suya, tuvo que esperar X segundos para la primera iteración, también estoy ejecutando mi en primer plano, así que ...

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

Rápido, sucio y probablemente peligroso para arrancar, pero si eres aventurero y sabes lo que estás haciendo, pon esto repeat.shy chmod 755todo,

while true
do 
    eval $1 
    sleep $2 
done

Invocarlo con ./repeat.sh <command> <interval>

Mi sentido de la araña dice que esta es probablemente una forma malvada de hacer esto, ¿es cierto mi sentido de la araña?

mallyone
fuente
8
Eval? ¿Para qué? sleep $1; shift; "$@"o similar sería mucho mejor.
Mikel
No sé por qué esto es -2. eval puede ser excesivo ... excepto cuando lo necesite. Eval le permite obtener cosas como la redirección a la línea de comando.
Johan
1

Puede ejecutar un script desde init (agregando una línea a / etc / inittab). Este script debe ejecutar su comando, dormir durante el tiempo que desea esperar hasta ejecutar el script nuevamente y salir de él. Init iniciará su script nuevamente después de salir.

Roberto Paz
fuente
1

Manera fácil de repetir un trabajo desde crontab con un intervalo de menos de un minuto (ejemplo 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.
Charles nakhel
fuente
1

Puede obtener el poder del intérprete de python desde shell.

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

reemplace cinco en cualquier momento (seg) que desee.

Atención: la devolución usa MAYÚS + ENTRAR para devolver la entrada en el shell. Y no olvide escribir 4 sangría de ESPACIO en cada línea en el ciclo while.

pah8J
fuente
Tenga en cuenta que python's os.system()ejecuta un shell para interpretar la línea de comandos, por lo que invoca pythonpara ejecutar las cáscaras en un bucle para ejecutar un comando es un poco exagerado periódicamente. También podrías hacer todo en el shell.
Stéphane Chazelas
Estoy de acuerdo contigo. Sin embargo, hay un sueño de 5 segundos como se describe en cada ciclo. Por lo tanto, se puede ignorar el costo adicional de iniciar un nuevo shell. Si este costo hace que el período sea más largo de lo esperado, puede reducir el tiempo de sueño como requisito.
pah8J
0

Esta solución funciona en MacOSX 10.7. Funciona maravillosamente

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

En mi caso

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

o

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

para limpiar mi escritorio constantemente.

Dan Ruiz
fuente
1
¿Querías tener un sleepcomando allí?
Keith Thompson
44
while [ 0 ]es una forma extraña de escribir un bucle infinito; funciona porque el testcomando (también conocido como [) trata una cadena no vacía como verdadera. while : ; do ... ; donees más idiomático, o si lo prefieres puedes usarlo while true ; do ... ; done.
Keith Thompson el
0

Puede obtener esta función recursiva:

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

o agregue un:

ininterval $*

y llama al guión.

usuario desconocido
fuente
0

Para ejecutar repetidamente un comando en una ventana de consola, generalmente ejecuto algo como esto:

while true; do (run command here); done

Esto también funciona para múltiples comandos, por ejemplo, para mostrar un reloj que se actualiza continuamente en una ventana de consola:

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

Thomas Bratt
fuente
¿Por qué el voto negativo? Puede ver que esta es una solución de trabajo copiando y pegando el ejemplo en una ventana de terminal.
Thomas Bratt
Creo que te votaron porque es un poco peligroso y consume la 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
usuario56318
fuente
0

Con zshy una sleepimplementación que acepta argumentos de punto flotante:

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

O para forever:

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

Si su sleepno admite flotadores, siempre puede redefinirlo como una envoltura alrededor zshde la zselectconstrucción:

zmodload zsh/zselect
sleep() zselect -t $((($1 * 100) | 0))
Stéphane Chazelas
fuente
-1

... Me pregunto cuántas soluciones complicadas se pueden crear al resolver este problema.

Puede ser tan fácil ...

open /etc/crontab 

ponga 1 línea siguiente al final del archivo como:

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

Si desea ejecutar algo cada 1 segundo, simplemente coloque allí:

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

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

y dentro de ese archivo.sh debería ser:

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

your code or commands

¡y eso es todo!

¡DISFRUTAR!

Mirra
fuente
66
Esto no es correcto. * / 60 se ejecutará cada 60 minutos y * / 5, por ejemplo, cada 5 minutos.
mika
1
segundos no están permitidos en crontab
GAD3R
prueba tus cosas antes de responder con una mierda equivocada.
sjas
-1
until ! sleep 60; do echo $(date); command; command; command; done

funciona para mi.

  1. No lo necesito exactamente cada 60 segundos
  2. "watch" no mira comandos en todos los sistemas
  3. "watch" solo puede tomar un comando (o un archivo de script)
  4. poner el sueño en la condición en lugar del cuerpo hace que el bucle sea mejor interrumpible (por lo que un reclamo en respuesta a una pregunta similar sobre stackoverflow. desafortunadamente puedo encontrarlo en este momento)
  5. Quiero ejecutarlo una vez antes del retraso, así que lo uso hasta en lugar de while
Titus
fuente
Acabo de notar que "hasta" aparentemente ejecuta el sueño antes de la primera iteración también. ¿Hay alguna forma de poner la condición al final del ciclo en bash?
Titus