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

Awk 修改文本文件最后一行中的特定字段,awk,sed,Awk,Sed,我试图找出是否有一个快速的单行sed或awk脚本,我可以执行它来修改文本文件中的某个值,特别是文件最后一行中的值 当前我的文件有一个带有数据行计数的尾行。我想修改它,使其包含包括页眉和页脚在内的计数。任何帮助都将不胜感激 文件1代码: 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|

我试图找出是否有一个快速的单行sed或awk脚本,我可以执行它来修改文本文件中的某个值,特别是文件最后一行中的值

当前我的文件有一个带有数据行计数的尾行。我想修改它,使其包含包括页眉和页脚在内的计数。任何帮助都将不胜感激

文件1代码:

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

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
如果
文件{,}
不起作用,请使用
文件
读取文件两次。 第一次计数行,然后更新计数器以显示正确的行


只计算以
H
D
T

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

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
如果
文件{,}
不起作用,请使用
文件
读取文件两次。 第一次计数行,然后更新计数器以显示正确的行


只计算以
H
D
T

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

更新2

  • 此解决方案有效,因为它只读取输入文件一次
  • 但是,要获得一种更为惯用的
    awk
    解决方案,该解决方案也只能读取文件一次,请参阅。
  • 此解决方案使用
    getline
    ,这是一个
    awk
    函数,它有许多缺陷(但也有合法的应用程序)-请参阅
    • 举个例子:这个答案的原始版本根本上被打破了,因为它只适用于具有奇数行的输入文件;请再次参阅Ed的答案以获取说明
  • 另一个通常会使基于
    getline
    的解决方案产生问题的方面是可维护性-修改此解决方案以完成不仅仅是更新行数的工作将是很麻烦的

一种只读取输入文件一次的
awk
解决方案:

注释版本:

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 in place
-进行就地修改-请参见更新2:

  • 此解决方案有效,因为它只读取输入文件一次
  • 但是,要获得一种更为惯用的
    awk
    解决方案,该解决方案也只能读取文件一次,请参阅。
  • 此解决方案使用
    getline
    ,这是一个
    awk
    函数,它有许多缺陷(但也有合法的应用程序)-请参阅
    • 举个例子:这个答案的原始版本根本上被打破了,因为它只适用于具有奇数行的输入文件;请再次参阅Ed的答案以获取说明
  • 另一个通常会使基于
    getline
    的解决方案产生问题的方面是可维护性-修改此解决方案以完成不仅仅是更新行数的工作将是很麻烦的

一种只读取输入文件一次的
awk
解决方案:

注释版本:

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
-参见进行就地修改,它不是严格意义上的单行程序,它对“T”行的格式进行了假设,但是:

(sed '${=;d;}' | sed '$s/^/T|/') < infile > outfile
(sed'${=;d;}'| sed'$s/^/T |/')outfile
和awk单班轮:

awk '/^T/ {sub(/[0-9]*$/, NR)}; {print}' < infile > outfile
awk'/^T/{sub(/[0-9]*$/,NR)};{print}'outfile

严格来说,它不是一行,它对“T”行的格式进行了假设,但是:

(sed '${=;d;}' | sed '$s/^/T|/') < infile > outfile
(sed'${=;d;}'| sed'$s/^/T |/')outfile
和awk单班轮:

awk '/^T/ {sub(/[0-9]*$/, NR)}; {print}' < infile > outfile
awk'/^T/{sub(/[0-9]*$/,NR)};{print}'outfile

修改以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
解决方案进行了一些辩论,并且由于很难在评论中给出示例,因此以下是几个示例,说明为什么不应使用该
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
请注意修改这两个版本在简单性方面的差异。修改
getline
版本是笨拙的、复杂的、非琐碎的、不明显的、低效的、容易出现潜在错误的、需要重复代码和/或重大重写等

我们在上面看到的是试图使用
getline
来解决awk的自然文本处理模式可以轻松处理的问题的常见后果


getline
在适当使用时非常有用,有关有效应用程序的一些示例,请参阅。

要修改以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
解决方案进行了一些辩论,并且由于很难在评论中给出示例,因此以下是几个示例,说明为什么不应使用该
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以进行调试)而言,难以(最多)进行增强: