编辑:为了清晰起见对其进行了编辑,并使示例文件最小化且更具可重复性,以便更轻松地获得帮助。谢谢!
我的文件有 1000 多行。每个文件都使用相同数量的行进行格式化。的格式有 3 个“标题行”、1000 多行值(正值和负值,尾随小数点后 6 位),后面跟着 13 个“尾行”。行的格式如下所示。在我的真实文件中,在某些行上,我想要不同的命令,例如从行打印文本、对实际数据取平均值、复制文本和数据平均值的行以及对日期和时间取平均值。
这是一个大纲或一些长文件,其中包含有关每行目标的一些注释。
下面的概要是一个简化的示例。包含数据的行(示例中的第 4-9 行)实际上是真实文件中的第 4-1436 行。那么大纲中的第 10 行就是实际文件中的第 1437 行。 (希望这是有道理的)。数据行可以包含负数或正数,范围为 -100 到 +5000。
ABCDEFGH # Line 1... print text into output file (same on across all files)
1 # Line 2... Take average of values across all the files in this line
2048 # Line 3... Take average of values across all the files in this line
8.123456 # Line 4... Take average of values across all the files in this line (could be positive or negative)
5.123456 # Line 5... Take average of values across all the files in this line (could be positive or negative)
5.654321 # Line 6... Take average of values across all the files in this line (could be positive or negative)
4.654321 # Line 7... Take average of values across all the files in this line (could be positive or negative)
9.654321 # Line 8... Take average of values across all the files in this line (could be positive or negative)
1.654321 # Line 9... Take average of values across all the files in this line (could be positive or negative)
90.00 # Line 10... Check and make sure value in this line across print if same
Sprite # Line 11... check and see if text is same across all values and print if same
cats10 # Line 12... check and see if text is same across all values and print if same
07/02/20 # Line 13... See below for explantion on next 3 lines
08:32 # Line 14...
08:32 # Line 15...
290.000000 # Line 16... average across all files on this line
10.750000 # Line 17... average across all files on this line
SCANS23 # Line 18... output should be SCANS "average of values"
INT_TIME57500 # Line 19... output should be INT_TIME "sum of values"
SITE northpole # Line 20...Check if all lines are same if so print line
LONGITUDE -147.850037 # Line 21... Output should be LONGITUDE "average"
LATITUDE 64.859375 # Line 22... Output should be LONGITUDE "average"
第 13 行是数据的来源日期,第 14 行是开始时间和结束时间。可能使用某种日期到十进制命令..有没有办法取日期的平均值?如果一个数据是在 07/02/20 获取的,另一个数据是在 07/02/18 获取的,那么输出可以是 07/02/19 吗?时间的平均值也会被考虑在内。
我认为一些扩展的三元运算符可能是一条路径,但使用这么多不同的情况根本不起作用。
awk -F: '
FNR==1 { c++ };
/^LATITUDE/ { a[FNR] += $6 };
/^LONGITUDE/ { a[FNR] += $5 };
/^SITE/ { a[FNR] += $4 };
/^INT_TIME/ { a[FNR] += $3 };
/^SCANS/ { a[FNR] += $2 };
/^[+-]?([0-9]*[.])?[0-9]+$/ { a[FNR] += $1 };
END {
for (i in a) {
printf (i==22 ? "LATITUDE%f":
i==21 ? "LONGITUDE%2.3f":
i==20 ? "SITE%2.3f":
i==19 ? "INT_TIME%2.3f":
i==18 ? "SCANS%2.3f": "%f") "\n", a[i] / c
}
}' /home/test/test1.* > /home/average
假定所有示例文件都位于其中,/home/test/aaaaaa-bbbb-cc10dddd-L1-2020070119*-01.std
并希望“平均”文件输出的/home/dir/aaaaaa-bbbb-cc10dddd-L1-2020070119-01.std
格式为 /aaaaaa-bbbb-cc10-dddd-L1-"year""month""day""hour"-"elevation number “.std
输入文件于 2020 年 1 月 7 日 19 点在海拔 1 处拍摄:
/home/dir/dir2/aaaaaa-bbbb-cc10dddd-L1-202007011918-01.std
/home/dir/dir2/aaaaaa-bbbb-cc10dddd-L1-202007011929-01.std
/home/dir/dir2/aaaaaa-bbbb-cc10dddd-L1-202007011941-01.std
/home/dir/dir2/aaaaaa-bbbb-cc10dddd-L1-202007011953-01.std
输出文件将是
/home/dir/aaaaaa-bbbb-cc10dddd-L1-2020070119-01.std
/home/dir/dir2/aaaaaa-bbbb-cc10dddd-L1-202007011918-01.std
ABCDEFGH
1
2048
-3.249389
-4.544701
5.822962
2.372011
-17.937092
20.000408
5.00
Sprite
cats10
07/01/20
19:18
19:18
290.000000
10.690000
SCANS23
INT_TIME57500
SITE northpole
LONGITUDE -147.850037
LATITUDE 64.859375
/home/dir/dir2/aaaaaa-bbbb-cc10dddd-L1-202007011929-01.std
ABCDEFGH
1
2048
-6.369022
-4.957337
-2.715081
1.766033
-20.002853
21.522350
5.00
Avantes
buoy10
07/01/20
19:29
19:29
290.000000
10.310000
SCANS23
INT_TIME57500
SITE giroof
LONGITUDE -147.850037
LATITUDE 64.859375
/home/dir/dir2/aaaaaa-bbbb-cc10dddd-L1-202007011926-01.std
ABCDEFGH
1
2048
2.961413
-14.236549
19.784035
2.711583
-18.305300
9.369226
5.00
Avantes
buoy10
07/02/20
19:26
19:26
290.000000
10.310000
SCANS23
INT_TIME57500
SITE giroof
LONGITUDE -147.850037
LATITUDE 64.859375
答案1
这可能接近您所需要的,paste
将(希望不是太多)输入文件放入awk
,关闭任何locale
影响:
paste file[1-3] | LC_ALL=C awk -v"LNCT=$(wc -l <file1)" '
function avg( sum) {for (i=1; i<=NF; i++) sum += $i
return sum/NF
}
function same() {for (i=2; i<=NF; i++) if ($1 != $i) return 0
return 1
}
NR == 1 {print $1
next
}
NR <= (LNCT-13) ||
NR >= (LNCT-6) &&
NR <= (LNCT-5) {print avg()
next
}
NR > (LNCT-13) &&
NR <= (LNCT-10) {print (same()?$1:"")
}
NR >= (LNCT-9) &&
NR <= (LNCT-7) {if (NR == (LNCT-9)) FMT = "%m/%d/%y"
else FMT = "%H:%M"
for (i=1; i<=NF; i++) {CMD = "date +%s -d\"" $i"\""
CMD | getline $i
close (CMD)
}
CMD = "date +" FMT " -d\"@" avg() "\""
CMD | getline ITEM
close (CMD)
print ITEM
}
{ITEM = $1
gsub (/[0-9]*/, "", ITEM)
if (gsub (/SCANS|INT_TIME|LONGITUDE|LATITUDE/, "")) {print ITEM, avg()
}
if (gsub (/SITE/, "")) print ITEM, (same()?$1:"")
}
'
ABCDEFGH
1
2048
-2.219
-7.91286
7.63064
2.28321
-18.7484
16.964
5.00
07/01/20
19:24
19:24
290
10.4367
SCANS 23
INT_TIME 57500
SITE
LONGITUDE -147.85
LATITUDE 64.8594
它有点笨拙,因为它通过行号检测“特殊处理”行,尤其是。日期/时间的,但它似乎做了所要求的事情。我们需要预先计算行数,并wc - l
通过awk
变量传递输出,假设所有文件都具有相同的长度。可能还有其他/更好的方法。对于日期/时间计算:date
对于每次发生的事件运行外部命令来说,这是相当消耗资源的,而且并非在所有操作系统版本上都可用。它适用于我的 Linux 系统,但我愿意接受更好的想法。
答案2
这可能就是您正在寻找的内容,在日期平均值计算中使用 GNU awk 进行时间函数,并假设您的时区是 UTC 并且所有日期都是本世纪并且您没有任何空输入行:
$ cat tst.sh
#!/usr/bin/env bash
paste "$@" |
awk '
BEGIN { FS="\t"; CONVFMT="%0.6f" }
( 1 <= NR) && (NR <= 1) { print chkSameStrnums() }
( 2 <= NR) && (NR <= 9) { print getTagAveNr() }
(10 <= NR) && (NR <= 12) { print chkSameStrnums() }
(13 <= NR) && (NR <= 13) { print getAveDate() }
(14 <= NR) && (NR <= 15) { print getAveTime() }
(16 <= NR) && (NR <= 17) { print getTagAveNr() }
(18 <= NR) && (NR <= 18) { print getTagAveNr() }
(19 <= NR) && (NR <= 19) { print getTagSumNr() }
(20 <= NR) && (NR <= 20) { print chkSameStrnums() }
(21 <= NR) && (NR <= 22) { print getTagAveNr() }
function sumNrFlds( i,sum,val) {
for (i=1; i<=NF; i++) {
val = $i
sub(/^[^0-9-]+/,"",val)
sum += val
}
return sum
}
function getTagAveNr( tag) {
tag = $1
sub(/[0-9.-]+$/,"",tag)
return tag (sumNrFlds() / NF)
}
function getTagSumNr( tag) {
tag = $1
sub(/[0-9.-]+$/,"",tag)
return tag sumNrFlds()
}
function getAveDate( i,sum,d,secs) {
for (i=1; i<=NF; i++) {
split($i,d,"/")
secs = mktime("20"d[3] " " d[1] " " d[2] " 12 00 00", 1)
sum += secs
}
return strftime("%m/%d/%y",int(sum/NF))
}
function getAveTime( i,sum,t,ave,hrs,mins) {
for (i=1; i<=NF; i++) {
split($i,t,":")
mins = (t[1] * 60) + t[2]
sum += mins
}
ave = sum/NF
hrs = int(ave/60)
mins = int(ave - (hrs * 60))
return (hrs ":" mins)
}
function chkSameStrnums( i,diff) {
for (i=2; i<=NF; i++) {
if ($i != $1) {
diff = 1
break
}
}
return (diff ? "different" : $1)
}
'
$ ./tst.sh file?
ABCDEFGH
1
2048
-2.218999
-7.912862
7.630639
2.283209
-18.748415
16.963995
5.00
different
different
07/01/20
19:24
19:24
290
10.436667
SCANS23
INT_TIME172500
different
LONGITUDE -147.850037
LATITUDE 64.859375
如果两次之间的日期发生变化,时间计算会变得更有趣,但您通常没有办法在数据中表示这一点,所以我将其作为练习(提示:如果结束时间小于开始时间)时间并且您的间隔永远不能超过 24 小时,那么您就知道您已经过了一天,因此可以在结束时间上添加 24 小时 - 如果间隔可以超过 24 小时,那么您就不走运了)。