在固定宽度文件上使用 awk substr

Using awk substr on fixed width file

我有一个固定宽度的文件,我想按前四个字符定义的值将行拆分到名为 file_1985.datfile_1986.dat 的文件中。拿这个最小的文件:

$ cat foo.dat
1985tiny dancer
1986largechicken
1985hey  jude

我想这样结束:

$ cat file_1985.dat
1985tiny dancer
1985hey  jude

还有这个:

$ cat file_1986.dat
1986largechicken

我很确定我需要做这样的事情:

awk -F, '{if(???)print > "file_1985.dat";else print > "file_1986.dat"}' foo.dat

其中 ??? 涉及 substr 的一些使用。有人可以在这里建议吗?

使用 substr([=15=], 1, 4) 获取文件名中的 4 个字符(从字符 1 开始,长度为 4 的子字符串):

awk '{ 
  out = "file_" substr([=10=], 1, 4) ".dat" # set filename
  if (out != prev) close(prev)          # close previous file
  print >> out                          # write to file
  prev = out                            # remember filename to check on next line
}' foo.dat

文件名 out 来自将字符串文字与 substr 的结果连接的结果。此变量用于确定 print 的输出最终所在的文件。

>>在"append mode"中打开一个文件,这意味着如果您重新打开同一个文件,您不会丢失之前的内容。

正在测试:

$ awk '{ out = "file_" substr([=11=], 1, 4) ".dat"; if (out != prev) close(prev); print >> out; prev = out  }' foo.dat
$ cat file_1985.dat 
1985tiny dancer
1985hey  jude
$ cat file_1986.dat 
1986largechicken

close 的使用是防止打开太多文件的预防措施,但如果您的输入不是太大,那么您可以简化为:

awk '{ print > ("file_" substr([=12=], 1, 4) ".dat") }' foo.dat

关于性能,您可以尝试对输入进行排序,以避免重复打开和关闭相同的文件(尽管排序本身会花费时间):

sort -s -k1.1,1.4 foo.dat | awk '{ out = "file_" substr([=13=], 1, 4) ".dat"; if (out != prev) close(prev); print > out; prev = out  }'

这里我也将 >> 更改为 > 因为 awk 只会打开每个文件一次。

您还可以通过缓存结果来避免每行重复一次相同的字符串连接:

{
  ss = substr([=14=], 1, 4)
  if (!(ss in outs)) {
    outs[ss] = "file_" ss ".dat"
  }
  out = outs[ss]
  if (out != prev) close(prev)
  print >> out
  prev = out
}

将它放在像 script.awk 和 运行 这样的脚本中,就像 awk -f script.awk foo.dat

gawk -v FIELDWIDTHS="4 200" '{ print  > "file_"  ".dat" }' foo.dat

来自手册页: 如果 FIELDWIDTHS 变量设置为 space 分隔的数字列表,则每个字段都应具有固定宽度,并且 gawk 使用指定的宽度拆分记录。每个字段宽度可以 可选地在前面加上一个冒号分隔的值,指定在字段开始之前要跳过的字符数。 FS 的值被忽略。为 FS 或 FPAT 分配新值会覆盖 使用 FIELDWIDTHS。

如果您使用的是 GNU awk,或者您的输出文件少于 12 个,那么:

awk '{print > ("file_"substr([=10=],1,4)".dat")}' foo.dat

否则要避免 "too many open files" 错误:

awk '{out="file_"substr([=11=],1,4)".dat"; print >> out; close(out)}' foo.dat