Registro de data e hora preciso no Bash com printf (não é um problema trivial)

2

Shell: Bash.

Objetivo: obter o tempo t em milissegundos desde algum ponto fixo no tempo, adequado para o registro de data e hora do material com printf.

Condição: a solução deve passar no teste de tudo-em-uma-linha-de-texto.

Adicional: a solução deve ser atômica (sim, direita! ..), leve, manter quantização & arredondando as questões para um mínimo, blah ..

t=$[$(date +%s%N)/1000000] < --- minha solução, o ponto fixo sendo 1 de janeiro de 1970 neste caso. MAS fundamentalmente ruim devido às duas chamadas de data.

printf "t=%d\n" $[$(date +%s%N)/1000000] < --- aqui está, usando printf.

t=$(date +%s)$[10#$(date +%N)/1000000] < --- exemplo terrível. Mesmo parece precisar de de-pad de, em seguida, re-pad com zeros à esquerda.

printf "t=%d%03d\n" $(date +%s) $[10#$(date +%N)/1000000] < --- aqui está, usando printf.

Alguma sugestão melhor (sensata)?

EDITAR (anexar):

t=$(date +%s%N) e, em seguida, printf "%s\n" ${t::13} < --- eu acho, mas não uma linha.

    
por Anthony Webber 24.09.2017 / 18:40

3 respostas

4

Como observado por @Arrow, com date implementações que suportam %N como as de GNU ou ast-open, você pode usar %s%3N para limitar a precisão, mas exceto em ksh93 onde date pode ser feito para ser a versão embutida do date do ast-open, o comando date não está embutido. Levará algumas centenas, senão mil microsegundos, para começar e mais alguns para imprimir a data e retornar.

bash copiou um subconjunto de ksh93 printf '%(...)T' format, mas não a %N part.

Aqui, parece que você precisa usar shells mais avançados, como ksh93 ou zsh .

Esses shells podem fazer com que sua variável $SECONDS registre o tempo desde que o shell iniciou (e que você também pode redefinir para qualquer valor) ponto flutuante:

$ typeset -F SECONDS=0; date +%s%3N; echo $SECONDS
1506318780647
0.0017870000

Demorou até 1787 microssegundos para executar o% GNUdate aqui.

Você pode usar $((SECONDS*1000)) para obter um número de milissegundos, pois ambos os shells suportam aritmética de ponto flutuante (cuidado: ksh93 homenageia a marca decimal do local).

Para o tempo de época como um ponto flutuante, zsh tem $EPOCHREALTIME :

$ zmodload zsh/datetime
$ echo $EPOCHREALTIME
1506318947.2758708000

E ksh93 pode usar "$(printf '%(%s.%N)T' now)" (observe que a substituição de comando de ksh93 não separa processos nem usa pipes para builtins, portanto não é tão cara quanto em outras shells parecidas com Bourne).

Você também pode definir a variável $EPOCHREALTIME com:

$ EPOCHREALTIME.get() { .sh.value=$(printf "%(%s.%6N)T");
$ echo "$EPOCHREALTIME"
1506333341.962697

Para o registro de data e hora automático, você também pode usar set -o xtrace e $PS4 que imprime a hora atual. Em zsh :

$ zsh -c 'PS4="+%D{%s.%.}> "; set -x; sleep 1; date +%s.%N'
+1506332128.753> sleep 1
+1506332129.754> date +%s.%N
1506332129.755322928

Em ksh93:

$ ksh -c 'PS4="+\$(printf "%(%s.%3N)T")> "; set -x; sleep 1; date +%s.%N'
+1506332247.844> sleep 1
+1506332248.851> date +%s.%N
1506332248.853111699

Dependendo do seu caso de uso, você pode contar com moreutils do ts para a sua marcação de tempo:

$ (date +%s.%6N; date +%s.%6N) | ts %.s
1506319395.000080 1506319394.970619
1506319395.000141 1506319394.971972

( ts dá tempo para ler a linha da saída de date pelo pipe).

Ou para o tempo entre as linhas de saída:

$ (date +%s.%6N; date +%s.%6N) | ts -i %.s
0.000011 1506319496.806554
0.000071 1506319496.807907

Se você deseja ter o tempo necessário para executar um determinado comando (pipeline), também é possível usar a palavra-chave time , ajustando o formato com $TIMEFORMAT em bash :

$ TIMEFORMAT=%E; time date
Mon 25 Sep 09:51:41 BST 2017
0.002

Essas diretivas de formato de hora vêm inicialmente de csh (embora bash , ao contrário de zsh ou GNU time , suporte apenas um subconjunto minúsculo). Em (t) csh, você pode cronometrar cada comando definindo a variável $time special:

$ csh -xc 'set time = (0 %E); sleep 1; sleep 2'
set time = ( 0 %E )
sleep 1
0:01.00
sleep 2
0:02.00

(o primeiro número ( 0 here) diz que os comandos que levam mais que muitos segundos devem ser cronometrados, o segundo especifica o formato).

    
por 25.09.2017 / 08:09
2
  1. Primeiro, a ferramenta date não faz parte do bash, é uma ferramenta externa.
  2. O próprio Bash não pode fazer isso sem comandos externos.
  3. Fazer isso com comandos externos não pode ultrapassar seu critério de linha única.

Assim, você não pode fazer isso.

No entanto, se você permitir duas linhas ou escrever uma função para isso, poderá fazê-lo, como foi escrito nos comentários.

Extensão: você pode fazer isso com comandos mais recentes (> 4.2), mesmo como um comando de linha única, embora não seja a linha mais simples que você escreveu . Verifique a outra resposta.

    
por 24.09.2017 / 20:39
2

tl; dr

$ date +'%s%3N'
1506298414529

bash

Uma solução bash estrita (sem executáveis externos) é possível desde o bash 4.2:

$ printf '%(%s)T\n' "-1"
1506298414

$ printf '%(%Y%m%d-%H:%M:%S)T\n' "-1"
20170924-20:13:34

Mas isso não permite milissegundos nem nanossegundos (ainda).

data

Para obter milissegundos ou nanossegundos, é necessário usar a data do GNU como esta:

$ printf '%s\n' "$(date +'%Y%m%d-%H:%M:%S.%N')"
20170924-20:13:34.326113922

Ou

$ printf '%s\n' "$(date +'%s.%N')"
1506298414.529678016

O limite de 3 dígitos na parte fracionária dos segundos pode ser produzido com um formato %.3f para printf:

$ printf '%.3f\n' "$(date +'%s.%N')"
1506298414.529

Ou melhor, use a capacidade de reduzir o número de dígitos permitidos pelo formato de nanossegundos de data:

$ printf '%s\n' "$(date +'%s.%3N')"
1506298414.529

E então, o ponto pode ser removido:

$ printf '%s\n' "$(date +'%s%3N')"
1506298414529

Naturalmente, neste caso, a solução mais simples (sem printf, em vez do que foi perguntado exatamente) parece mais apropriada:

$ date +'%s%3N'
1506298414529
    
por 25.09.2017 / 02:46