Написание сценария оболочки для извлечения журналов в указанную временную метку

Ниже приведен сценарий. Когда я пытаюсь его выполнить, он говорит, что произошла ошибка при анализе даты.

[17:02:04:308][01-03-2024] <-- Это структура отметки времени в моем файле журнала. Ошибка в терминале:

./extract_logs.sh serverout.txt "[17:02:04:308][01-03-2024]" "[17:02:04:325][01-03-2024]"
date: invalid date ‘ 17:02:04:308  01-03-2024 ’
date: invalid date ‘ 17:02:04:325  01-03-2024 ’
date: invalid date ‘ 17:02:04:227  01-03-2024 ’
./extract_logs.sh: line 39: [: : integer expression expected
date: invalid date ‘ 17:02:04:303  01-03-2024 ’
./extract_logs.sh: line 39: [: : integer expression expected
date: invalid date ‘ 17:02:04:304  01-03-2024 ’
./extract_logs.sh: line 39: [: : integer expression expected
date: invalid date ‘ 17:02:04:308  01-03-2024 ’
./extract_logs.sh: line 39: [: : integer expression expected

Код:

#!/bin/bash

# Function to convert timestamp to seconds since epoch
convert_to_seconds() {
    local timestamp=$1
    date -d "$timestamp" +%s
}

# Check if correct number of arguments are passed
if [ "$#" -ne 3 ]; then
    echo "Usage: $0 <log_file_name> <start_timestamp> <end_timestamp>"
    echo "Timestamps should be in the format '[HH:MM:SS:SSS][DD-MM-YYYY]'"
    exit 1
fi

log_file=$1
start_timestamp=$2
end_timestamp=$3

# Convert start and end timestamps to seconds since epoch
start_seconds=$(convert_to_seconds "$start_timestamp")
end_seconds=$(convert_to_seconds "$end_timestamp")

# Check if log file exists
if [ ! -f "$log_file" ]; then
    echo "File not found: $log_file"
    exit 1
fi

# Read the log file line by line
while IFS= read -r line; do
    # Extract the timestamp from the log line
    if [[ $line =~ (\[[0-9]{2}:[0-9]{2}:[0-9]{2}:[0-9]{3})\]\[([0-9]{2}-[0-9]{2}-[0-9]{4})\] ]]; then
        log_time = "[${BASH_REMATCH[1]}][${BASH_REMATCH[2]}]"
        log_seconds=$(convert_to_seconds "$log_time")

        # Check if log time is within the specified range
        if [ "$log_seconds" -ge "$start_seconds" ] && [ "$log_seconds" -le "$end_seconds" ]; then
            echo "$line"
        fi
    fi
done < "$log_file"

Почему ваш формат даты вообще безумен?

tripleee 11.06.2024 14:31

не знаю, я новичок. это была задача, которую поручил мой наставник, и он поделился со мной этим файлом журнала.

Arav Stark 11.06.2024 15:23

В вашем примере передаются две временные метки из разных тысячных долей одной и той же секунды, но логика вашего скрипта преобразует все временные метки в секунды эпохи (что отбрасывает дроби). Это не сработает.

Paul Hodges 11.06.2024 22:01
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
3
56
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Простое объяснение заключается в том, что отметки даты в вашем безумном формате неприемлемы для date.

Обходной путь — преобразовать даты во что-то разумное; но поскольку это всего лишь числа, вам не нужно преобразовывать значение в секунды — просто выполните простое сравнение строк после изменения порядка полей.

#!/bin/bash
 
# Check if correct number of arguments are passed
if [ "$#" -ne 3 ]; then
    echo "Usage: $0 <log_file_name> <start_timestamp> <end_timestamp>"
    echo "Timestamps should be in the format '[HH:MM:SS:SSS][DD-MM-YYYY]'"
    exit 1
fi
 
log_file=$1
start_timestamp=$2
end_timestamp=$3
 
# Check if log file exists
if [ ! -f "$log_file" ]; then
    echo "File not found: $log_file"
    exit 1
fi
 
awk -v start = "$start_timestamp" -v end = "$end_timestamp" '
  function parsedate(date) {
        split(date, a, /[]:[-]+/)
        return a[8] "-" a[7] "-" a[6] "T" a[2] ":" a[3] ":" a[4] "." a[5]
  }
  BEGIN {
    st = parsedate(start)
    et = parsedate(end)
  }
  ((p = parsedate($0)) >= st) && (p <= et)' "$1"

Я переработал основную логику на Awk, поскольку она значительно более лаконична, чем эквивалентный код Bash.

Демо: https://ideone.com/GOgdoP

Кстати, обратите внимание, что существует несколько версий date. Судя по всему, вы используете Linux, который использует GNU date (который поддерживает, например, опцию -d для указания даты). Например, BSD/MacOS у вас есть разные опции (включая возможность указать, как именно преобразовать строку в каком-то дурацком формате в дату, но со множеством других странностей).

tripleee прав — у вас не очень хорошая структура временных меток.
Если вы можете это контролировать, исправьте это.

Если возможно, используйте YEAR-MO-DY HH::MM::SS, как в

$: date +'%F %T'
2024-06-11 08:02:06

Тем не менее, это всего лишь вопрос правильного анализа ваших данных.

$: [[ $timestamp =~ ^\[([[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2}):[[:digit:]]{3}\]\[([[:digit:]]{2})-([[:digit:]]{2})-([[:digit:]]{4})\] ]] && printf "%s\n" "${BASH_REMATCH[@]}" || echo nomatch
[17:02:04:308][01-03-2024]
17:02:04
01
03
2024

$: date -d "${BASH_REMATCH[4]}-${BASH_REMATCH[3]}-${BASH_REMATCH[2]} ${BASH_REMATCH[1]}" +%s                                                     1709334124

Итак, чтобы переписать существующую функцию:

$: convert_to_seconds() {
  if [[ $1 =~ ^\[([[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2}):[[:digit:]]{3}\]\[([[:digit:]]{2})-([[:digit:]]{2})-([[:digit:]]{4})\]$ ]]
  then date -d "${BASH_REMATCH[4]}-${BASH_REMATCH[3]}-${BASH_REMATCH[2]} ${BASH_REMATCH[1]}" +%s
  else echo "Invalid timestamp: '$1'" >&2
       return 1
  fi
}

$: convert_to_seconds $timestamp
1709334124

$: convert_to_seconds foo
Invalid timestamp: 'foo'

Немного прикрасился -

convert_to_seconds() {
  local timestamp = "$1" format
  local pattern=$'^\[([[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2}):[[:digit:]]{3}\]' # the time portion
        pattern+=$'\[([[:digit:]]{2})-([[:digit:]]{2})-([[:digit:]]{4})\]$'           # the date portion
   if [[ $1 =~ $pattern ]]
   then format = "${BASH_REMATCH[4]}-${BASH_REMATCH[3]}-${BASH_REMATCH[2]} ${BASH_REMATCH[1]}" # parsed
        date -d "${BASH_REMATCH[4]}-${BASH_REMATCH[3]}-${BASH_REMATCH[2]} ${BASH_REMATCH[1]}" +%s;
   else echo "Invalid timestamp: '$1'" >&2
   return 1
   fi
}

Трипли тоже прав, что awk будет для этого намного эффективнее.

Paul Hodges 11.06.2024 15:19

к сожалению, у меня нет полномочий изменять формат метки времени.

Arav Stark 11.06.2024 16:28

Другие вопросы по теме