LINUX.ORG.RU

Вычитание чисел в строках, используя awk, perl или другой однострочник

 ,


2

1

Доброго времени суток!

Подскажите, пожалуйста, каким способом можно решить следующую задачу с помощью однострочника?

Задача в том, чтобы вычитать значение из первого столбца предыдущей стройки из первого столбца текущей строки и помещать его перед текущей строкой.

Есть файл, вида:

$ cat blabla 
13:06:27.578195 blablabla
13:06:27.578243 bla bla
13:06:27.578271 bla
13:06:27.578337 zzz
13:06:27.578372 zzz zzz zzz
13:06:27.578372 zzz zzz zzz
13:07:02.224100 aaa bbb ccc

Необходимо, используя любую команду(ы) в одну строку получить вывод вида:

0 13:06:27.578195 blablabla
0.000048 13:06:27.578243 bla bla
0.000028 13:06:27.578271 bla
0.000066 13:06:27.578337 zzz
0.000035 13:06:27.578372 zzz zzz zzz
34,645728 13:07:02.224100 aaa bbb ccc

Кажется, что это можно сделать с помощью awk, но у меня не получилось.

Спасибо за любую помощь!!

Двор мети, гуманитарий.

anonymous ()

Раздели строку из первого столбца на четыре числа. Третье число прибавь к первому, умноженному на 3600, второму, умноженному на 60, и четвёртому, разделённому на миллион. Так получишь время для текущей строки в секундах. Сохраняй время между строками в переменной. Так ты сможешь вычислять разницу.

i-rinat ★★★★★ ()
Ответ на: комментарий от kardjoe

Я не разобрался, как это сделать.

Пытался таким образом:

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

$ cat blabla|awk '{print $1}'|awk -F ":" '{print $3}'|awk 'BEGIN {
   a=0; b=0; for (i = 0; i < NR; ++i) {
      if (NR > 0) a=$0;b='NR-1'; print a-b 
   } 
}'

В целом я хотел в цикле пройти файл и вычитать из i значения значение i-1, где i был бы номером строки.

ameame2021 ()
Ответ на: комментарий от i-rinat

Да, дату можно считать от начала Unix-time, т.е. поменять формат времени, но с вычитанием строк в awk, в цикле у меня возникают трудности.

ameame2021 ()
Ответ на: комментарий от ameame2021

Значения переменных сохраняются между строками.

$ yes | head -5 | awk 'BEGIN{ a = 0 } { a += 2; print a }'
2
4
6
8
10
$ 
i-rinat ★★★★★ ()
Ответ на: комментарий от ameame2021

Блок с правилом BEGIN выполняется только один раз, перед началом обработки файла. В момент его выполнения awk ещё не видел ни одной строки, поэтому NR в этом блоке будет равно нулю, и цикл for не будет выполнен ни разу.

i-rinat ★★★★★ ()
$a = $b = @()
$c = @($null)

gc blabla | % {
    $x = $_ -split '(?<=^\S+)\s(?=.+$)'
    $a += $x[0]
    $b += $x[1]
}

1..($a.count-1) | % { $c += ([timespan] $a[$_] - [timespan] $a[$_-1]).TotalMilliseconds }
0..($a.count-1) | % { '{0,9} {1} {2}' -f $c[$_],$a[$_],$b[$_] }


          13:06:27.578195 blablabla
    0,048 13:06:27.578243 bla bla
    0,028 13:06:27.578271 bla
    0,066 13:06:27.578337 zzz
    0,035 13:06:27.578372 zzz zzz zzz
        0 13:06:27.578372 zzz zzz zzz
34645,728 13:07:02.224100 aaa bbb ccc
anonymous ()
Ответ на: комментарий от ameame2021

Придумал, как можно это реализовать, но на более простом примере:

$ cat blablabla2
10
20
34
45
59
156
183

Реализация:

$ n=`cat blablabla2|wc -l`;for (( i=1; i<$n; i++ )); do head -${i} blablabla2|tail -2|awk 'NR==1{s=$1;next}{s-=$1}END{print s,FS,$0}'|sed 's/-//';done
10   10
10   20
14   34
11   45
14   59
97   156

Для первой строки вычисление работает неверно, но в остальном работает успешно.

ameame2021 ()

а чего бы не заюзать qalculate, он/она со временем классно работает, так задача сводится к простому вычитанию значений.

zmc ()
$ cat blabla | perl -nle 'if($prev){$cur=ms();show(diff($cur,$prev));$prev=$cur}else{$prev=$_;print "0 $_"} sub ms{/^([^\s]+)/;$1}; sub diff{conv($_[0])-conv($_[1])} sub conv{$_[0]=~/(\d+):(\d+):(\d+)\.(\d*)/; $1*3600*$2*60+$3+($4?$4/1000000:0)} sub show{printf("%.12f $_\n",$_[0]/1000000)}'

0 13:06:27.578195 blablabla
0.000000000048 13:06:27.578243 bla bla
0.000000000028 13:06:27.578271 bla
0.000000000066 13:06:27.578337 zzz
0.000000000035 13:06:27.578372 zzz zzz zzz
0.000000000000 13:06:27.578372 zzz zzz zzz
2.807974645728 13:07:02.224100 aaa bbb ccc

pru-mike ★★ ()
Ответ на: комментарий от pru-mike

Здорово, только результат немного неверный (в последней строке).

Благодарю за помощь!

В итоге должно получится:

0 13:06:27.578195 blablabla
0.000048 13:06:27.578243 bla bla
0.000028 13:06:27.578271 bla
0.000066 13:06:27.578337 zzz
0.000035 13:06:27.578372 zzz zzz zzz
0.000000 13:06:27.578372 zzz zzz zzz
34.645728 13:07:02.224100 aaa bbb ccc
ameame2021 ()
Ответ на: комментарий от ameame2021

действительно, опечатался

$ cat blabla | perl -nle 'if($prev){$cur=ms();show(diff($cur,$prev));$prev=$cur}else{$prev=$_;print "0 $_"} sub ms{/^([^\s]+)/;$1}; sub diff{conv($_[0])-conv($_[1])} sub conv{$_[0]=~/(\d+):(\d+):(\d+)\.(\d*)/; $1*3600+$2*60+$3+($4?$4/1000000:0)} sub show{printf("%.6f $_\n",$_[0])}'
0 13:06:27.578195 blablabla
0.000048 13:06:27.578243 bla bla
0.000028 13:06:27.578271 bla
0.000066 13:06:27.578337 zzz
0.000035 13:06:27.578372 zzz zzz zzz
0.000000 13:06:27.578372 zzz zzz zzz
34.645728 13:07:02.224100 aaa bbb ccc
pru-mike ★★ ()
Ограничение на отправку комментариев: только для зарегистрированных пользователей