使用正则表达式格式化文本文件

时间:2017-08-23 23:48:44

标签: unix

我正在尝试格式化下面的文本文件,记录顺序将始终如此

Dept 0100 Batch Load Errors for 8/16/2016 4:45:56 AM 

Case 1111111111
Rectype: ABCD 
    Key:UMUM_REF_ID=A12345678,UMSV_SEQ_NO=1
        UMSV ERROR  :UNITS_ALLOW must be > or = UNITS_PAID 

Case 2222222222
Rectype: ABCD 
    Key:UMUM_REF_ID=B87654321,UMSV_SEQ_NO=2
        UMSV ERROR  :UNITS_ALLOW must be > or = UNITS_PAID 
        NTNB ERROR  :Invalid Value                       NTNB_MCTR_SUBJ=AMOD

Case 3333333333
Rectype: WXYZ 
    Key:UMUM_REF_ID=U19817250,UMSV_SEQ_NO=2
        UMSV ERROR  :UNITS_ALLOW must be > or = UNITS_PAID 

作为输出

1111111111~ABCD~UMUM_REF_ID=A12345678,UMSV_SEQ_NO=1~UMSV ERROR  :UNITS_ALLOW must be > or = UNITS_PAID
2222222222~ABCD~UMUM_REF_ID=B87654321,UMSV_SEQ_NO=2~UMSV ERROR  :UNITS_ALLOW must be > or = UNITS_PAID|NTNB ERROR  :Invalid Value NTNB_MCTR_SUBJ=AMOD
3333333333~WXYZ~UMUM_REF_ID=U19817250,UMSV_SEQ_NO=2~UMSV ERROR  :UNITS_ALLOW must be > or = UNITS_PAID

我尝试了正则表达式,如下所示

sed -r '/^Case/!d;$!N;/\nRectype/!D;s/\s+$/ /;s/(.*)\n(.*)/\2\1\n\1/;P;D' file.txt

但这只适用于Rectype行,无法实现休息。

谢谢。

1 个答案:

答案 0 :(得分:0)

在我看来,你并不是在寻找正则表达式。您正在寻找文本重新格式化,并且您似乎在sed中选择了正则表达式匹配作为处理字段的方法。

了解XY problems here。值得庆幸的是,您已经包含了原始数据和预期输出,这对于新的StackOverflow成员来说是非常棒的。 (真的!你呢!)所以我可以推荐一种可能更适合你的替代品。

很糟糕。另一个命令行工具,就像sed一样,几乎安装在地球上每个类似unix的系统上。

$ awk -v RS= -v OFS="~" '!/^Case/{next} {sub(/^Key:/,"",$5); key=$5; for (f=6;f<=NF;f++) { if ($f=="NTNB") key=key "|"; else if ($f=="UMSV") key=key OFS; else key=key " "; key=key $f } print $2,$4,key}' inp2
1111111111~ABCD~UMUM_REF_ID=A12345678,UMSV_SEQ_NO=1~UMSV ERROR :UNITS_ALLOW must be > or = UNITS_PAID
2222222222~ABCD~UMUM_REF_ID=B87654321,UMSV_SEQ_NO=2~UMSV ERROR :UNITS_ALLOW must be > or = UNITS_PAID|NTNB ERROR :Invalid Value NTNB_MCTR_SUBJ=AMOD
3333333333~WXYZ~UMUM_REF_ID=U19817250,UMSV_SEQ_NO=2~UMSV ERROR :UNITS_ALLOW must be > or = UNITS_PAID

这是发生了什么。

  • awk -v RS= - 这很重要。它设置一个“空”记录分隔符,告诉awk我们正在处理多行记录。记录以空行终止,此记录中的字段由空格分隔。 (空格,制表符,换行符。)
  • -v OFS="~" - 为方便起见,设置波形符的输出字段分隔符。
  • $1!="Case"{next} - 如果当前记录没有单词“Case”作为其第一个字段,那么它不是我们可以处理的行,所以跳过它。
  • sub(/^Key:/,"",$5); key=$5; - 从第五个字段的开头修剪单词Key,将字段保存到变量。
  • for (f=6;f<=NF;f++) { - 逐步浏览其余字段
  • if ($f=="NTNB") key=key "|"; - 设置相应的字段分隔符。
  • else if ($f=="UMSV") key=key OFS; - ...
  • else key=key " "; - 如果文字看起来不像新字段,则为空格。
  • key=key $f } - 最后,将当前字段添加到我们的运行变量
  • print $2,$4,key} - 并打印所有内容。

注意:有一点不做就是保持间距,就像你在问题中的“预期输出”中所显示的那样。两个或多个空格将始终缩小到一个空格,因为在每个记录中,字段由空格分隔。

每条评论

更新

Windows使用\r\n(CRLF)来结束行,而unix / linux仅使用\n(LF)。由于您的文件是在Windows中生成的,因此“空白”行实际上包含不可见的CR,awk永远不会看到记录分隔符。

要查看文件的“真实”内容,您可以使用hexdumpod等工具。例如:

$ printf 'foo\r\nbar\r\n' | od -c
0000000    f   o   o  \r  \n   b   a   r  \r  \n
0000012

在您的情况下,只需运行:

$ od -c filename | less

(如果没有,则使用more。)

许多系统都有一个名为dos2unix的软件包,它可以转换文本格式。

如果您没有dos2unix,则应该可以使用其他工具实现相同的功能。在GNU sed中:

sed -i 's/\r$//' filename

或者在其他sed变体中,但是使用支持格式替换的shell(如bash)(阅读man sed以查看是否有-i选项):

sed $'s/\r$//' inputfile > outputfile

或者稍微不那么精确,因为它会删除所有CR,即使它们不在行尾,也可以使用tr

tr -d '\015' < inputfile > outputfile

或者如果perl可用,你可以使用一个几乎与sed相同的替换表达式(perl文档很容易告诉你选项的作用):

perl -i -pe 's/\r\n$/\n/g' filename
祝你好运!