Führen Sie den Befehl alle X Sekunden aus

10

Ich möchte einen Befehl alle 10 Sekunden ausführen und ihn im Hintergrund ausführen lassen (wodurch er eliminiert wird watch?). Alle Antworten zeigen ungefähr Folgendes: Dies wird jedoch immer 11 bis 14 Sekunden lang ausgeführt. Wie kann dies erreicht werden?

while true; do
    # perform command that takes between 1 and 4 seconds
    sleep 10
done
user1032531
quelle
vielleicht mit Echtzeitsignalen. und ordnungsgemäße Handhabung von Schaltsekunden. Viel Glück.
Mikesserv
@mikeserv Also, versuch es nicht? Die Genauigkeit von Millisekunden ist mir egal, sagen Sie einfach eine halbe Sekunde.
user1032531
Was versuchst du zu erreichen? Ich finde, dass eine Aufgabe, die mit dieser Häufigkeit ausgeführt werden muss, normalerweise eine Problemumgehung für ein anderes Problem darstellt.
Sobrique
@Sobrique Genau. Ich mache einen Proof of Concept eines Polling-Datenloggers. In Wirklichkeit wird es in einer anderen Sprache programmiert, aber das funktioniert vorerst.
user1032531

Antworten:

16

Wie wäre es mit:

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

ETA: Mit all diesen Kommentaren, Alternativen und der Unterschale für zusätzlichen Schutz sieht das viel komplizierter aus als ursprünglich. Zum Vergleich: So sah es aus, bevor ich anfing, mir Sorgen zu machen, waitoder killmit ihrem $!und dem Bedürfnis nach Isolation:

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

Der Rest ist wirklich nur für den Fall, dass Sie ihn brauchen.

Der Sidhekin
quelle
Können Sie die Gründe für Ihren Code-Dump erklären?
Ismael Miguel
2
Um zu zitieren, für welche Shell Ihr Code funktioniert (wie die Antwort von mikeserv), damit die Leute nicht verwirrt werden, wenn sich eine andere Shell anders verhält :)
ash
@IsmaelMiguel Ich dachte, die Kommentare würden die Argumentation erklären. Ich bin mir nicht sicher, was unklar ist.
Die Sidhekin
1
Das Bash-Handbuch lautet $!: "Erweitert sich auf die Prozess-ID des Jobs, der zuletzt in den Hintergrund gestellt wurde", und das sleepwird nicht in den Hintergrund gestellt, also nein, wird es nicht. :)
Die Sidhekin
1
Beeindruckend. in beiden Punkten falsch. wann ist das passiert?
Mikeserv
9

Sie können folgende in so etwas wie das tun bash, zshoder ksh:

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

bashFolgendes sagt das Handbuch $SECONDS:

$SECONDS

  • Jedes Mal, wenn auf diesen Parameter verwiesen wird, wird die Anzahl der Sekunden seit dem Zurückrufen des Shell-Aufrufs angegeben. Wenn ein Wert zugewiesen wird, $SECONDSgibt der Wert, der bei nachfolgenden Referenzen zurückgegeben wird, die Anzahl der Sekunden seit der Zuweisung plus den zugewiesenen Wert an. Wenn dies der Fall $SECONDSist unset, verliert es seine besonderen Eigenschaften, auch wenn es anschließend zurückgesetzt wird.

Hier ist ein Arbeitsbeispiel:

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

10
20
30
40
50
60
70
80
90
100
mikeserv
quelle
2
Um ein Zurücksetzen zu vermeiden, SECONDStun Sie dies sleep "$((10-(($SECONDS-$start_time)%10)))". Sie müssen start_time=$SECONDSvor der Schleife tun .
Strg-Alt-Delor
@richard - warum vermeiden?
Mikesserv
denn eines Tages werden Sie es in einem Skript haben, das Sie in einer solchen Schleife aufrufen. Und Sie werden sich den ganzen Tag fragen, warum es nicht richtig funktioniert. Kurz gesagt, es wird nicht verschachtelt, wenn Sie zurücksetzen SECONDS. Das Laichen einer Sub-Shell wie in Ihrer letzten Bearbeitung sollte dieses Problem ebenfalls vermeiden.
Strg-Alt-Delor
1
@richard - da es sich um eine while trueSchleife handelte, habe ich nicht angenommen, dass ein Shell-Zustand diese überleben würde. Das Wickeln einer solchen Schleife in einen eigenen Kontext ist normalerweise sowieso die beste Vorgehensweise. In einer solchen Situation wäre ich jedoch mehr besorgt darüber trapals um der $SECONDSSache willen.
Mikesserv
Danke Mike, ich habe überlegt, etwas Ähnliches zu machen (wusste aber noch nicht genau, wie). Aber Sidhekins Lösung scheint dasselbe zu tun, nein? Ich bin nicht qualifiziert, über die bessere Antwort zu urteilen, fühlte mich aber verpflichtet, etwas auszuwählen.
user1032531
3

Nur BASH - Sie können auch die von Ihrem Befehl aufgewendete Zeit berechnen und von 10 subtrahieren:

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

Von BASH Mann:

TIMEFORMAT Der Wert dieses Parameters wird als Formatzeichenfolge verwendet, die angibt, wie die Timing-Informationen für Pipelines angezeigt werden sollen, denen das zeitreservierte Wort vorangestellt ist. Das Zeichen% führt eine Escape-Sequenz ein, die auf einen Zeitwert oder andere Informationen erweitert wird. Die Escape-Sequenzen und ihre Bedeutungen sind wie folgt; Die Klammern kennzeichnen optionale Teile.
%% Ein Literal%.
% [p] [l] R Die verstrichene Zeit in Sekunden.
% [p] [l] U Die Anzahl der im Benutzermodus verbrachten CPU-Sekunden.
% [p] [l] S Die Anzahl der im Systemmodus verbrachten CPU-Sekunden.
% P Der CPU-Prozentsatz, berechnet als (% U +% S) /% R.

Das optionale p ist eine Ziffer, die die Genauigkeit und die Anzahl der Bruchstellen nach einem Dezimalpunkt angibt. Bei einem Wert von 0 wird kein Dezimalpunkt oder Bruch ausgegeben.

Dani_l
quelle