修改文本文件最后一行中的特定字段

时间:2014-05-13 04:16:02

标签: awk sed

我试图弄清楚是否有一个快速的单行程序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

4 个答案:

答案 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
  • 此解决方案使用getlineawk函数有很多陷阱(但也有合法的应用程序) - 请参阅http://awk.freeshell.org/AllAboutGetline
    • 例证:这个答案的原始版本从根本上被打破,因为它只适用于具有奇数行数的输入文件;再看看Ed的回答,以示例说明。
  • 另一个可以使基于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 DT

开头的行
awk -F\| 'FNR==NR{if (/^(H|D|T)/) f++;n=NR;next} FNR==n {$NF=f} 1' OFS=\| file{,}