Если все числа целые, то с помощью GNU awk
, который поддерживает границы слов \<...\>
, вы можете сделать
gawk 'gsub(/\<0\>/, "0") <5' infile
Вот awk
решение:
awk -F'\\|~\\^' '{
if($1=="H"){
head=$0
}
else if($1=="T"){
foot=$1"|~^"$2
foot4=$4
}
else{
date=$3;
sub("T.*","", date);
data[date][NR]=$0;
sum[date]+=$4;
num[date]++
}
}
END{
for(date in data){
file=date".txt";
gsub("-","",file);
print head > file;
for(line in data[date]){
print data[date][line] > file
}
printf "%s|~^%.3f|~^%s|~^%.3f\n", foot, num[date],
foot4, sum[date] > file
}
}' file
При запуске с вашими примерами данных получается:
$ cat 20190305.txt
H|~^20200425|~^abcd|~^sum
R|~^abc|~^2019-03-05T12:33:52.27|~^105603.042|~^2018-10-23T12:33:52.27|~^aus
R|~^abc|~^2019-03-05T12:33:52.27|~^2054.026|~^2018-10-24T12:33:52.27|~^usa
R|~^abc|~^2019-03-05T12:33:52.27|~^30.00|~^2018-08-05T12:33:52.27|~^ddd
R|~^abc|~^2019-03-05T12:33:52.27|~^20.00|~^2018-07-23T12:33:52.27|~^audg
T|~^20200425|~^4.000|~^xxx|~^107707.068
$ cat 20190306.txt
H|~^20200425|~^abcd|~^sum
R|~^abc|~^2019-03-06T12:33:52.27|~^123562388.23456|~^2018-04-12T12:33:52.27|~^hhh
R|~^abc|~^2019-03-06T12:33:52.27|~^10.00|~^2018-09-11T12:33:52.27|~^virginia
R|~^abc|~^2019-03-06T12:33:52.27|~^15.03|~^2018-10-23T12:33:52.27|~^jjj
R|~^abc|~^2019-03-06T12:33:52.27|~^10.04|~^2018-04-08T12:33:52.27|~^jj
T|~^20200425|~^4.000|~^xxx|~^123562423.305
Кроме того, поскольку вы, кажется, думаете, что awk работает медленно, я проверил это на большом файле. Чтобы создать тестовый файл, я запустил это:
perl -e '@d=<>; print $d[0]; $str=join("",@d[1..$#d-1]); print $str x 3500000; print $d[$#d]' file > bigFile
Это создает файл размером 1,9G с 28000002 строками, где 1-я строка — это заголовок исходного файла, последняя строка — нижний колонтитул исходного файла, а строки между ними — 3,5 миллиона повторений содержимого исходного файла. Затем я запустил свой awk для этого файла (обратите внимание, что у меня достаточно оперативной памяти для этого, вам потребуется не менее 618 МБ свободной оперативной памяти):
$ time awk -F'\\|~\\^' '{ if($1=="H"){head=$0} else if($1=="T"){foot=$1"|~^"$2; foot4=$4;} else{date=$3; sub("T.*","", date);data[date][NR]=$0;sum[date]+=$4;num[date]++;} }END{for(date in data){file=date;gsub("-","",file); sub("T.*",".txt",file); print head > file; for(line in data[date]){print data[date][line] > file} printf "%s|~^%s|~^%s|~^%s\n", foot, num[date], foot4, sum[date] > file } }' bigFile
real 2m8.603s
user 2m0.610s
sys 0m6.795s
Итак, 2 минуты на обработку 1,9 ГБ данных в 28 000 002 строк. Это довольно быстро (, хотя решение Исаака было быстрее на 1 м 30 и использует меньше памяти, поэтому пользовательский интерфейс предлагает вам использовать его вместо этого ). Я могу абсолютно гарантировать, что вы никогда не получите это так быстро с помощью цикла оболочки for
. И, если уж на то пошло, с циклом R for.
С вашим последним редактированием вы изменили весь вопрос.
Теперь для каждой строки отметка времени должна быть преобразована в имя файла.
То есть с 2019-03-06T12:33:52.27
по 20190306
. Это само по себе требует некоторой обработки строк, которая никогда не бывает быстрой на любом языке.
Эта небольшая часть может быть выполнена в awk с помощью:
awk 'BEGIN{FS="\\|~\\^";OFS="|~^"}
$1=="R"{
t=gensub(/-/, "","g",$3)
s=gensub(/T.*/,"",1,t);
$3=s
}
1
' "file" >"file.adj"
Затем остается (ваш первоначальный вопрос )о разделении файла на основе даты даты метки времени. Список минимальных необходимых изменений::
4
. Весь процесс может быть выполнен в awk следующим образом:
awk 'BEGIN { FS="\\|~\\^"; OFS="|~^" }
$1=="H"{ header=$0; hdr=$2 }
$1=="R"{
t=gensub(/-/, "","g",$3)
file=gensub(/T.*/,"",1,t);
sum[file]+=$4
if(count[file]==0){ print header >file }
count[file]++
print $0 >>file
}
END {
for( i in sum ){
print "T",hdr,count[i],"xxx",sum[i] >> i;
close(i)
}
}
' "file"
Используя повторение Perl для 1 миллиона раз исходного файла, обработка всего файла занимает всего 49,32 секунды. Минимальное использование памяти (В памяти должны оставаться только суммы и подсчеты в день ). Мне кажется, это довольно быстро.