如何在 linux 中加速 awk 命令?
How to speed up awk command in linux?
我在 linux 中使用 awk 命令将 utc 转换为本地时间,但是文件很大 (>30 gb) 并且需要一个多小时。
这是我的代码:
awk -F"," '{cmd="date -d \"$(date -d \"""\")-4hours\" \"+%Y%m%d_%H\"";cmd | getline datum; close(cmd); print [=11=] ","datum""}' data.txt
我怎样才能加快这个命令的速度,或者有什么简单的方法可以在 linux 中进行这种转换?
Here is the sample of input:
utc,id
2018-03-31 16:00:49.425,4485
2018-04-1 17:01:19.425,30019
2018-05-31 18:01:49.425,15427
2018-08-20 19:01:55.425,17579
2018-09-2 20:02:31.425,23716
2018-10-15 21:03:34.425,24772
expected output:
utc,id,localtime
2018-03-31 16:00:49.425,4485,20180331_12
2018-04-1 17:01:19.425,30019,20180401_13
2018-05-31 18:01:49.425,15427,20180531_14
2018-08-20 19:01:55.425,17579,20180820_15
2018-09-2 20:02:31.425,23716,20180902_16
2018-10-15 21:03:34.425,24772,20181015_17
你原来的解决方案之所以慢是因为对date
的系统调用。您使用 awk 处理的每个 record/line 调用外部命令来执行 date-conversion。这样的外部调用需要加载到内存中执行,其输出需要由 awk 处理。如果我们可以在 awk 本身中执行实际的 date-conversion,我们可以加快速度。
一般评论:当您将日期和时间从 UTC 转换为您当地的时区时,您必须考虑到 1 月 1 日与 8 月 1 日处于不同的时区。这由于 daylight-saving 时间。下面的算法没有提供解决方案,因为 OP 只是请求移动 4h,或者移动到他当前的 time-zone。 (备注: gawk 4.1.2 或更高版本的解决方案将考虑 DST)
下面我介绍了几种解决方案,您可以根据使用的 awk 使用这些解决方案:
Gnu awk: gawk
的各种扩展之一是 Time-functions。两个对这个问题有用的 time-functions 是 mktime
和 strftime
:
mktime(datespec,[utc-flag])
: This converts a date specification string, datespec
, of the form YYYY MM DD hh mm ss
into a Unix epoch time, i.e. the total seconds since 1970 01 01 UTC. Since gawk-4.2.1 you can use the utc-flag
to indicate datespec
is in UTC or not. Prior to gawk-4.2.1 it assumed the local time-zone.
strftime(format,timestamp,[utc-flag])
: This converts an epoch-time timestamp
into a formatted string (same formatting as the date
command). You can use the utc-flag
the indicate that the returned time should be in UTC or in the local time-zone.
More info in the GNU awk manual
我们要将字段 1 从 UTC 转换为本地 time-zone。由于我们不知道字段 1 的格式,我们假设存在一个函数 convert_time(str)
,它将 str
格式化为 mktime
可以接受的格式 YYYY MM DD hh mm ss
:
gawk 4.1.2 或更新版本:
$ awk 'BEGIN{FS=OFS=","}
{ # convert into YYYY MM DD hh mm ss
datestring=convert_time()
# convert datestring (as UTC) into epoch
datum=mktime(datestring,1)
# convert epoch into string (local TZ)
datum=strftime("2%Y%m%d_%H2",datum)
# print and append
print [=10=],datum
}' data.txt
在 gawk 4.1.2 之前: 这里我们不能使用 utc-flag
,所以我们强制 awk 在 UTC 中工作:
$ TZ=UTC awk 'BEGIN{FS=OFS=","}
{ # convert into YYYY MM DD hh mm ss
datestring=convert_time()
# convert datestring (as UTC) into epoch
datum=mktime(datestring)
# perform TZ correction
datum-=4*3600;
# convert epoch into string (local TZ)
datum=strftime("2%Y%m%d_%H2",datum)
# print and append
print [=11=],datum
}' data.txt
POSIX awk: 如果你没有 GNU awk,但是任何其他 awk,你不能使用那些 time-functions 因为它们是特定于 GNU awk .但是可以实现它们:
awk '
# Algorithm from "Astronomical Algorithms" By J.Meeus
function mktime_posix(datestring, a,t) {
split(datestring,a," ")
if (a[1] < 1970) return -1
if (a[2] <= 2) { a[1]--; a[2]+=12 }
t=int(a[1]/100); t=2-t+int(t/4)
t=int(365.25*a[1]) + int(30.6001*(a[2]+1)) + a[3] + t - 719593
return t*86400 + a[4]*3600 + a[5]*60 + a[6]
}
function strftime_posix(epoch, JD,yyyy,mm,dd,HH,MM,SS,A,B,C,D,E ) {
if (epoch < 0 ) return "0000 00 00 00 00 00.000000"
JD=epoch; SS=JD%60; JD-=SS; JD/=60; MM=JD%60;
JD-=MM; JD/=60; HH=JD%24; JD-=HH; JD/=24;
JD+=2440588
A=int((JD - 1867216.25)/(36524.25))
A=JD+1+A-int(A/4)
B=A+1524; C=int((B-122.1)/365.25); D=int(365.25*C); E=int((B-D)/30.6001)
dd=B-D-int(30.6001*E)
mm = E < 14 ? E-1 : E - 13
yyyy=mm>2?C-4716:C-4715
return sprintf("2%0.4d-%0.2d-%0.2d %0.2d:%0.2d:%06.3f",yyyy,mm,dd,HH,MM,SS)
}
{ # convert into YYYY MM DD hh mm ss
datestring=convert_time()
# convert datestring (as UTC) into epoch
datum=mktime_posix(datestring)
# perform TZ correction
datum-=4*3600;
# convert epoch into string (local TZ)
datum=strftime_posix(datum)
# print and append
print [=12=],datum
}' data.txt
我在 linux 中使用 awk 命令将 utc 转换为本地时间,但是文件很大 (>30 gb) 并且需要一个多小时。
这是我的代码:
awk -F"," '{cmd="date -d \"$(date -d \"""\")-4hours\" \"+%Y%m%d_%H\"";cmd | getline datum; close(cmd); print [=11=] ","datum""}' data.txt
我怎样才能加快这个命令的速度,或者有什么简单的方法可以在 linux 中进行这种转换?
Here is the sample of input:
utc,id
2018-03-31 16:00:49.425,4485
2018-04-1 17:01:19.425,30019
2018-05-31 18:01:49.425,15427
2018-08-20 19:01:55.425,17579
2018-09-2 20:02:31.425,23716
2018-10-15 21:03:34.425,24772
expected output:
utc,id,localtime
2018-03-31 16:00:49.425,4485,20180331_12
2018-04-1 17:01:19.425,30019,20180401_13
2018-05-31 18:01:49.425,15427,20180531_14
2018-08-20 19:01:55.425,17579,20180820_15
2018-09-2 20:02:31.425,23716,20180902_16
2018-10-15 21:03:34.425,24772,20181015_17
你原来的解决方案之所以慢是因为对date
的系统调用。您使用 awk 处理的每个 record/line 调用外部命令来执行 date-conversion。这样的外部调用需要加载到内存中执行,其输出需要由 awk 处理。如果我们可以在 awk 本身中执行实际的 date-conversion,我们可以加快速度。
一般评论:当您将日期和时间从 UTC 转换为您当地的时区时,您必须考虑到 1 月 1 日与 8 月 1 日处于不同的时区。这由于 daylight-saving 时间。下面的算法没有提供解决方案,因为 OP 只是请求移动 4h,或者移动到他当前的 time-zone。 (备注: gawk 4.1.2 或更高版本的解决方案将考虑 DST)
下面我介绍了几种解决方案,您可以根据使用的 awk 使用这些解决方案:
Gnu awk: gawk
的各种扩展之一是 Time-functions。两个对这个问题有用的 time-functions 是 mktime
和 strftime
:
mktime(datespec,[utc-flag])
: This converts a date specification string,datespec
, of the formYYYY MM DD hh mm ss
into a Unix epoch time, i.e. the total seconds since 1970 01 01 UTC. Since gawk-4.2.1 you can use theutc-flag
to indicatedatespec
is in UTC or not. Prior to gawk-4.2.1 it assumed the local time-zone.
strftime(format,timestamp,[utc-flag])
: This converts an epoch-timetimestamp
into a formatted string (same formatting as thedate
command). You can use theutc-flag
the indicate that the returned time should be in UTC or in the local time-zone.More info in the GNU awk manual
我们要将字段 1 从 UTC 转换为本地 time-zone。由于我们不知道字段 1 的格式,我们假设存在一个函数 convert_time(str)
,它将 str
格式化为 mktime
可以接受的格式 YYYY MM DD hh mm ss
:
gawk 4.1.2 或更新版本:
$ awk 'BEGIN{FS=OFS=","}
{ # convert into YYYY MM DD hh mm ss
datestring=convert_time()
# convert datestring (as UTC) into epoch
datum=mktime(datestring,1)
# convert epoch into string (local TZ)
datum=strftime("2%Y%m%d_%H2",datum)
# print and append
print [=10=],datum
}' data.txt
在 gawk 4.1.2 之前: 这里我们不能使用 utc-flag
,所以我们强制 awk 在 UTC 中工作:
$ TZ=UTC awk 'BEGIN{FS=OFS=","}
{ # convert into YYYY MM DD hh mm ss
datestring=convert_time()
# convert datestring (as UTC) into epoch
datum=mktime(datestring)
# perform TZ correction
datum-=4*3600;
# convert epoch into string (local TZ)
datum=strftime("2%Y%m%d_%H2",datum)
# print and append
print [=11=],datum
}' data.txt
POSIX awk: 如果你没有 GNU awk,但是任何其他 awk,你不能使用那些 time-functions 因为它们是特定于 GNU awk .但是可以实现它们:
awk '
# Algorithm from "Astronomical Algorithms" By J.Meeus
function mktime_posix(datestring, a,t) {
split(datestring,a," ")
if (a[1] < 1970) return -1
if (a[2] <= 2) { a[1]--; a[2]+=12 }
t=int(a[1]/100); t=2-t+int(t/4)
t=int(365.25*a[1]) + int(30.6001*(a[2]+1)) + a[3] + t - 719593
return t*86400 + a[4]*3600 + a[5]*60 + a[6]
}
function strftime_posix(epoch, JD,yyyy,mm,dd,HH,MM,SS,A,B,C,D,E ) {
if (epoch < 0 ) return "0000 00 00 00 00 00.000000"
JD=epoch; SS=JD%60; JD-=SS; JD/=60; MM=JD%60;
JD-=MM; JD/=60; HH=JD%24; JD-=HH; JD/=24;
JD+=2440588
A=int((JD - 1867216.25)/(36524.25))
A=JD+1+A-int(A/4)
B=A+1524; C=int((B-122.1)/365.25); D=int(365.25*C); E=int((B-D)/30.6001)
dd=B-D-int(30.6001*E)
mm = E < 14 ? E-1 : E - 13
yyyy=mm>2?C-4716:C-4715
return sprintf("2%0.4d-%0.2d-%0.2d %0.2d:%0.2d:%06.3f",yyyy,mm,dd,HH,MM,SS)
}
{ # convert into YYYY MM DD hh mm ss
datestring=convert_time()
# convert datestring (as UTC) into epoch
datum=mktime_posix(datestring)
# perform TZ correction
datum-=4*3600;
# convert epoch into string (local TZ)
datum=strftime_posix(datum)
# print and append
print [=12=],datum
}' data.txt