Compare a cadeia de data e hora

1

Eu tenho um arquivo contém os seguintes dados (apenas dados de amostra são mostrados. o arquivo conterá no máximo linhas de 2001)

0001:3002:2018/07/16:12.34.31:ERR 
0002:3002:2018/07/16:12.34.44:ERR 
0003:3002:2018/07/16:12.34.57:ERR 
0004:3002:2018/07/16:12.35.10:ERR 
0005:3002:2018/07/16:12.35.23:ERR 
0006:3002:2018/07/16:12.35.36:ERR 
0007:3002:2018/07/16:12.35.49:ERR 
0008:3002:2018/07/16:12.36.02:ERR 
0009:3002:2018/07/16:12.36.15:ERR

Vou passar uma data em 2018/07/16: 12.36.15 para o script bash. Eu quero ler cada linha deste arquivo e comparar a data na linha com a data passada e retornar a linha cuja data é maior que a data passada.

O que eu fiz até agora ?

#!/bin/sh

SEARCH_DATE=$1
errorCodeFilePath=/home/.errorfile.log
lines='cat $errorCodeFilePath'
for line in $lines; do
   errorCodeDate=$(echo $line |grep -Eo '[[:digit:]]{4}/[[:digit:]]{2}/[[:digit:]]{2}:[[:digit:]]{2}.[[:digit:]]{2}.[[:digit:]]{2}');  
   if [ $errorCodeDate -ge $SEARCH_DATE ];
    then
        echo $errorCodeDate
    fi
done

Perguntas

  1. Não tenho certeza se a comparação de datas funcionará? Estou recebendo "expressão de erro inteiro esperada". Eu literalmente não tenho ideia de como escrever scripts Bash e esta é minha primeira tentativa.

  2. Como funciona essa comparação de datas? Além disso, após o trabalho de comparação de datas, preciso obter os dígitos entre primeiro: e o segundo: para todas as linhas correspondentes.

por Sharon 16.07.2018 / 15:50

3 respostas

2

Seu script lê o arquivo inteiro em uma variável e depois repete o valor dessa variável. Isso tem três problemas:

  1. No caso mais geral, pode-se desconhecer o tamanho do arquivo de entrada, o que significa que, em algumas circunstâncias, a variável pode se tornar muito grande.
  2. Repetir o valor sem aspas da variável dependerá do shell dividir os dados em espaços em branco (espaços, tabulações e novas linhas). Se os dados contiverem espaços em branco separados das novas linhas, o loop provavelmente fará a coisa errada.
  3. O shell executará a globalização do nome de arquivo nos valores da variável sem aspas antes de fazer o loop sobre ela. Isso significa que, se os dados contiverem padrões de globbing, como * ou [...] , eles serão correspondidos aos nomes de arquivos existentes.

Esta resposta usa o fato de que os timestamps usados são são no sentido de que, mais tarde, eles datilografam depois dos anteriores (pelo menos no local POSIX).

#!/bin/bash

while IFS= read -r line; do
    timestamp=${line%:*}            # Remove ":ERR" at the end
    timestamp=${timestamp#*:*:}     # Remove numbers from start ("0001:3002:")
    if [[ "$timestamp" > "$1" ]]; then
        # According to the current locale, the timestamp in "$timestamp"
        # sorts after the timestamp in "$1".
        printf "Greater: %s\n" "$line"
    fi
done <file

Este script receberá um timestamp no mesmo formato que está no arquivo como seu único argumento. Ele itera sobre o conteúdo do arquivo file e, para cada linha, analisa o registro de data e hora e o compara com o registro de data e hora na linha de comando. A comparação é feita usando o operador > em bash e será true se o registro de data e hora no arquivo for classificado (lexicograficamente) após o registro de data e hora determinado na localidade atual. Se a comparação for verdadeira, a linha do arquivo é impressa.

As duas substituições separadas para analisar o timestamp da linha, excluindo partes do final e início da linha, podem ser substituídas por

timestamp=$( cut -d ':' -f 3,4 <<<"$line" )

mas isso seria mais lento quando estiver chamando um utilitário externo.

Teste:

$ bash script.sh '2018/07/16:12.36.00'
Greater: 0008:3002:2018/07/16:12.36.02:ERR
Greater: 0009:3002:2018/07/16:12.36.15:ERR

Se você deseja produzir apenas o registro de data e hora do arquivo em vez da linha original, altere "$line" para "$timestamp" no comando printf .

Nesse caso, você também pode acelerar as coisas fazendo o loop assim:

#!/bin/bash

cut -d ':' -f 3,4 file |
while IFS= read -r timestamp; do
    if [[ "$timestamp" > "$1" ]]; then
        # According to the current locale, the timestamp in "$timestamp"
        # sorts after the timestamp in "$1".
        printf "Greater: %s\n" "$timestamp"
    fi
done

Aqui, usamos cut para obter as colunas 3rd e 4th : -delimited do arquivo (o timestamp), o que significa que não precisamos fazer nenhuma análise das linhas originais.

Relacionados:

por 16.07.2018 / 19:08
2

Sua idéia está correta, mas você pode consertar algumas coisas para fazer o script funcionar conforme o esperado.

  1. Primeiramente, usando cat para um arquivo e armazenando em uma variável, o loop é um anti-padrão na melhor das hipóteses. A abordagem iria quebrar as cordas pelo espaço em branco. Use o redirecionamento de arquivos com um loop while.
  2. Sempre cite as variáveis do shell para preservar o conteúdo da variável e impedir que elas passem pela divisão de palavras, conforme mencionado no ponto anterior
  3. Em vez de grep , use o suporte de regex nativo de bash para extrair a string de data da conversão EPOCH
  4. Por padrão bash não fornece uma maneira de comparar date strings, você precisa converter em valores EPOCH equivalentes e fazer uma comparação inteira

Então, colocando isso junto, sem usar ferramentas de terceiros e com apenas internals shell. Precisa do comando date dos utilitários GNU para usar o -d flag e pode não funcionar nas máquinas nativas date from * BSD.

#!/usr/bin/env bash   

errorCodeFilePath="/home/.errorfile.log"

re='[0-9]+/[0-9]+/[0-9]+:[0-9]+\.[0-9]+\.[0-9]+'

convDateString() {
    day="${1##*:}"
    time="${1%%:*}"
    printf '%d' "$(date -d"$time ${day//./:}" +%s)"
}

while IFS= read -r line; do
    inputArg="$1"
    inputEPOCH="$(convDateString "${inputArg}")"
    if [[ $line =~ $re ]]; then
        lineEPOCH="$(convDateString "${BASH_REMATCH[*]}")"
        if [ "$lineEPOCH" -gt "$inputEPOCH" ]; then
            echo "${BASH_REMATCH[@]}" is greater
        fi
    fi
done<"$errorCodeFilePath"

Teste seu arquivo em uma entrada de amostra em questão como

$ bash script.sh "2018/07/16:12.36.00"
2018/07/16:12.36.02 is greater
2018/07/16:12.36.15 is greater

Com tudo dito, você deve considerar a leitura Por que usar um loop de shell para processar texto é considerado uma prática ruim? . Porque o processamento de texto com shell é lento em comparação com outras ferramentas que são destinadas ao processamento de arquivos dedicados.

    
por 16.07.2018 / 16:52
0

Tente isso,

#!/bin/sh

SEARCH_DATE="$1"
errorCodeFilePath=/home/nagios/temp/test1
lines='cat $errorCodeFilePath'
for line in $lines; do
   errorCodeDate=$(echo $line |grep -Eo '[[:digit:]]{4}/[[:digit:]]{2}/[[:digit:]]{2}:[[:digit:]]{2}.[[:digit:]]{2}.[[:digit:]]{2}');
if [ $(date -d "'echo $errorCodeDate| tr ':' ' '| tr '.' ':''" +%s) -ge $(date -d "'echo $SEARCH_DATE| tr ':' ' '| tr '.' ':''" +%s) ];
    then
        echo $errorCodeDate
    fi
done
    
por 16.07.2018 / 16:36

Tags