使用 Awk/Sed 命令比较和替换内联

Compare and Replace inline using Awk/Sed command

我有一个文件(固定长度),其中搜索以数字 30 开头的连续 2 行,然后比较位置 183-187 中的值,如果两者都匹配则打印行号。到目前为止,我能够达到预期的结果。但是我想用空格替换行号中存在的值而不篡改固定长度。

awk '/^30*/a=substr([=13=],183,187);getline;b=substr([=13=],183,187); if(a==b) print NR}' file

上面命令的解释: 行号以 30* 开头 为 183 到 187 之间的起始位置赋值 获取下一行 从 183 到 187 之间的位置为 b 赋值 比较 a 和 b - 如果匹配,则在以 30 开头的连续 2 行中证明 183 到 187 之间的值。 打印行号(这是第二个匹配行号)

以上命令按预期工作并打印行号。

示例记录(仅用于解释目的,因此未使用固定长度)

10        ABC
20        XXX
30        XYZ
30        XYZ
30        XYZ
30        XYZ
40        YYY
10        ABC
20        XXX
30        XYZ
30        XYZ
40        YYY

使用上面的命令我可以获得第 3 行和第 4 行,但无法用空格替换第 4 行输出(内联替换),这样固定宽度就不会受到影响

预期输出

10        ABC
20        XXX
30        XYZ
30       
30       
30       
40        YYY
10        ABC
20        XXX
30        XYZ
30       
40        YYY

以上所有行的长度应为 255 个字符 - 当发生替换时,它必须内联而不将其添加为新空格。

我们将不胜感激任何帮助。谢谢

我会使用 GNU AWK 并将每个字符视为字段,请考虑以下示例,令 file.txt 内容为

10 ABC
20 XXX
30 XYZ
30 XYZ
40 YYY

然后

awk 'BEGIN{FPAT=".";OFS=""}prev~/^30*/{a=substr(prev,4,3);b=substr([=11=],4,3);if(a==b){===" "}}{print}{prev=[=11=]}' file.txt

产出

10 ABC
20 XXX
30 XYZ
30    
40 YYY

说明:我选择将整行存储在名为 prev 的变量中,而不是使用 getline,因此我将 {prev=[=17=]} 作为最后一个操作。我将 FPAT 设置为 .,表示应将任何单个字符视为字段,并将 OFS(输出字段分隔符)设置为空字符串,以便在行准备期间不会添加不需要的字符。如果 prev (前一行或第一行的空字符串)以 3 开头,我从前一行 (prev) 中获取字符 4,5,6 的子字符串并将其存储在变量 a 并从当前行 ([=25=]) 获取字符为 4、5、6 的子字符串并将其存储在变量 b 中,如果 ab 相等,我更改第 4、5 和 6 个字符每个 space。不管它是否被更改,我 print 行。免责声明:这假设您要处理最多 2 个具有相同子字符串的后续行。注意 /^30*/ 不检查字符串是否以 30 开头,而是检查它是否以 3 开头,例如它将匹配 312,您可能应该改用 /^30/,我选择使用您的模式不变,因为您暗示它确实适用于您的数据。

(在 gawk 4.2.1 中测试)

这可能对你有用 (GNU sed):

sed -E '/^30/{N;s/^(.{182}(.{5}).*\n.{182})/     /}' file

匹配开始于 30 的行并追加以下行。

使用模式匹配,如果两行从183-187的5个字符相同,则将第二组5个字符替换为5个空格。


对于多个相邻行使用:

sed -E '/^30/{:a;N;s/^(.{182}(.{5}).*\n.{182})/     /;ta}' file

或者选择:

sed -E ':a;$!N;s/^(30.{180}(\S{5}).*\n.{182})/     /;ta;P;D' file

听起来这就是你想要的,在每个 Unix 机器上使用任何 shell 中的任何 awk:

$ awk -v tgt=30 -v beg=11 -v end=13 '
    (==tgt) && seen[]++ { [=10=]=substr([=10=],1,beg-1) sprintf("%*s",end-beg,"") substr([=10=],end+1) }
1' file
10        ABC
20        XXX
30        XYZ
30
40        YYY

只需将 -v beg=11 -v end=13 更改为 beg=183 -v end=187 即可获得您的真实数据。

如果您再次想使用 getline,请务必先阅读 awk.freeshell。org/AllAboutGetline 因为这通常是错误的方法。