Ripeti un comando Unix ogni x secondi per sempre


480

C'è un comando Unix incorporato il repeatcui primo argomento è il numero di volte in cui ripetere un comando, in cui il comando (con qualsiasi argomento) è specificato dagli argomenti rimanenti repeat.

Per esempio,

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

farà eco la stringa data 100 volte e poi si fermerà.

Vorrei un comando simile - chiamiamolo forever- che funziona in modo simile tranne che il primo argomento è il numero di secondi di pausa tra le ripetizioni e si ripete per sempre. Per esempio,

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

Ho pensato di chiedere se esiste una cosa del genere prima di scriverla. So che è come uno script Perl o Python a 2 righe, ma forse c'è un modo più standard per farlo. In caso contrario, sentiti libero di pubblicare una soluzione nel tuo linguaggio di scripting preferito, lo stile Rosetta Stone .

PS: Forse un modo migliore per farlo sarebbe quello di generalizzare repeatimpiegando sia il numero di volte per ripetere (con -1 che significa infinito) sia il numero di secondi per dormire tra le ripetizioni. Gli esempi precedenti diventerebbero quindi:

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

1
Perché no: ripeti [-t time] [-n numero] comando [args ...]?
Jonathan Leffler,

17
Freddo. Sfortunatamente sia sul mio Ubuntu che sulla mia ripetizione AIX il comando non è stato trovato . Puoi fare un type repeate fammi sapere da dove viene?

3
Sono anche su Ubuntu e non ho ripetuto installato. Qualsiasi informazione su come installarlo sarebbe utile.
Rory,

7
repeatè un comando incorporato in csh e tcsh.
Keith Thompson,

2
@KeithThompson, csh, tcsh e zsh . Anche se è più una parola chiave che un builtin
Stéphane Chazelas,

Risposte:


615

Prova il watchcomando.

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

Così che:

watch -n1  command

eseguirà il comando ogni secondo (bene, tecnicamente, ogni secondo più il tempo necessario per commandl'esecuzione poiché watch(almeno le implementazioni procpse busybox) dormono solo un secondo tra due serie di command, per sempre.

Desideri passare il comando a execinvece di sh -c, utilizzare l' -xopzione:

watch -n1 -x command

Su macOS, puoi ottenere watchda Mac Ports :

port install watch

Oppure puoi ottenerlo da Homebrew :

brew install watch

2
puoi usare MacPorts per installarlo. sudo port install watch

10
Disponibile anche in homebrew (brew install watch).
Lyle,

28
+1 Per un nuovo potente strumento. Una volta while true; do X; sleep Y; done, questo è molto meglio.
Adam Matan,

4
Il problema con questo strumento (almeno su Ubuntu) è che forza lo schermo intero, quindi se sto scaricando alcune informazioni periodiche, perdo le informazioni precedenti.
scorpiodawg,

4
Si chiama cmdwatchsu FreeBSD (dalle porte:) sysutils/cmdwatch.
Ouki,

243

bash

while+ sleep:

while true
do 
    echo "Hi"
    sleep 1
done

Ecco la stessa cosa di una scorciatoia one-liner (dai commenti sotto):

while sleep 1; do echo "Hi"; done

Utilizza ;per separare i comandi e utilizza sleep 1per il whiletest poiché restituisce sempre true. Puoi inserire più comandi nel loop: basta separarli con;


2
Credo che funzioni anche come scritto in una shell Bourne generica.
dmckee,

5
@dreeves, utilizzando ";" come separatore, i comandi possono essere eseguiti in una sola riga. Guarda la risposta di Toony come esempio
bbaja42,

56
sleep 1 restituisce sempre true in modo da poterlo utilizzare come condizione while. Non la cosa più leggibile al mondo ma assolutamente legittima: while sleep 1; fai l'eco "Ciao"; fatto
David Costa,

3
@David Costa: hai fatto un punto ragionevole, ma sleepnon sempre ritorna vero. Ī̲ ha usato tali loop (anche se con un ritardo maggiore), in particolare perché esiste un modo per terminarlo con grazia uccidendo il processo di sospensione.
Incnis Mrsi,

2
@IncnisMrsi Non ci ho pensato, in realtà restituisce zero solo quando è trascorso il tempo e diverso da zero quando è terminato da un segnale. Grazie per averlo segnalato
David Costa,

71

Questa è solo una versione più breve di altre while+sleeprisposte, se stai eseguendo questo tipo di attività spesso come la tua routine quotidiana, l'utilizzo di questo ti salva da inutili pressioni di tasti e se la tua riga di comando inizia ad allungare la comprensione, questa è un po 'più semplice. Ma questo inizia prima di dormire.

Questo è generalmente utile se devi seguire qualcosa che ha un output di una riga come il carico della macchina:

while sleep 1; do uptime; done

Sì. Questo dovrebbe essere documentato accanto a UUOC.
Johan

10
E se vuoi che funzioni come "guarda", puoi farlowhile clear; do date; command;sleep 5; done
Johan

Wow, grazie per la while sleepcombinazione, salva i tasti!
George Y.

45

Un problema che tutte le risposte postate finora hanno è che il tempo di esecuzione del comando può andare alla deriva. Ad esempio, se si esegue un sleep 10comando tra i comandi e l'esecuzione del comando richiede 2 secondi, verrà eseguita ogni 12 secondi; se l'esecuzione richiede una quantità variabile di tempo, a lungo termine il tempo in cui viene eseguito può essere imprevedibile.

Questo potrebbe essere proprio quello che vuoi; in tal caso, utilizzare una delle altre soluzioni o utilizzare questa ma semplificando la sleepchiamata.

Per una risoluzione di un minuto, i processi cron verranno eseguiti all'ora specificata, indipendentemente dalla durata di ciascun comando. (In effetti un nuovo cron job verrà avviato anche se il precedente è ancora in esecuzione.)

Ecco un semplice script Perl che dorme fino all'intervallo successivo, quindi ad esempio con un intervallo di 10 secondi il comando potrebbe essere eseguito alle 12:34:00, 12:34:10, 12:34:20, ecc., Anche se il il comando stesso richiede alcuni secondi. Se il comando viene eseguito per più di intervalsecondi, l'intervallo successivo verrà ignorato (diversamente dal cron). L'intervallo viene calcolato rispetto all'epoca, quindi un intervallo di 86400 secondi (1 giorno) verrà eseguito a mezzanotte 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
più semplice in bash per ridurre al minimo la deriva (anche se potrebbe spostarsi su finestre di utilizzo molto lunghe a causa del comando sleep e bash stesso accumulando i suoi tempi di esecuzione minuscoli): while :; do sleep 1m & command; wait; done
matematica

1
@math: Clever. Non funziona se nella shell corrente sono in esecuzione altri processi in background ( waitli aspetteranno tutti). È risolvibile cambiando waitin wait $!, dov'è $!il PID del sleepprocesso. Pubblica questo come risposta e lo voterò. Ma produce un output estraneo in una shell interattiva.
Keith Thompson,

29

Penso che tutte le risposte qui finora siano o troppo contorte, o invece rispondano a una domanda diversa:

  • "Come eseguire ripetutamente un programma in modo che ci siano X secondi di ritardo tra il termine del programma e l'avvio successivo" .

La vera domanda era:

  • "Come eseguire un programma ogni X secondi"

Queste sono due cose molto diverse quando il comando impiega tempo per terminare.

Prendi ad esempio lo script foo.sh(fai finta che questo sia un programma che richiede alcuni secondi per essere completato).

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

Si desidera eseguire questo ogni secondo, e la maggior parte suggerirebbe watch -n1 ./foo.sh, o while sleep 1; do ./foo.sh; done. Tuttavia, questo dà l'output:

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

Che non viene eseguito esattamente ogni secondo. Anche con la -pbandiera, come man watchsuggerisce la pagina potrebbe risolverlo, il risultato è lo stesso.

Un modo semplice per eseguire l'attività desiderata, che alcuni tocchi, è quello di eseguire il comando in background. In altre parole:

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

E questo è tutto ciò che c'è da fare.

Potresti eseguirlo ogni 500 ms sleep 0.5o con quello che hai.


1
Coloro che hanno votato su risposte diverse, non comprendono l'eleganza della tua risposta ...
Serge Stroobandt,

Questo va bene a meno che il tuo programma non abbia effetti collaterali. Se il programma impiega più tempo dell'intervallo, gli effetti collaterali possono interferire (come sovrascrivere un file). Se il programma attende qualcos'altro e quel qualcos'altro impiegherà un'eternità, allora inizierai sempre più lavori in background a tempo indeterminato. Usare con cautela.
John Moeller,

4
potrebbe invertire l'ordine dello sfondo per garantire che non venga eseguito più di un ./foo.sh alla volta, ma non più velocemente di 1 al secondo: while :; do sleep 1 & ./foo.sh; wait; done
matematica

Sì, andrà alla deriva, qualche millisecondo su ogni ciclo, ma lo farà. Fai date +"%H:%M:%S.%N"in foo.sh per vedere come la frazione aumenterà lentamente su ogni ciclo.
Isaac,

Sì, è vero che la maggior parte delle risposte non affronta letteralmente la domanda. Ma è una scommessa quasi sicura che rispondano a ciò che l'OP voleva. Sarebbe una scommessa più sicura se l'OP avesse effettivamente accettato una risposta ... Ma questo è buono, a condizione che tu sia a conoscenza di possibili effetti collaterali e sei certo che il tempo medio di esecuzione del comando non supererà il tempo di ciclo
Auspex,

17

In bash:

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

( echopotrebbe essere sostituito da qualsiasi comando ...

O in perl:

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

Dove print "I will not automate this punishment in absurdum.\n"potrebbe essere sostituito con il comando "any" circondato da backtick (`).

E per una pausa, aggiungi sleepun'istruzione all'interno del ciclo 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 ]è un brutto modo di scriverlo. Significa che 0è una stringa con lunghezza> 0. while [ 1 ]o while [ jeff ]farebbe la stessa cosa. Meglio scrivere while true.
Mikel,

5
@Mikel: Oppurewhile :
Keith Thompson il

10

Bash recente> = 4.2 con kernel Linux recente, risposta basata.

Per limitare i tempi di esecuzione, non ci sono forcelle ! Vengono utilizzati solo i built-in.

Per questo, io uso la readfunzione integrata invece di sleep. Sfortunatamente questo non funzionerà con quaranta sessioni.

Funzione rapida " repeat" come richiesto:

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
}

Piccolo test con stringhe tra virgolette:

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

A seconda della granularità e della durata del comando inviato ...

Sotto i recenti kernel Linux, esiste un procfile /proc/timer_listcontenente informazioni temporali in nanosecondi.

Se si desidera eseguire un comando esattamente una volta al secondo, il comando deve terminare in meno di un secondo! E da lì, devi sleepsolo il resto dell'attuale secondo.

Se il ritardo è più importante e il comando non richiede tempo significativo, è possibile:

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

Ma se il tuo obiettivo è ottenere una granularità più fine, devi:

Usa le informazioni sui nanosecondi per attendere l'inizio di un secondo ...

Per questo, ho scritto una piccola funzione 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
}

Dopo averli acquistati, potresti:

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

eseguire ${command[@]}direttamente dalla riga di comando, rispetto a

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

questo deve dare esattamente lo stesso risultato.

Assume la funzione " repeat" come richiesto:

Potresti procurartelo:

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
}

Quindi provalo:

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è un built-in , mentre sleepnon lo è. Ciò potrebbe avere un effetto bordo (ovvero premere [key: return] per interrompere in modo silenzioso il sonno ), ma potrebbe essere trattato mediante test$foo
F. Hauri,

Risposta molto impressionante
gena2x

4

Ti interessa provare questo (Bash)?

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

Non sono molto entusiasta di Shell, quindi i miglioramenti sono ben accetti. E non mi sembra di avere un comando "ripeti" nella mia distribuzione.

2
repeatè specifico per csh e tcsh.
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

Bash e alcune delle sue conchiglie affini hanno la (( ... ))notazione conveniente in cui le espressioni aritmetiche possono essere valutate.

Quindi, come risposta alla tua terza sfida, in cui sia il conteggio delle ripetizioni che il ritardo tra ciascuna ripetizione dovrebbero essere configurabili, ecco un modo per farlo:

repeat=10
delay=1

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

Questa risposta soffre anche della deriva temporale descritta nella risposta di Keith .


Questo è stato il migliore.
Ahmad Awais,

3

Come menzionato da gbrandt, se il watchcomando è disponibile, utilizzalo sicuramente. Alcuni sistemi Unix, tuttavia, non lo hanno installato di default (almeno non dove lavoro io).

Ecco un'altra soluzione con sintassi e output leggermente diversi (funziona in BASH e SH):

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

Modifica: ho rimosso alcuni "." nell'ultima dichiarazione di eco ... holdover dai miei giorni Perl;)


3
while [ 1 ]funziona solo perché 1 viene trattato come una stringa, proprio come while [ -n 1 ]. while [ 0 ]o while [ jeff ]farebbe la stessa cosa. while trueha molto più senso.
Mikel,

3

Se la tua intenzione non è quella di visualizzare un messaggio sullo schermo e se puoi permetterti di ripetere il lavoro in termini di minuti, crontab , forse, sarebbe il tuo strumento migliore. Ad esempio, se desideri eseguire il tuo comando ogni minuto, scrivi qualcosa del genere nel tuo crontabfile:

* * * * * my_precious_command

Consulta il tutorial per ulteriori esempi. Inoltre, puoi impostare facilmente i tempi usando Crontab Code Generator .


3

E se avessimo entrambi?

Ecco l'idea: con solo "intervallo", si ripete per sempre. Con "intervallo" e "volte", ripete questo numero di volte, separato da "intervallo".

L'utilizzo :

$ loop [interval [times]] command

Quindi, l'algoritmo sarà:

  • Voce di elenco
  • se $ 1 contiene solo cifre, è l'intervallo (predefinito 2)
  • se $ 2 contiene solo cifre, è il numero di volte (infinito predefinito)
  • mentre loop con questi parametri
    • "intervallo" di sonno
    • se è stato dato un numero di volte, decrementa una var fino a quando non viene raggiunta

Perciò :

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: non funziona con i float, nonostante sleepli accetti.
Baronato

2

Ho finito per creare una variante sulla risposta del cagnolino. Con i suoi hai dovuto aspettare X secondi per la prima iterazione, sto anche eseguendo il mio in primo piano quindi ..

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

1

Veloce, sporco e probabilmente pericolosa per l'avvio, ma se siete avventurosi e sapere cosa si sta facendo, mettere questo in repeat.she chmod 755che,

while true
do 
    eval $1 
    sleep $2 
done

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

Il mio senso spidey dice che questo è probabilmente un modo malvagio per farlo, il mio senso spidey è giusto?


8
Eval !? Per che cosa? sleep $1; shift; "$@"o simile sarebbe molto meglio.
Mikel,

Non so perché questo è -2. eval potrebbe essere eccessivo ... tranne quando ne hai bisogno. Eval ti consente di ottenere cose come il reindirizzamento sulla riga di comando.
Johan

1

È possibile eseguire uno script da init (aggiungendo una riga a / etc / inittab). Questo script deve eseguire il comando, dormire per il tempo che si desidera attendere fino a quando non si esegue nuovamente lo script ed uscire. Init riavvierà nuovamente lo script dopo l'uscita.


1

Modo semplice per ripetere un lavoro da crontab con un intervallo inferiore a un minuto (esempio 20 secondi):

crontab: * * * * * script.sh

script.sh:

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

sleep 20
>>retype your commands here.

sleep 20
>>retype your commands here.

1

È possibile ottenere il potere dall'interprete Python dalla shell.

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

sostituisci cinque in qualsiasi momento (sec) che ti piace.

Attenzione: il ritorno usa MAIUSC + INVIO per restituire l'input nella shell. E non dimenticare di digitare 4 rientro SPACE in ciascuna riga del ciclo while.


Si noti che python's os.system()esegue una shell di interpretare la riga di comando, in modo da invocare pythonper eseguire conchiglie in un ciclo per eseguire un comando è periodicamente un po' eccessivo. Potresti anche fare tutto nella shell.
Stéphane Chazelas,

Sono d'accordo con te. Tuttavia, vi è un sonno di 5 secondi, come descritto in ciascun ciclo. Pertanto, il costo aggiuntivo derivante dall'avvio di una nuova shell può essere ignorato. Se questo costo rende il periodo più lungo del previsto, è possibile ridurre un po 'di tempo di sonno come requisito.
pah8J,

0

Questa soluzione funziona in MacOSX 10.7. Funziona meravigliosamente.

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

Nel mio caso

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

o

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

per pulire costantemente il mio desktop.


1
Intendevi avere un sleepcomando lì?
Keith Thompson,

4
while [ 0 ]è un modo strano di scrivere un ciclo infinito; funziona perché il testcomando (noto anche come [) considera vera una stringa non vuota. while : ; do ... ; doneè più idiomatico, o se preferisci puoi usare while true ; do ... ; done.
Keith Thompson,

0

È possibile generare questa funzione ricorsiva:

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

o aggiungi un:

ininterval $*

e chiama la sceneggiatura.


0

Per eseguire ripetutamente un comando in una finestra della console di solito eseguo qualcosa del genere:

while true; do (run command here); done

Funziona anche con più comandi, ad esempio per visualizzare un orologio in continuo aggiornamento in una finestra della console:

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


Perché il downvote? Puoi vedere che questa è una soluzione funzionante copiando e incollando l'esempio in una finestra terminale.
Thomas Bratt,

Penso che tu abbia votato perché è un po 'pericoloso e consuma 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

0

Con zshe sleepun'implementazione che accetta argomenti in virgola mobile:

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

O per forever:

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

Se il tuo sleepnon supporta i float, puoi sempre ridefinirlo come un wrapper attorno zshal zselectbuiltin:

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

-1

... Mi chiedo quante soluzioni complicate possano essere create per risolvere questo problema.

Può essere così facile ...

open /etc/crontab 

metti 1 riga successiva alla fine del file come:

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

Se vuoi eseguire qualcosa ogni 1 secondo, inserisci lì:

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

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

e all'interno di quel file.sh dovrebbe essere:

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

your code or commands

e questo è tutto!

GODERE!


6
Questo non è corretto * / 60 verrà eseguito ogni 60 minuti e * / 5, ad esempio, ogni 5 minuti.
mika,

1
secondi non consentiti su crontab
GAD3R

prova le tue cose prima di rispondere con merda sbagliata.
sabato

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

per me va bene.

  1. Non ne ho bisogno esattamente ogni 60 secondi
  2. "watch" non guarda i comandi su tutti i sistemi
  3. "watch" può richiedere solo un comando (o un file di script)
  4. mettere il sonno nella condizione invece del corpo rende il loop meglio interrompibile (quindi un reclamo in una risposta a una domanda simile su stackoverflow. sfortunatamente posso trovarlo al momento)
  5. Voglio eseguirlo una volta prima del ritardo, quindi lo uso fino a quando invece di while

Ho appena notato che "fino a" apparentemente esegue anche il sonno prima della prima iterazione. c'è un modo per mettere la condizione alla fine del loop in bash?
Tito,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.