我正在尝试格式化下面的文本文件,记录顺序将始终如此
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行,无法实现休息。
谢谢。
答案 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
永远不会看到记录分隔符。
要查看文件的“真实”内容,您可以使用hexdump
或od
等工具。例如:
$ 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
祝你好运!