Wiederholen Sie für immer alle x Sekunden einen Unix-Befehl

480

Es gibt einen integrierten Unix-Befehl, repeatdessen erstes Argument angibt, wie oft ein Befehl wiederholt werden soll, wobei der Befehl (mit allen Argumenten) durch die verbleibenden Argumente für angegeben wird repeat.

Zum Beispiel,

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

wird die angegebene Zeichenfolge 100 Mal wiedergeben und dann anhalten.

Ich hätte gerne einen ähnlichen Befehl - nennen wir es ihn forever- der ähnlich funktioniert, mit der Ausnahme, dass das erste Argument die Anzahl der Sekunden angibt, die zwischen den Wiederholungen eine Pause eingelegt werden muss und der sich für immer wiederholt. Zum Beispiel,

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

Ich dachte, ich würde fragen, ob so etwas existiert, bevor ich es schreibe. Ich weiß, es ist wie ein zweizeiliges Perl- oder Python-Skript, aber vielleicht gibt es eine Standardmethode dafür. Wenn nicht, können Sie eine Lösung in Ihrer bevorzugten Skriptsprache, Rosetta Stone, veröffentlichen .

PS: Vielleicht ist es eine bessere Möglichkeit, dies zu verallgemeinern repeat, wenn Sie sowohl die Anzahl der Wiederholungen (-1 bedeutet unendlich) als auch die Anzahl der Sekunden zwischen den Wiederholungen angeben. Die obigen Beispiele würden dann werden:

% repeat 100 0 echo "I will not automate this punishment."
% repeat -1 5 echo "This will get echoed every 5 seconds forever."
Dreeves
quelle
1
Warum nicht: Befehl [-t time] [-n number] wiederholen [args ...]?
Jonathan Leffler
17
Cool. Leider beide auf meinem Ubuntu und meine AIX Wiederholung ist Befehl nicht gefunden . Können Sie ein type repeatund lassen Sie mich wissen, woher kommt?
3
Ich bin auch auf Ubuntu und habe keine Wiederholung installiert. Informationen zur Installation sind hilfreich.
Rory
7
repeatist ein eingebauter Befehl in csh und tcsh.
Keith Thompson
2
@ KeithThompson, csh, tcsh und zsh . Obwohl es eher ein Schlüsselwort als ein eingebautes ist
Stéphane Chazelas

Antworten:

615

Versuchen Sie den watchBefehl.

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

Damit:

watch -n1  command

Der Befehl wird jede Sekunde ausgeführt (naja, technisch gesehen, jede Sekunde plus die Zeit, die für commanddie Ausführung benötigt wird, da watch(zumindest die procpsund busyboxImplementierungen) nur eine Sekunde zwischen zwei Läufen von command) für immer ruht.

Möchten Sie den Befehl execstatt an übergeben sh -c, verwenden Sie die -xOption:

watch -n1 -x command

Unter macOS können Sie watchvon Mac-Ports Folgendes erhalten :

port install watch

Oder Sie können es von Homebrew bekommen :

brew install watch
Gregor Brandt
quelle
2
Sie können MacPorts verwenden, um es zu installieren. sudo port install watch
10
Auch als Homebrew erhältlich (Brew Install Watch).
Lyle
28
+1 Für ein neues mächtiges Werkzeug. Früher while true; do X; sleep Y; done- das ist viel besser.
Adam Matan
4
Das Problem mit diesem Tool (zumindest unter Ubuntu) ist, dass es den Vollbildmodus erzwingt. Wenn ich also regelmäßig Informationen abspeichere, verliere ich die vorherigen Informationen.
Scorpiodawg
4
Es wird cmdwatchunter FreeBSD aufgerufen (von den Ports aus:) sysutils/cmdwatch.
Ouki
243

Bash

while+ sleep:

while true
do 
    echo "Hi"
    sleep 1
done

Hier ist das Gleiche wie bei einem Einzeiler mit Kurzschrift (aus den Kommentaren unten):

while sleep 1; do echo "Hi"; done

Wird verwendet ;, um Befehle zu trennen und sleep 1für den whileTest zu verwenden, da immer true zurückgegeben wird. Sie können weitere Befehle in die Schleife einfügen - trennen Sie sie einfach mit;

Syntax-Fehler
quelle
2
Ich glaube, es funktioniert auch so, wie es in einer generischen Bourne-Shell geschrieben ist.
dmckee
5
@dreeves mit ";" Befehle können als Trennzeichen in einer Zeile ausgeführt werden. Schauen Sie sich als Beispiel Toonys Antwort an
bbaja42
56
sleep 1 gibt immer true zurück, sodass Sie es als while-Bedingung verwenden können. Nicht das lesenswerteste auf der Welt, aber absolut legitim: im Schlaf 1; Echo "Hi"; erledigt
David Costa
3
@ David Costa: Sie haben einen vernünftigen Punkt gemacht, aber sleepnicht immer wahr zurück. Ī̲ verwendet solche Schleifen (wenn auch mit längerer Verzögerung), weil es eine Möglichkeit gibt, sie ordnungsgemäß zu beenden, indem der Schlafprozess abgebrochen wird.
Incnis Mrsi
2
@IncnisMrsi Ich habe nicht daran gedacht, es gibt tatsächlich nur dann Null zurück, wenn die Zeit abgelaufen ist, und nicht Null, wenn es durch ein Signal beendet wird. Vielen Dank für den Hinweis
David Costa
71

Dies ist nur eine kürzere Version anderer while+sleepAntworten. Wenn Sie diese Art von Aufgaben häufig ausführen, erspart dies Ihnen unnötiges Drücken von Tasten, und wenn Ihre Befehlszeile länger wird, ist dies ein bisschen einfacher. Aber dieser fängt mit dem Schlafen an.

Dies ist im Allgemeinen nützlich, wenn Sie einer einzeiligen Ausgabe wie der Maschinenlast folgen müssen:

while sleep 1; do uptime; done
Bekir Dogan
quelle
Ja. Dies sollte neben UUOC dokumentiert werden.
Johan
10
Und wenn Sie möchten, dass es wie "Uhr" funktioniert, können Sie dies tunwhile clear; do date; command;sleep 5; done
Johan
Wow, danke für die while sleepKombination, spart Tastenanschläge!
George Y.
45

Ein Problem, das alle bisher veröffentlichten Antworten haben, ist, dass die Zeit, zu der der Befehl ausgeführt wird, abweichen kann. Wenn Sie beispielsweise einen sleep 10Befehl zwischen zwei Befehlen ausführen und die Ausführung des Befehls 2 Sekunden dauert, wird er alle 12 Sekunden ausgeführt. Wenn die Laufzeit unterschiedlich lang ist, kann die Laufzeit auf lange Sicht unvorhersehbar sein.

Dies könnte genau das sein, was Sie wollen; Verwenden Sie in diesem Fall eine der anderen Lösungen, oder verwenden Sie diese, aber vereinfachen Sie den sleepAufruf.

Bei einer Auflösung von einer Minute werden Cron-Jobs zur angegebenen Zeit ausgeführt, unabhängig davon, wie lange die einzelnen Befehle dauern. (Tatsächlich wird ein neuer Cron-Job gestartet, auch wenn der vorherige noch ausgeführt wird.)

Hier ist ein einfaches Perl-Skript, das bis zum nächsten Intervall ruht. In einem Intervall von beispielsweise 10 Sekunden wird der Befehl möglicherweise um 12:34:00, 12:34:10, 12:34:20 usw. ausgeführt, selbst wenn die Befehl selbst dauert mehrere Sekunden. Wenn der Befehl länger als intervalSekunden ausgeführt wird, wird das nächste Intervall übersprungen (im Gegensatz zu cron). Das Intervall wird relativ zur Epoche berechnet, sodass um Mitternacht UTC ein Intervall von 86400 Sekunden (1 Tag) ausgeführt wird.

#!/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
quelle
Siehe auch diese andere Frage
Stéphane Chazelas
1
Einfacher in Bash, um Drift zu minimieren (obwohl es aufgrund des Sleep-Befehls über sehr lange Nutzungsfenster driften kann und sich seine winzigen Ausführungszeiten ansammeln): while :; do sleep 1m & command; wait; done
Mathe
1
@math: Clever. Es funktioniert nicht, wenn in der aktuellen Shell andere Hintergrundprozesse ausgeführt werden ( waitauf alle warten). Das lässt sich beheben, indem Sie waitauf " wait $!Wo $!ist die PID des sleepProzesses ?" Ändern . Poste dies als Antwort und ich stimme dem zu. In einer interaktiven Shell wird jedoch eine gewisse Fremdausgabe erzeugt.
Keith Thompson
29

Ich denke, alle Antworten hier sind entweder zu kompliziert oder beantworten stattdessen eine andere Frage:

  • Msgstr "So führen Sie ein Programm wiederholt aus, sodass zwischen dem Beenden des Programms und dem nächsten Start eine Verzögerung von X Sekunden liegt" .

Die eigentliche Frage war:

  • "Wie man alle X Sekunden ein Programm ausführt"

Dies sind zwei sehr unterschiedliche Dinge, wenn der Befehl einige Zeit in Anspruch nimmt.

Nehmen Sie zum Beispiel das Skript foo.sh(stellen Sie sich vor, dies ist ein Programm, das einige Sekunden benötigt).

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

Sie möchten dies jede Sekunde ausführen, und die meisten würden vorschlagen watch -n1 ./foo.sh, oder while sleep 1; do ./foo.sh; done. Dies ergibt jedoch die Ausgabe:

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

Welches ist nicht genau jede Sekunde ausgeführt. Selbst mit der -pFlagge ist man watchdas Ergebnis dasselbe , wie auf der Seite vorgeschlagen.

Eine einfache Möglichkeit, die gewünschte Aufgabe zu erledigen, besteht darin, den Befehl im Hintergrund auszuführen. Mit anderen Worten:

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

Und das ist alles was dazu gehört.

Du sleep 0.5könntest es alle 500 ms mit laufen lassen , oder was hast du.

Swalog
quelle
1
Diejenigen, die über unterschiedliche Antworten abgestimmt haben, verstehen die Eleganz Ihrer Antwort nicht ...
Serge Stroobandt
Dies ist gut, es sei denn, Ihr Programm hat Nebenwirkungen. Wenn das Programm länger als das Intervall dauert, können die Nebenwirkungen stören (wie das Überschreiben einer Datei). Wenn das Programm auf etwas anderes wartet und etwas anderes ewig dauert, starten Sie immer mehr Hintergrundjobs auf unbestimmte Zeit. Mit Vorsicht verwenden.
John Moeller
4
könnte die Reihenfolge der Hintergründe umkehren, um sicherzustellen, dass nicht mehr als ein ./foo.sh gleichzeitig ausgeführt wird, jedoch nicht schneller als 1 pro Sekunde: while :; do sleep 1 & ./foo.sh; wait; done
math
Ja, es wird ein paar Millisekunden auf jeder Schleife driften, aber es wird. Machen Sie date +"%H:%M:%S.%N"in foo.sh, um zu sehen, wie der Bruchteil in jeder Schleife langsam zunimmt.
Isaac
Ja, es stimmt, dass die meisten Antworten die Frage nicht buchstäblich beantworten. Aber es ist eine fast sichere Wette, dass sie antworten, was die OP wollte. Es wäre sicherer, wenn das OP tatsächlich eine Antwort akzeptiert hätte ... Dies ist jedoch gut, vorausgesetzt, Sie kennen die möglichen Nebenwirkungen und sind sicher, dass die durchschnittliche Laufzeit des Befehls die Zykluszeit nicht überschreiten wird
Auspex
17

In bash:

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

( echoKann durch einen beliebigen Befehl ersetzt werden ...

Oder in perl:

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

Wo print "I will not automate this punishment in absurdum.\n"könnte mit "jedem" Befehl mit Backticks (`) ersetzt werden.

Fügen Sie für eine Pause eine sleepAnweisung in die for-Schleife ein:

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

und

perl -e 'for (;1;) {print "I will not automate this punishment in absurdum.\n"; sleep 1}'
Kevin
quelle
16
while [ 0 ]ist ein schlechter Weg, um es zu schreiben. Dies bedeutet, dass 0es sich um eine Zeichenfolge mit einer Länge> 0 handelt while [ 1 ]oder while [ jeff ]dasselbe tun würde. Besser schreiben while true.
Mikel
5
@ Mikel: Oderwhile :
Keith Thompson
10

Letzte Bash> = 4.2 unter neueren Linux-Kernel, basierte Antwort.

Um die Ausführungszeit zu begrenzen, gibt es keine Gabeln ! Es werden nur eingebaute verwendet.

Dafür verwende readich stattdessen die eingebaute Funktion sleep. Leider funktioniert das nicht mit kleinen Sessions.

Schnelle Funktion " repeat" wie gewünscht:

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
}

Kleiner Test mit zitierten Strings:

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

Abhängig von der Granularität und Dauer des übermittelten Befehls ...

Unter neueren Linux-Kerneln gibt es ein Protokoll, /proc/timer_listdas Zeitinformationen in Nanosekunden enthält.

Wenn Sie einen Befehl genau einmal pro Sekunde ausführen möchten, muss der Befehl in weniger als einer Sekunde beendet sein! Und von dort müssen Sie sleepnur den Rest der aktuellen Sekunde.

Wenn die Verzögerung wichtiger ist und Ihr Befehl keine nennenswerte Zeit benötigt, können Sie:

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

Wenn Sie jedoch eine feinere Granularität erzielen möchten, müssen Sie:

Verwenden Sie Nanosekunden-Informationen, um bis zum Beginn einer Sekunde zu warten ...

Dafür habe ich eine kleine Bash-Funktion geschrieben:

# 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
}

Nach der Beschaffung können Sie:

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

laufen ${command[@]}direkt auf der Kommandozeile, als zu vergleichen

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

dies muss genau dasselbe Ergebnis liefern.

Stellt die Funktion " repeat" wie gewünscht ein:

Sie könnten dies beziehen:

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
}

Dann probiere es aus:

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
quelle
read -tist ein eingebautes , während sleepes nicht ist. Dies könnte Randeffekte haben (dh das Drücken von [key: return] unterbricht leise den Schlaf ), aber dies könnte durch Testen $foo
behoben
Sehr beeindruckende Antwort
gena2x
4

Möchtest du das ausprobieren (Bash)?

forever ()   {
    TIMES=shift;
    SLEEP=shift;
    if [ "$TIMES" = "-1" ]; then  
        while true;
        do 
            $@
            sleep $SLEEP
        done
    else
        repeat "$TIMES" $@ 
    fi; }
Mann bei der Arbeit
quelle
Ich bin nicht zu heiß auf Shell, daher sind Verbesserungen willkommen. Und ich habe anscheinend keinen "Wiederholungs" -Befehl in meiner Distribution.
2
repeatist spezifisch für csh und tcsh.
Keith Thompson
2
@ Keith Thompson: und 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
quelle
4

Bash und einige seiner verwandten Shells haben die praktische (( ... ))Notation, in der arithmetische Ausdrücke ausgewertet werden können.

Als Antwort auf Ihre dritte Herausforderung, bei der sowohl die Anzahl der Wiederholungen als auch die Verzögerung zwischen den einzelnen Wiederholungen konfigurierbar sein sollten, haben Sie folgende Möglichkeiten:

repeat=10
delay=1

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

Diese Antwort leidet auch unter der Zeitverschiebung, die in Keiths Antwort behandelt wird .

Thor
quelle
Das war das Beste.
Ahmad Awais
3

Wie von gbrandt erwähnt, sollte der watchBefehl unbedingt verwendet werden , wenn er verfügbar ist. Auf einigen Unix-Systemen ist es jedoch nicht standardmäßig installiert (zumindest dort, wo ich arbeite).

Hier ist eine andere Lösung mit leicht unterschiedlicher Syntax und Ausgabe (funktioniert in BASH und SH):

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

Bearbeiten: Ich habe einige "." in der letzten echo aussage ... holdover aus meinen perl tagen;)

Kevin
quelle
3
while [ 1 ]funktioniert nur, weil 1 wie ein String behandelt wird while [ -n 1 ]. while [ 0 ]oder while [ jeff ]würde das gleiche tun. while truemacht viel mehr Sinn.
Mikel
3

Wenn Sie keine Meldung auf Ihrem Bildschirm anzeigen möchten und es sich leisten können, den Auftrag in Minuten zu wiederholen, ist crontab möglicherweise das beste Werkzeug. Wenn Sie beispielsweise Ihren Befehl jede Minute ausführen möchten, schreiben Sie Folgendes in Ihre crontabDatei:

* * * * * my_precious_command

Bitte schauen Sie sich das Tutorial für weitere Beispiele an. Sie können die Timings auch einfach mit Crontab Code Generator einstellen .

Barun
quelle
3

Was wäre, wenn wir beide hätten?

Hier ist die Idee: Mit nur "Intervall" wird es für immer wiederholt. Mit "Intervall" und "Zeiten" wird diese Anzahl von Malen wiederholt, getrennt durch "Intervall".

Die Verwendung :

$ loop [interval [times]] command

Der Algorithmus lautet also:

  • Listenpunkt
  • Wenn $ 1 nur Ziffern enthält, ist dies das Intervall (Standard 2).
  • Wenn $ 2 nur Ziffern enthält, ist dies die Häufigkeit (Standardeinstellung unendlich).
  • while-Schleife mit diesen Parametern
    • Schlaf "Intervall"
    • Wenn eine Anzahl von Malen angegeben wurde, dekrementieren Sie eine Variable, bis sie erreicht ist

Deshalb :

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
}
Freiherr
quelle
Hinweis: Es funktioniert nicht mit Floats, obwohl sleepsie akzeptiert werden.
Baronsed
2

Am Ende habe ich eine Variante der Antwort von swalog erstellt. Mit seiner musste man X Sekunden auf die erste Iteration warten, ich renne auch meine so in den Vordergrund ..

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

Schnell, schmutzig und wahrscheinlich gefährlich zu booten, aber wenn Sie abenteuerlustig sind und wissen, was Sie tun, setzen Sie dies in repeat.shund chmod 755es,

while true
do 
    eval $1 
    sleep $2 
done

Rufe es mit auf ./repeat.sh <command> <interval>

Mein spidey sense sagt, dass dies wahrscheinlich ein böser Weg ist, mein spidey sense richtig?

Mallyon
quelle
8
Eval !? Wozu? sleep $1; shift; "$@"oder ähnliches wäre viel besser.
Mikel
Keine Ahnung warum das -2 ist. eval kann übertrieben sein ... außer wenn du es brauchst. Mit Eval können Sie Dinge wie die Umleitung auf die Befehlszeile erhalten.
Johan
1

Sie können ein Skript von init ausführen (indem Sie eine Zeile zu / etc / inittab hinzufügen). Dieses Skript muss Ihren Befehl ausführen, sich für die Zeit, die Sie warten möchten, in den Ruhezustand versetzen, bis das Skript erneut ausgeführt wird, und es beenden. Init startet Ihr Skript nach dem Beenden erneut.

Roberto Paz
quelle
1

Einfache Möglichkeit, einen Auftrag von crontab mit einem Intervall von weniger als einer Minute zu wiederholen (Beispiel: 20 Sekunden):

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
quelle
1

Sie können die Leistung vom Python-Interpreter aus der Shell beziehen.

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

Ersetzen Sie fünf in einer beliebigen Zeit (Sek.).

Achtung: Mit UMSCHALT + EINGABETASTE geben Sie die Eingabe in der Shell zurück. Und vergessen Sie nicht, in jede Zeile der while-Schleife 4 SPACE- Einrückungen einzugeben.

pah8J
quelle
Beachten Sie, dass python"s" os.system()eine Shell ausführt, um die Befehlszeile zu interpretieren. Das Aufrufen pythonvon "s" , um Shells in einer Schleife auszuführen, um einen Befehl regelmäßig auszuführen, ist also ein wenig übertrieben. Sie können genauso gut alles in der Shell tun.
Stéphane Chazelas
Ich stimme mit Ihnen ein. Es gibt jedoch 5 Sekunden Schlaf, wie Sie in jeder Schleife beschrieben haben. Somit können die zusätzlichen Kosten für das Starten einer neuen Shell ignoriert werden. Wenn diese Kosten den Zeitraum länger als erwartet machen, können Sie die erforderliche Schlafzeit verkürzen.
Pah8J
0

Diese Lösung funktioniert unter MacOSX 10.7. Das funktioniert wunderbar.

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

In meinem Fall

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

oder

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

um meinen Desktop ständig aufzuräumen.

Dan Ruiz
quelle
1
Wollten Sie dort einen sleepBefehl haben?
Keith Thompson
4
while [ 0 ]ist ein seltsamer Weg, eine Endlosschleife zu schreiben; Dies funktioniert, weil der testBefehl (auch bekannt als [) eine nicht leere Zeichenfolge als wahr behandelt. while : ; do ... ; doneist idiomatischer, oder wenn Sie es vorziehen, können Sie verwenden while true ; do ... ; done.
Keith Thompson
0

Sie könnten diese rekursive Funktion beziehen:

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

oder fügen Sie hinzu:

ininterval $*

und das Skript aufrufen.

Benutzer unbekannt
quelle
0

Um einen Befehl in einem Konsolenfenster wiederholt auszuführen, führe ich normalerweise Folgendes aus:

while true; do (run command here); done

Dies funktioniert auch für mehrere Befehle, um beispielsweise eine ständig aktualisierte Uhr in einem Konsolenfenster anzuzeigen:

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

Thomas Bratt
quelle
Warum die Gegenstimme? Sie können sehen, dass dies eine funktionierende Lösung ist, indem Sie das Beispiel kopieren und in ein Terminalfenster einfügen.
Thomas Bratt
Ich denke, du wurdest abgewählt, weil es ein bisschen gefährlich ist und die CPU verbraucht.
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
quelle
0

Mit zshund einer sleepImplementierung, die Gleitkomma-Argumente akzeptiert:

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

Oder für forever:

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

Wenn Ihr sleepFloats nicht unterstützt, können Sie es jederzeit als Wrapper um zshdas zselectBuilt-In neu definieren:

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

... Ich frage mich, wie viel komplizierte Lösungen bei der Lösung dieses Problems erstellt werden können.

Es kann so einfach sein ...

open /etc/crontab 

setze dort 1 nächste Zeile an das Ende der Datei wie:

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

Wenn Sie alle 1 Sekunde etwas ausführen möchten, geben Sie Folgendes ein:

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

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

und innerhalb dieser file.sh sollte sein:

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

your code or commands

und das ist alles!

GENIESSEN!

Mirra
quelle
6
Das ist nicht richtig. * / 60 wird alle 60 Minuten und * / 5 beispielsweise alle 5 Minuten ausgeführt.
mika
1
Sekunden sind auf crontab
GAD3R 30.11.17
Teste deine Sachen, bevor du mit falscher Scheiße antwortest.
So
-1
until ! sleep 60; do echo $(date); command; command; command; done

funktioniert bei mir.

  1. Ich brauche es nicht genau alle 60 Sekunden
  2. "watch" überwacht nicht alle Befehle auf allen Systemen
  3. "watch" kann nur einen Befehl (oder eine Skriptdatei) annehmen
  4. Wenn Sie den Schlaf in den Zustand anstatt in den Körper versetzen, ist die Schleife besser unterbrechbar (so dass eine Behauptung in einer Antwort auf eine ähnliche Frage zum Stackoverflow. Leider kann ich sie im Moment finden.)
  5. Ich möchte es einmal vor der Verzögerung ausführen, also benutze ich bis statt bis
Titus
quelle
Mir ist gerade aufgefallen, dass "till" anscheinend den Schlaf auch vor der ersten Iteration ausführt. Gibt es eine Möglichkeit, die Bedingung am Ende der Schleife in Bash zu setzen?
Titus