使用 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
中,如果 a
和 b
相等,我更改第 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 因为这通常是错误的方法。
我有一个文件(固定长度),其中搜索以数字 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
中,如果 a
和 b
相等,我更改第 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 因为这通常是错误的方法。