¿Cómo obtener milisegundos desde la época de Unix?

30

Quiero hacer un script bash que mida el tiempo de inicio de un navegador para el que estoy usando un html que obtiene la marca de tiempo en carga en milisegundos usando JavaScript.

En el script de shell justo antes de llamar al navegador, aparece la marca de tiempo con:

date +%s

El problema es que obtiene la marca de tiempo en segundos, y la necesito en milisegundos, ya que a veces cuando se ejecuta por segunda vez, el navegador se inicia en menos de un segundo y necesito poder medir ese tiempo con precisión usando milisegundos en lugar de segundos.

¿Cómo puedo obtener la marca de tiempo en milisegundos de un script bash?

Eduard Florinescu
fuente

Respuestas:

28

date +%s.%Nle dará, por ejemplo., 1364391019.877418748. El% N es el número de nanosegundos transcurridos en el segundo actual. Tenga en cuenta que tiene 9 dígitos y, por defecto, la fecha rellenará esto con ceros si es inferior a 100000000. Esto es realmente un problema si queremos hacer cálculos matemáticos con el número, porque bash trata los números con un cero inicial como octal . Este relleno se puede deshabilitar mediante el uso de un guión en la especificación del campo, por lo que:

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

ingenuamente te daría milisegundos desde la época.

Sin embargo , como Stephane Chazelas señala en el comentario a continuación, son dos datellamadas diferentes que producirán dos tiempos ligeramente diferentes. Si el segundo se ha desplazado entre ellos, el cálculo será un segundo completo. Asi que:

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

O optimizado (gracias a los comentarios a continuación, aunque esto debería haber sido obvio):

echo $(( $(date '+%s%N') / 1000000));
encerrada dorada
fuente
Lo probé y funciona, gracias + 1 / ¡acepto!
Eduard Florinescu
77
Tenga en cuenta que entre el momento en que ejecuta la primera fecha y la segunda fecha, pueden haber pasado varios millones de nanosegundos y puede que ni siquiera sea el mismo segundo. Lo mejor para hacer $(($(date +'%s * 1000 + %N / 1000000'))). Eso todavía no tiene mucho sentido, pero sería más confiable.
Stéphane Chazelas
1
Se solucionó desactivando el relleno cero como se indica en la fecha (1)
Alois Mahdal
3
%Nno está disponible en BSD y OS X.
orkoden
1
¿Por qué no solo echo $(( $(date '+%s%N') / 1000000))?
Hitechcomputergeek
47

Con la implementación de GNU o ast-open de date(o busybox 'si está construida con la FEATURE_DATE_NANOopción habilitada), usaría:

$ date +%s%3N

que devuelve milisegundos desde la época de Unix.

javabeangrinder
fuente
55
Elegante, agradable, +1
Eduard Florinescu
1
Votación por la elegancia
sleepycal
De hecho elegante, +1. Si, como yo, quiere estar seguro de que %3Nhace el relleno de cero a la izquierda necesario (sin leer los documentos. ;-), puede hacerlo while sleep 0.05; do date +%s%3N; done, y sí, los ceros necesarios están ahí.
Terry Brown
Gracias terry Sin embargo, de %3Nhecho es cero acolchado. Además, no creo que su solución agregue ceros, solo demore la respuesta muy poco.
javabeangrinder
javabeangrinder, el código de @ TerryBrown no fue una solución , solo pruebe el código para verificar que en %3Nrealidad esté rellenado con 0.
Stéphane Chazelas
10

En caso de que alguien esté usando otros shells que no sean bash, ksh93y zshtenga una $SECONDSvariable de coma flotante si hace algo typeset -F SECONDSque puede ser útil para medir el tiempo con precisión:

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

Desde la versión 4.3.13 (2011) zshtiene una $EPOCHREALTIMEvariable especial de coma flotante en el zsh/datetimemódulo:

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

Tenga en cuenta que eso se deriva de los dos enteros (por segundos y nanosegundos) devueltos por clock_gettime(). En la mayoría de los sistemas, eso es más precisión de la que doublepuede contener un solo número de coma flotante en C , por lo que perderá precisión cuando lo use en expresiones aritméticas (excepto las fechas en los primeros meses de 1970).

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

Para calcular las diferencias de tiempo de alta precisión (aunque dudo que necesite más que una precisión de milisegundos), es posible que desee utilizar la $epochtimematriz especial en su lugar (que contiene los segundos y nanosegundos como dos elementos separados).

Desde la versión 5.7 (2018), el strftimeshell incorporado también admite un %Nformato de nanosegundos a la GNU datey a %.para especificar la precisión, por lo que el número de milisegundos desde la época también se puede recuperar con:

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

(o almacenado en una variable con -s var)

Stéphane Chazelas
fuente
Tenga en cuenta que la variable "$ SEGUNDOS" no está conectada / relacionada con el tiempo EPOCH. La parte fraccionaria (milisegundos, microsegundos y nanosegundos) puede ser bastante diferente en cada uno.
Isaac
8

Para evitar cálculos para obtener los milisegundos, los intérpretes de JavaScript de la línea de comandos pueden ayudar:

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

Paquetes que contienen esos intérpretes en Ubuntu Eoan:

hombre trabajando
fuente
2

En bash 5+ hay una variable con la granularidad micro-segundos: $EPOCHREALTIME.

Asi que:

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

Imprime el tiempo desde la época (1/1/70) en milisegundos.

De lo contrario, debe usar la fecha. Ya sea la fecha GNU:

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

Una de las alternativas enumeradas en https://serverfault.com/a/423642/465827

O brew install coreutilspara OS X

O, si todo lo demás falla, compile (gcc epochInµsec.c) este programa c (ajuste según sea necesario):

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

Tenga en cuenta que llamar a cualquier programa externo necesita tiempo para leerse y cargarse desde el disco, iniciarse, ejecutarse y finalmente imprimir el resultado. Eso tomará varios milisegundos. Esa es la precisión mínima que se puede lograr con cualquier herramienta externa (incluida la fecha).

Isaac
fuente