Jak uzyskać milisekundy od epoki Uniksa?

30

Chcę zrobić skrypt bash, który mierzy czas uruchamiania przeglądarki, dlatego używam html, który pobiera znacznik czasu w czasie ładowania w milisekundach za pomocą JavaScript.

W skrypcie powłoki tuż przed wywołaniem przeglądarki otrzymuję znacznik czasu z:

date +%s

Problem polega na tym, że znacznik czasu pobiera się w sekundach, a ja potrzebuję go w milisekundach, ponieważ czasami, gdy uruchamiany jest drugi raz, przeglądarka uruchamia się w mniej niż sekundę i muszę być w stanie dokładnie zmierzyć ten czas za pomocą milisekund zamiast sekund.

Jak mogę uzyskać znacznik czasu w milisekundach ze skryptu bash?

Eduard Florinescu
źródło

Odpowiedzi:

28

date +%s.%Nda ci np 1364391019.877418748. % N to liczba nanosekund, które upłynęły w bieżącej sekundzie. Zauważ, że składa się z 9 cyfr i domyślnie data wstawi to z zerami, jeśli jest mniejsza niż 100000000. To jest właściwie problem, jeśli chcemy zrobić matematykę z liczbą, ponieważ bash traktuje liczby z wiodącym zerem jako liczbę ósemkową . Dopełnienie można wyłączyć za pomocą łącznika w specyfikacji pola, więc:

echo $((`date +%s`*1000+`date +%-N`/1000000))

dawałby ci naiwnie milisekundy od epoki.

Jednak , jak zauważa Stephane Chazelas w komentarzu poniżej, są to dwa różne datepołączenia, które dadzą dwa nieco inne czasy. Jeśli sekunda przejechała między nimi, obliczenia potrwają całą sekundę. Więc:

echo $(($(date +'%s * 1000 + %-N / 1000000')))

Lub zoptymalizowane (dzięki komentarzom poniżej, choć powinno to być oczywiste):

echo $(( $(date '+%s%N') / 1000000));
Złotowłosa
źródło
Próbowałem i działa, dziękuję + 1 / accept!
Eduard Florinescu
7
Zwróć uwagę, że między pierwszym a drugim terminem upłynęło kilka milionów nanosekund i może to nie być ta sama sekunda. Najlepiej zrobić $(($(date +'%s * 1000 + %N / 1000000'))). To nadal nie ma większego sensu, ale byłoby bardziej niezawodne.
Stéphane Chazelas
1
Naprawiono przez wyłączenie dopełniania zerowego, jak podano w dacie (1)
Alois Mahdal
3
%Nnie jest dostępny w BSD i OS X.
orkoden
1
Dlaczego nie tylko echo $(( $(date '+%s%N') / 1000000))?
Hitechcomputergeek
47

Z implementacją GNU lub ast-open date(lub busybox ', jeśli został zbudowany z FEATURE_DATE_NANOwłączoną opcją), użyłbym:

$ date +%s%3N

która zwraca milisekundy od epoki Uniksa.

javabeangrinder
źródło
5
Elegancki, miły, +1
Eduard Florinescu
1
Upvoting for elegance
sleepycal
Rzeczywiście elegancki, +1. Jeśli tak jak ja, chcesz mieć pewność %3N, że wypełnia niezbędne lewe zero (bez czytania dokumentacji. ;-), możesz to zrobić while sleep 0.05; do date +%s%3N; donei tak, niezbędne są zera.
Terry Brown
Dziękuję Terry! Jednak %3Nrzeczywiście jest wyściełany zerami. Co więcej, nie sądzę, aby twoje rozwiązanie dodawało zera, po prostu opóźniaj odpowiedź tak mało.
javabeangrinder
javabeangrinder, kod @ TerryBrown nie był rozwiązaniem , wystarczy przetestować kod, aby sprawdzić, czy %3Nrzeczywiście jest on wypełniony zerami.
Stéphane Chazelas
10

W przypadku, gdy ktoś używa innych niż muszle bash, ksh93i zshmają zmiennoprzecinkowe $SECONDSzmienną jeśli zrobić typeset -F SECONDS, które mogą być przydatne do pomiaru czasu z dokładnością:

$ typeset -F SECONDS=0
$ do-something
something done
$ echo "$SECONDS seconds have elapsed"
18.3994340000 seconds have elapsed

Od wersji 4.3.13 (2011) w module zshznajduje się $EPOCHREALTIMEspecjalna zmienna zmiennoprzecinkowa zsh/datetime:

$ zmodload zsh/datetime
$ echo $EPOCHREALTIME
1364401642.2725396156
$ printf '%d\n' $((EPOCHREALTIME*1000))
1364401755993

Zauważ, że pochodzi od dwóch liczb całkowitych (dla sekund i nanosekund) zwróconych przez clock_gettime(). W większości systemów jest to większa precyzja, niż doublemoże pomieścić pojedyncza liczba zmiennoprzecinkowa C , więc stracisz precyzję, gdy użyjesz jej w wyrażeniach arytmetycznych (z wyjątkiem dat w pierwszych miesiącach 1970 r.).

$ t=$EPOCHREALTIME
$ echo $t $((t))
1568473231.6078064442 1568473231.6078064

Aby obliczyć różnice czasowe o wysokiej precyzji (wątpię, byś potrzebował więcej niż milisekundowej precyzji), możesz $epochtimezamiast tego użyć specjalnej tablicy (która zawiera sekundy i nanosekundy jako dwa osobne elementy).

Od wersji 5.7 (2018) strftimewbudowana powłoka obsługuje także %Nformat nanosekundowy à la GNU datei a, %.aby określić precyzję, więc liczbę milisekund od epoki można również odzyskać za pomocą:

zmodload zsh/datetime
strftime %s%3. $epochtime

(lub przechowywane w zmiennej z -s var )

Stéphane Chazelas
źródło
Zauważ, że zmienna „$ SECONDS” nie jest połączona / powiązana z czasem EPOCH. Część ułamkowa (milisekundy, mikrosekundy i nanosekundy) może być całkiem inna w każdym z nich.
Izaak
8

Aby uniknąć obliczeń w celu uzyskania milisekund, interpretery JavaScript w wierszu poleceń mogą pomóc:

bash-4.2$ node -e 'console.log(new Date().getTime())' # node.js
1364391220596

bash-4.2$ js60 -e 'print(new Date().getTime())'       # SpiderMonkey
1364391220610

bash-4.2$ jsc -e 'print(new Date().getTime())'        # WebKit 
1364391220622

bash-4.2$ seed -e 'new Date().getTime()'              # GNOME object bridge
1364391220669

Pakiety zawierające tych tłumaczy na Ubuntu Eoan:

człowiek w pracy
źródło
2

W bash 5+ istnieje zmienna o ziarnistości mikrosekundowej: $EPOCHREALTIME .

Więc:

$ echo "$(( ${EPOCHREALTIME//.} / 1000 ))"
1568385422635

Drukuje czas od epoki (1/1/70) w milisekundach.

W przeciwnym razie musisz użyć daty. Albo data GNU:

echo "$(date +'%s%3N')"

Jedna z alternatyw wymienionych na https://serverfault.com/a/423642/465827

Lub brew install coreutils dla OS X

Lub, jeśli wszystko inne zawiedzie, skompiluj (gcc epochInµsec.c) ten program c (dostosuj w razie potrzeby):

#include <stdio.h>
#include <sys/time.h>
int main(void)
{
  struct timeval time_now;
    gettimeofday(&time_now,NULL);
    printf ("%ld secs, %ld usecs\n",time_now.tv_sec,time_now.tv_usec);

    return 0;
}

Pamiętaj, że wywołanie dowolnego programu zewnętrznego wymaga czasu na odczyt i załadowanie z dysku, uruchomienie, uruchomienie i wreszcie wydrukowanie wyniku. To zajmie kilka milisekund. Jest to minimalna precyzja, którą można osiągnąć za pomocą dowolnego narzędzia zewnętrznego (w tym daty).

Izaak
źródło