我试图弄清楚是否有一个快速的单行程序sed或awk脚本我可以执行以修改文本文件中的某个值,特别是最后一行中的值文件。
目前我的文件有一个包含数据行数的预告行。我想修改它,以便它包括计数,包括页眉和页脚。任何帮助将不胜感激。
file1代码:
H|ACCT|XEC|1|TEMP|20130215035845|
D|849002|48|1208004|1
D|849007|28|1208004|1
D|849007|38|1208004|1
T|3
修改后输出应为
H|ACCT|XEC|1|TEMP|20130215035845|
D|849002|48|1208004|1
D|849007|28|1208004|1
D|849007|38|1208004|1
T|5
答案 0 :(得分:4)
修改以T:
开头的行$ awk '{sub(/^T.*/,"T|"NR)}1' file
H|ACCT|XEC|1|TEMP|20130215035845|
D|849002|48|1208004|1
D|849007|28|1208004|1
D|849007|38|1208004|1
T|5
按原始要求修改输入文件的最后一行:
$ awk '{printf "%s",p} {p=$0 ORS} END{sub(/\|.*/,"|"NR,p); print p}' file
H|ACCT|XEC|1|TEMP|20130215035845|
D|849002|48|1208004|1
D|849007|28|1208004|1
D|849007|38|1208004|1
T|5
由于在其评论中存在一些争论,即为什么我低估了getline
解决方案here,因为很难在评论中提供示例 - 这里有几个例子说明为什么你不应该使用它getline
解决这个问题的解决方案(或任何类似的解决方案)(或任何类似的解决方案):
适用于一组输入:
$ cat file1
H|ACCT|XEC|1|TEMP|20130215035845|
D|849002|48|1208004|1
D|849007|28|1208004|1
D|849007|28|1208004|1
T|3
$ awk '{printf "%s",p} {p=$0 ORS} END{sub(/\|.*/,"|"NR,p); print p}' file1
H|ACCT|XEC|1|TEMP|20130215035845|
D|849002|48|1208004|1
D|849007|28|1208004|1
D|849007|28|1208004|1
T|5
$ awk '{l=$0; if(getline==1){print l; print} else {sub("\\|.*","|"NR);print}}' file1
H|ACCT|XEC|1|TEMP|20130215035845|
D|849002|48|1208004|1
D|849007|28|1208004|1
D|849007|28|1208004|1
T|5
另一个失败:
$ cat file2
H|ACCT|XEC|1|TEMP|20130215035845|
D|849002|48|1208004|1
D|849007|28|1208004|1
T|3
$ awk '{printf "%s",p} {p=$0 ORS} END{sub(/\|.*/,"|"NR,p); print p}' file2
H|ACCT|XEC|1|TEMP|20130215035845|
D|849002|48|1208004|1
D|849007|28|1208004|1
T|4
$ awk '{l=$0; if(getline==1){print l; print} else {sub("\\|.*","|"NR);print}}' file2
H|ACCT|XEC|1|TEMP|20130215035845|
D|849002|48|1208004|1
D|849007|28|1208004|1
T|3
尴尬(充其量)增强最小的工作,例如将每行打印到stderr进行调试:
$ awk '{print |"cat>&2"} {printf "%s",p} {p=$0 ORS} END{sub(/\|.*/,"|"NR,p); print p}' file2
$ awk '{print |"cat>&2"; l=$0; if(getline==1){print |"cat>&2"; print l; print} else {print |"cat>&2"; sub("\\|.*","|"NR); print}}' file1
注意修改2个版本之间的简单性差异。修改getline
版本是笨拙的,复杂的,非平凡的,非显而易见的,低效的,容易出现阴险错误,需要重复代码和/或重写等等......
我们上面看到的是尝试使用getline
来解决awk的自然文本处理模式可以轻松处理的问题的非常常见的影响。
getline
非常有用,有关有效应用的一些示例,请参阅http://awk.info/?tip/getline。
答案 1 :(得分:2)
它不是严格意义上的单行,它假设“T”行的格式,但是:
(sed '${=;d;}' | sed '$s/^/T|/') < infile > outfile
还有一个单行电话:
awk '/^T/ {sub(/[0-9]*$/, NR)}; {print}' < infile > outfile
答案 2 :(得分:0)
更新2 :
awk
解决方案,它也只会读取一次文件,请参阅@Ed Morton's answer。 getline
,awk
函数有很多陷阱(但也有合法的应用程序) - 请参阅http://awk.freeshell.org/AllAboutGetline
getline
的解决方案通常存在问题的方面是可维护性 - 修改此解决方案不仅仅是更新行数也很麻烦。 awk
解决方案只读取输入文件一次:
awk '{l=$0; while(getline==1){print l;l=$0;} sub("\\|.*","|"NR); print}' file
带注释的版本:
awk '
{
l=$0 # save 1st line read
# Start a loop that reads all remaining lines.
# Print them EXCEPT for the LAST one.
while (getline == 1) { # loop until the last line is read
print l # print the saved line now known not to be the last
l=$0 # save this line for the next iteration
}
# Getting here means: the last line was read (and is stored in $0).
sub("\\|.*","|"NR) # replace the part after "|" with the line count
print # output modified last line
}
' file
请注意,POSIX awk
和许多实现不支持修改输入文件到位,因此您必须将输出(至少暂时)保存到不同的文件。
然而,正如@Ed Morton指出的那样,GNU awk
,版本4.1
或更高,确实允许使用-i inplace
进行就地修改 - 请参阅{ {3}}
答案 3 :(得分:-1)
awk
版本
awk -F\| 'FNR==NR{f++;next} FNR==f {$NF=f} 1' OFS=\| file{,}
H|ACCT|XEC|1|TEMP|20130215035845|
D|849002|48|1208004|1
D|849007|28|1208004|1
D|849007|38|1208004|1
T|5
如果file{,}
不起作用,请使用file file
两次读取文件。
第一次计算行数,然后更新计数器以显示正确的行。
仅计算以H
D
和T
awk -F\| 'FNR==NR{if (/^(H|D|T)/) f++;n=NR;next} FNR==n {$NF=f} 1' OFS=\| file{,}