Korzystam z tej pętli, aby sprawdzać i drukować niektóre rzeczy co sekundę. Ponieważ jednak obliczenia mogą potrwać kilkaset milisekund, wydrukowany czas czasami przeskakuje o sekundę.
Czy jest jakiś sposób na napisanie takiej pętli, że mam gwarancję otrzymania wydruku co sekundę? (Oczywiście pod warunkiem, że obliczenia w pętli zajmą mniej niż sekundę :))
while true; do
TIME=$(date +%H:%M:%S)
# some calculations which take a few hundred milliseconds
FOO=...
BAR=...
printf '%s %s %s\n' $TIME $FOO $BAR
sleep 1
done
bash
timestamps
sleep
naprzód
źródło
źródło
sched(7)
interfejs API (POSIX: patrz<sched.h>
i strony z niego połączone), zasadniczo nie możesz mieć gwarancji tego formularza w czasie rzeczywistym.Odpowiedzi:
Aby pozostać trochę bliżej oryginalnego kodu, robię to:
To trochę zmienia semantykę: jeśli twoje rzeczy zajęły mniej niż sekundę, po prostu poczeka na upłynięcie pełnej sekundy. Jednakże, jeśli twój materiał z jakiegoś powodu trwa dłużej niż sekundę, nie będzie generował jeszcze więcej podprocesów, bez końca.
Twoje rzeczy nigdy nie działają równolegle, a nie w tle, więc zmienne działają również zgodnie z oczekiwaniami.
Pamiętaj, że jeśli rozpoczniesz dodatkowe zadania w tle, będziesz musiał zmienić
wait
instrukcję, aby czekać tylko nasleep
konkretny proces.Jeśli potrzebujesz, aby był jeszcze dokładniejszy, prawdopodobnie będziesz musiał po prostu zsynchronizować go z zegarem systemowym i uśpieniem ms zamiast pełnych sekund.
Jak zsynchronizować z zegarem systemowym? Naprawdę nie mam pojęcia, głupia próba:
Domyślna:
Wyjście: 003511461 010510925 016081282 021643477 028504349 03 ... (ciągle rośnie)
Zsynchronizowany:
Wyjście: 002648691 001098397 002514348 001293023 001679137 00 ... (pozostaje taki sam)
źródło
sleep
obsługują ułamkowe sekundy?sleep 0.9 || sleep 1
niepoprawny parametr, jest właściwie jedynym powodem, dla którego sen nigdy się nie kończy.sleep 0.9
interpretowany jakosleep 0
naiwne implementacje (biorąc pod uwagę, że takatoi
by się stało ). Nie jestem pewien, czy to rzeczywiście spowodowałoby błąd.gdate
w systemie macOS, abydate +%N
działało).Jeśli możesz zrestrukturyzować swoją pętlę do skryptu / onelinera, najprostszym sposobem na to jest
watch
i jejprecise
opcja.Możesz zobaczyć efekt za pomocą
watch -n 1 sleep 0.5
- pokaże zliczanie sekund, ale czasami przeskakuje przez sekundę. Uruchomienie go tak, jakwatch -n 1 -p sleep 0.5
będzie generowane dwa razy na sekundę, co sekundę, i nie zobaczysz żadnych pominięć.źródło
Uruchamianie operacji w podpowłoce, która działa jako zadanie w tle, sprawiłoby, że nie kolidowałyby tak bardzo z
sleep
.Jedynym momentem, gdy „skradziono” z jednej sekundy, byłby czas potrzebny na uruchomienie podpowłoki, więc w końcu pominie sekundę, ale miejmy nadzieję, że rzadziej niż oryginalny kod.
Jeśli kod w podpowłoce zajmie więcej niż sekundę, pętla zacznie gromadzić zadania w tle i ostatecznie zabraknie zasobów.
źródło
Inną alternatywą (jeśli nie możesz użyć, np.
watch -p
Jak sugeruje Maelstrom), jestsleepenh
[ manpage ], który jest do tego przeznaczony.Przykład:
Zwróć uwagę na
sleep 0.2
to, że tam symulujesz wykonywanie czasochłonnego zadania, jedząc około 200 ms. Mimo to, wyjście nanosekund pozostaje stabilne (cóż, według nieoperacyjnych standardów systemu operacyjnego) - dzieje się to raz na sekundę:To mniej niż 1 ms inaczej i bez trendu. To całkiem dobrze; powinieneś spodziewać się odbić co najmniej 10 ms, jeśli w systemie jest jakieś obciążenie - ale nadal nie ma dryfu w czasie. Tj. Nie stracisz ani sekundy.
źródło
Z
zsh
:Jeśli sen nie obsługuje zmiennoprzecinkowych sekund, można użyć
zsh
„szselect
zamiast (pozmodload zsh/zselect
):Nie powinny one dryfować, dopóki wykonywanie poleceń w pętli zajmuje mniej niż sekundę.
źródło
Miałem dokładnie takie same wymagania dla skryptu powłoki POSIX, w którym wszyscy pomocnicy (usleep, GNUsleep, sleepenh, ...) nie są dostępni.
patrz: https://stackoverflow.com/a/54494216
źródło