2014-02-01 11 views
1

Я написал следующий Баш скрипт для преобразования миллисекунды Дни: часы: минуты: секунды: миллисекунды, чтобы сделать лог-файл более читаемым:Bash скрипт для преобразования миллисекунды Дни: часы: минуты: секунды: миллисекунды

#!/bin/bash 
    ### Constants ### 
    CON_DAYS=.0000000115741 
    CON_HOURS=.000000277778 
    CON_MINUTES=.000066667 
    CON_SECONDS=.001 

    ### Variables ### 
    INPUT="$1" 

    cat $INPUT | awk -v CON_HOURS=$CON_HOURS -v CON_MINUTES=$CON_MINUTES -v CON_SECONDS=$CON_SECONDS -v CON_DAYS=$CON_DAYS ' 
     { $1=substr($0,0,10) } 
     { MILLISECONDS = $1 } 
     { DAYS = int(MILLISECONDS * CON_DAYS) } 
     { MILLISECONDS = MILLISECONDS - int(DAYS/CON_DAYS) } 
     { HOURS = int(MILLISECONDS * CON_HOURS) } 
     { MILLISECONDS = MILLISECONDS - int(HOURS/CON_HOURS) } 
     { MINUTES = int(MILLISECONDS * CON_MINUTES) } 
     { MILLISECONDS = MILLISECONDS - int(MINUTES/CON_MINUTES) } 
     { SECONDS = int(MILLISECONDS * CON_SECONDS) } 
     { MILLISECONDS = MILLISECONDS - int(SECONDS/CON_SECONDS) } 
     { $1 = DAYS":"HOURS":"MINUTES":"SECONDS":"MILLISECONDS"ms" } 
     {print}' 
    exit 

Раздел входного файла:

1882224617mS ATMChannel: [1] CMLinkLayer Rx: 'DialDigits' (ls) 
1882224617mS ATMIO:  [1] TONE DIAL (11 digits) 
1882224617mS ATMChannel: [1] StateChange Connected->ToneDialing 

Есть несколько строк вывода, который показывает, что он не работает правильно:

22:19:224:14:186ms ATMChannel: [1] CMLinkLayer Rx: 'DialDigits' (ls) 
22:19:224:14:186ms ATMIO: [1] TONE DIAL (11 digits) 
22:19:224:14:186ms ATMChannel: [1] StateChange Connected->ToneDialing 

После нескольких часов поиска неисправностей я не могу найти свою ошибку. Любая помощь приветствуется.

+0

Можете ли вы разместить образец ввода и ожидаемый результат? – BroSlow

+0

Образец ввода был добавлен в сообщение. – socialdtk

+0

Текст на неисправном выходе, похоже, не находится на входе. Можете ли вы отредактировать ввод образца, чтобы включить неисправные входы? Удачи. – shellter

ответ

4

Split в миллисекунду и второй, используя дату GNU, вы должны быть легче получить результат

#!/bin/bash 

INPUT="$1" 

while read t rest 
do 
    ms=$(echo $t|sed -r "s/.*(.....)$/\1/") # get Milliseconds 617mS 
    se=$(echo $t|sed -r "s/(.....)$//")  # get seconds 1882224 
    days=$(echo $se/3600/24 |bc) 

    d="$days:$(date -d "1970-01-01 $se seconds" +%H:%M:%S):$ms" 
    echo "$d $rest" 
done < $INPUT 
+1

Приятный и простой, хотя поле дней округляется для некоторых причина? – BroSlow

+0

Получил ваш момент, дата +1 день, чем ваш результат. если нам нужно рассчитать, сколько дней нам нужно минус 1. – BMW

+0

+1 Имеет смысл, последний день 1969 года, похоже, хорошо работает. – BroSlow

1

Не использовать или для этого (если вы не делали все это в сценарии ).

Try что-то вроде

#!/bin/bash 

INPUT="$1" 
while IFS= read -r line; do 
    [[ $line =~ ^[0-9]+ ]] && days=$(echo "${BASH_REMATCH[0]}/(1000 * 60 * 60 * 24)" | bc -l) 
    [[ $days =~ '.'[0-9]+ ]] && hours=$(echo "${BASH_REMATCH[0]} * 24" | bc -l) 
    [[ $hours =~ '.'[0-9]+ ]] && minutes=$(echo "${BASH_REMATCH[0]} * 60" | bc -l) 
    [[ $minutes =~ '.'[0-9]+ ]] && seconds=$(echo "${BASH_REMATCH[0]} * 60" | bc -l) 
    [[ $seconds =~ '.'[0-9]+ ]] && ms=$(echo "${BASH_REMATCH[0]} * 1000" | bc) 
    [[ $line =~ 'mS'(.*) ]] && line="${BASH_REMATCH[1]}" 
    printf "%d:%d:%d:%d:%d" "$days", "$hours", "$minutes", "$seconds", "$ms" 2>/dev/null 
    printf "$line\n" 
done < "$INPUT" 

Который производит

21:18:50:24:616 ATMChannel: [1] CMLinkLayer Rx: 'DialDigits' (ls) 
21:18:50:24:616 ATMIO:  [1] TONE DIAL (11 digits) 
21:18:50:24:616 ATMChannel: [1] StateChange Connected->ToneDialing 
+0

Большое вам спасибо за помощь! У меня есть 2 проблемы с предоставленным скриптом. Во-первых, вывод столбца 1 теперь «0: 0: 0: 0: 0» для каждой строки. Во-вторых, он намного медленнее, чем использование awk (хотя я могу жить с этим). – socialdtk

+0

@socialdtk На том же входе вы отправили? Странно, ты бежишь от баша? Хотя я все равно поеду с [решением BMW] (http://stackoverflow.com/a/21505686/3076724). Может быть, некоторые специальные символы на линии, вы можете попробовать заменить '^ [0-9] +' в первой строке '[0-9] + 'mS'' – BroSlow

2

Если выполнение скорость вызывает беспокойство, это приведет к тому же результату, что и принятый ответ, но он делает это в 33 раза быстрее на моей машине (0.9s против 30-х лет с файлом в 1000 линии):

#!/bin/bash 
sed 's/\([[:digit:]]\{3\}\)mS/ \1mS/' "$1" | while read sec msec info; do 
     for scale in 86400 3600 60 1; do 
       num_scale=$((sec/scale)) 
       sec=$((sec - (scale * num_scale))) 
       echo -n "$num_scale:" 
     done 
     echo "$msec $info" 
done 

Эффективности исходит из следующих:

  1. Разделительный секунд от миллисекунд производятся в одном sed процессе, который питает while петля.
  2. Разделение секунд, миллисекунд и текста журнала выполняется в одном read в строке.
  3. Внутри основного контура отсутствуют трубы или подпроцессы.
  4. выводя дни, часы и т.д., это просто делаются через простую математику, которые не должны быть пропущенными через date командования парсера, форматировщик и т.д.

Хотя это не помечены в вашем вопросе, perl по умолчанию установлен на большинстве UNIX и вполне подходит для таких задач, и даже с быстрым и грязным стилем это еще один шаг в скорости (0,05 с одним файлом, 18 раз быстрее, чем мой BASH выше, и 600x быстрее, чем принятый ответ):

#!/usr/bin/perl 
@scales = (86400, 3600, 60, 1); 
while (<>) { 
     ($sec, $msec, $info) = /^(\d+)(\d{3}mS)(.+)$/; 
     foreach $scale (@scales) { 
       $num_scale = sprintf("%0d", $sec/$scale); 
       $sec -= $scale * $num_scale; 
       print "$num_scale:"; 
     } 
     print "$msec$info\n"; 
} 

Проверка времени выполнения и ou tput:

$ for script in accepted.bash iscfrc.bash iscfrc.pl; do echo -n $script:; time ./$script log_1000.txt >$script.output; echo; done 
accepted.bash: 
real 0m30.257s 
user 0m4.040s 
sys  0m8.089s 

iscfrc.bash: 
real 0m0.881s 
user 0m0.672s 
sys  0m0.200s 

iscfrc.pl: 
real 0m0.052s 
user 0m0.040s 
sys  0m0.012s 

$ md5sum *.output 
e3a05f4f88a9d912f4ba92112dfbf709 accepted.bash.output 
e3a05f4f88a9d912f4ba92112dfbf709 iscfrc.bash.output 
e3a05f4f88a9d912f4ba92112dfbf709 iscfrc.pl.output 

$ tail -n3 *.output 
==> accepted.bash.output <== 
21:18:50:24:617mS ATMIO:  [1] TONE DIAL (11 digits) 
21:18:50:24:617mS ATMChannel: [1] StateChange Connected->ToneDialing 
21:18:50:24:617mS ATMChannel: [1] CMLinkLayer Rx: 'DialDigits' (ls) 

==> iscfrc.bash.output <== 
21:18:50:24:617mS ATMIO:  [1] TONE DIAL (11 digits) 
21:18:50:24:617mS ATMChannel: [1] StateChange Connected->ToneDialing 
21:18:50:24:617mS ATMChannel: [1] CMLinkLayer Rx: 'DialDigits' (ls) 

==> iscfrc.pl.output <== 
21:18:50:24:617mS ATMIO:  [1] TONE DIAL (11 digits) 
21:18:50:24:617mS ATMChannel: [1] StateChange Connected->ToneDialing 
21:18:50:24:617mS ATMChannel: [1] CMLinkLayer Rx: 'DialDigits' (ls) 
Смежные вопросы