Wykonuj polecenie co X sekund

10

Chcę wykonać polecenie co 10 sekund i wykonać je w tle (eliminując w ten sposób watch?). Wszystkie odpowiedzi pokazują coś podobnego do poniższego, ale będzie to działać zawsze od 11 do 14 sekund. Jak można tego dokonać?

while true; do
    # perform command that takes between 1 and 4 seconds
    sleep 10
done
użytkownik1032531
źródło
może z sygnałami w czasie rzeczywistym. i prawidłowe obchodzenie się z sekundami przestępnymi. powodzenia.
mikeserv
@mikeserv Więc nie zawracaj sobie głowy próbowaniem? Nie dbam o milisekundową dokładność, po prostu powiedz pół sekundy.
user1032531,
Co próbujesz osiągnąć Uważam, że zadanie, które musi działać z taką częstotliwością, jest zwykle obejściem innego problemu.
Sobrique,
@Sobrique Dokładnie. Robię dowód koncepcji rejestratora danych z odpytywaniem. W rzeczywistości będzie programowany w innym języku, ale na razie to działa.
user1032531,

Odpowiedzi:

16

Co powiesz na:

( # 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: Z tymi wszystkimi komentarzami, alternatywami i podpowłoką dla dodatkowej ochrony, która wygląda o wiele bardziej skomplikowana niż się zaczęła. Dla porównania, oto jak to wyglądało, zanim zacząłem się martwić waitlub kill, wraz z ich $!potrzebą izolacji:

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

Reszta jest naprawdę tylko wtedy, gdy jej potrzebujesz.

Sidhekin
źródło
Czy potrafisz wyjaśnić powód zrzutu pamięci?
Ismael Miguel
2
Ponadto, aby zacytować, dla której powłoki działa twój kod (np. Odpowiedź mikeserv), aby ludzie nie byli zdezorientowani, gdy inna powłoka zachowuje się inaczej :)
ash
@ IsmaelMiguel Doszedłem do wniosku, że komentarze wyjaśnią to rozumowanie. Nie jestem pewien, co jest niejasne.
Sidhekin,
1
Podręcznik bash mówi o $!: „rozwija się do identyfikatora procesu zadania ostatnio umieszczonego w tle” i sleepnie jest umieszczany w tle, więc nie, nie będzie. :)
Sidhekin,
1
łał. źle w obu przypadkach. Kiedy to się stało?
mikeserv
9

Można zrobić coś jak następuje w bash, zshlub ksh:

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

Oto, co bashmówi instrukcja $SECONDS:

$SECONDS

  • Każdorazowe odwołanie do tego parametru oznacza liczbę sekund od wywołania powłoki. Jeśli wartość jest przypisana $SECONDS, wartością zwracaną przy kolejnych referencjach jest liczba sekund od przypisania plus przypisana wartość. Jeśli $SECONDStak unset, traci swoje specjalne właściwości, nawet jeśli zostanie później zresetowany.

Oto działający przykład:

(   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
źródło
2
Aby uniknąć resetowania, SECONDSwykonaj sleep "$((10-(($SECONDS-$start_time)%10)))". Musisz zrobić start_time=$SECONDSprzed pętlą.
ctrl-alt-delor
@richard - dlaczego tego unikać?
mikeserv
ponieważ pewnego dnia będziesz mieć go w skrypcie, który wywołujesz z taką pętlą. I będziesz spędzać cały dzień zastanawiając się, dlaczego to nie działa poprawnie. Krótko mówiąc, nie zagnieżdża się, jeśli zostanie zresetowany SECONDS. Odrodzenie podpowłoki jak w twojej ostatniej edycji, powinno również uniknąć tego problemu.
ctrl-alt-delor
1
@richard - ponieważ pytanie dotyczyło while truepętli, nie spodziewałem się, że jakikolwiek stan powłoki przetrwa. Tak czy inaczej, owijanie takiej pętli we własny kontekst jest zwykle najlepszą praktyką. W takiej sytuacji byłbym bardziej zaniepokojony robieniem tego dla samego trapsiebie niż dla samego $SECONDSsiebie.
mikeserv
Dzięki Mike, myślałem o zrobieniu czegoś podobnego (ale jeszcze nie wiedziałem, jak to zrobić). Ale rozwiązanie Sidhekina wydaje się robić to samo, nie? Nie mam uprawnień, aby oceniać lepszą odpowiedź, ale czułem się zobowiązany do wybrania czegoś.
user1032531,
3

Tylko BASH - Możesz również obliczyć czas spędzony przez twoje polecenie i odjąć od 10:

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

Od BASH człowieka:

TIMEFORMAT Wartość tego parametru służy jako ciąg formatu określający, w jaki sposób powinny być wyświetlane informacje o taktowaniu dla potoków poprzedzonych słowem zarezerwowanym czasowo. Znak% wprowadza sekwencję zmiany znaczenia, która jest rozwinięta do wartości czasu lub innej informacji. Sekwencje ucieczki i ich znaczenie są następujące; nawiasy klamrowe oznaczają części opcjonalne.
%% A dosłowny%.
% [p] [l] R Upływający czas w sekundach.
% [p] [l] U Liczba sekund procesora spędzonych w trybie użytkownika.
% [p] [l] S Liczba sekund procesora spędzonych w trybie systemowym.
% P Procent procesora obliczony jako (% U +% S) /% R.

Opcjonalne p jest cyfrą określającą dokładność, liczbę cyfr ułamkowych po przecinku. Wartość 0 powoduje, że nie są generowane żadne miejsca po przecinku.

Dani_l
źródło