【问题标题】:Sed command to find string and replace another string (txt format)sed 命令查找字符串并替换另一个字符串(txt 格式)
【发布时间】:2021-03-16 09:09:17
【问题描述】:

我正在尝试查找一个 txt 格式的字符串,每次找到它时,然后寻找一个特定的字符串以更改为另一个字符串。

想象下一个 hexa txt:

02 11 86 05 01 01 01 a0 11 60 0f 80 02 07 80 a1
09 06 07 04 00 00 01 00 1d 03 8a 02 01 2a 02 01
b7 09 01 27 30 22 a0 0a 80 08 33 04 03 92 22 14
00 11 86 05 01 01 01 a0 11 60 0f 80 02 07 80 a1
09 06 07 04 00 00 01 00 1d 03 8a 02 01 2a 02 01
b7 09 01 27 30 22 a0 0a 80 08 33 04 03 92 22 14

我每次遇到 2a 序列时都需要它来查找 09 01 序列并替换为 03 02。

预期输出:

02 11 86 05 01 01 01 a0 11 60 0f 80 02 07 80 a1
09 06 07 04 00 00 01 00 1d 03 8a 02 01 2a 02 01
b7 03 02 27 30 22 a0 0a 80 08 33 04 03 92 22 14
00 11 86 05 01 01 01 a0 11 60 0f 80 02 07 80 a1
09 06 07 04 00 00 01 00 1d 03 8a 02 01 2a 02 01
b7 03 02 27 30 22 a0 0a 80 08 33 04 03 92 22 14

我正在尝试这样的事情:

sed -i 's/09 01\(.*2a\)/03 02/g' packet.txt

【问题讨论】:

    标签: bash awk sed


    【解决方案1】:

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

    sed -zE 's/^/\x00/                       # introduce a unique delimiter
             :a;/\x00$/{s///;b}              # remove delimiter at end-of-file
             /\x002a/!{s/\x00(.)/\1\x00/;ba} # if not 2a pass over next char
             s//2a\x00/                      # next char is 2a prep for next string
             :b;/\x00$/ba                    # is it end of file
             /\x0009(\s)01/{s//03\102\x00/;ba}   # replace string and prep for 2a again
             s/\x00(.)/\1\x00/;bb' file      # not desired string so pass over char
    

    由于所需的字符串(在这种情况下为09 01)可能会出现在另一行或同一行内两次或更多次,因此行处理是不可行的。处理必须在字符级别,在此解决方案中,整个文件作为一个字符串处理(请参阅-z 选项)。

    确定了两种情况:

    1. 密钥(在本例中为 2a),在占位符 :a 内处理。
    2. 要替换的字符串(09 0103 02),在占位符 :b 内处理。

    一旦识别出密钥,处理就会转到下一个案例。替换所需的字符串后,将处理传回第一个案例。当遇到文件结尾时,任何一种情况都可以终止处理。

    注意该解决方案依赖于不包含空字符十六进制00的文件。

    【讨论】:

      【解决方案2】:

      使用 GNU awk 的替代 awk 解决方案:

      awk 'BEGIN { RS="2a" } { ORS=RS }  $0 ~ /09 01/ { $0=gensub("09 01","03 02","g",$0)}1' file
      

      将 2a 设置为记录分隔符。检查每条记录是否有“09 01”。如果存在,则使用 gensub 函数将“09 01”替换为“03 02”并将其设置为 $0。将输出记录分隔符设置为与记录分隔符相同后,使用简写1打印记录。

      【讨论】:

        【解决方案3】:

        假设您的意思是:“仅在 2a 之后发生时才替换”,那么您可以通过转换字节来做到这一点,以便每行仅出现一个 2a,例如:

        <hexa.txt tr '\n' ' ' | sed 's/2a/\n&/g'
        

        现在您只需在行以2a 开头时替换09 01,例如:

        sed -E 's/(^2a.*) 09 01/\1 03 02/'
        

        现在回到原始格式,即每行 16 个字节:

        tr '\n' ' ' | xargs -n16
        

        大家一起:

        <hexa.txt tr '\n' ' ' | sed 's/2a/\n&/g' |
        sed -E 's/(^2a.*) 09 01/\1 03 02/'       |
        tr '\n' ' ' | xargs -n16
        

        输出:

        02 11 86 05 01 01 01 a0 11 60 0f 80 02 07 80 a1
        09 06 07 04 00 00 01 00 1d 03 8a 02 01 2a 02 01
        b7 03 02 27 30 22 a0 0a 80 08 33 04 03 92 22 14
        00 11 86 05 01 01 01 a0 11 60 0f 80 02 07 80 a1
        09 06 07 04 00 00 01 00 1d 03 8a 02 01 2a 02 01
        b7 03 02 27 30 22 a0 0a 80 08 33 04 03 92 22 14
        

        【讨论】:

          【解决方案4】:

          如果这有帮助,

          cat *.txt | sed '/2a/s/09 01/02 03/g'
          

          【讨论】:

            【解决方案5】:

            我会用 awk 做到这一点:

            $ awk ' { for ( i = 1; i <= NF; ++i ) {
                        if ( $i == "2a" )
                            r = 1
                        if ( r && $i == "09" && $(i+1) == "01" ) {
                            r = 0
                            $i = "03"
                            $++i = "02"
                        }
                    }
                  }
                  1 ' hexa.txt > hexa.txt.modified
            

            找出差异:

            $ sdiff hexa.txt hexa.txt.modified
            02 11 86 05 01 01 01 a0 11 60 0f 80 02 07 80 a1                 02 11 86 05 01 01 01 a0 11 60 0f 80 02 07 80 a1
            09 06 07 04 00 00 01 00 1d 03 8a 02 01 2a 02 01                 09 06 07 04 00 00 01 00 1d 03 8a 02 01 2a 02 01
            b7 09 01 27 30 22 a0 0a 80 08 33 04 03 92 22 14               | b7 03 02 27 30 22 a0 0a 80 08 33 04 03 92 22 14
            00 11 86 05 01 01 01 a0 11 60 0f 80 02 07 80 a1                 00 11 86 05 01 01 01 a0 11 60 0f 80 02 07 80 a1
            09 06 07 04 00 00 01 00 1d 03 8a 02 01 2a 02 01                 09 06 07 04 00 00 01 00 1d 03 8a 02 01 2a 02 01
            b7 09 01 27 30 22 a0 0a 80 08 33 04 03 92 22 14               | b7 03 02 27 30 22 a0 0a 80 08 33 04 03 92 22 14
            

            【讨论】:

            • 太棒了!非常感谢!对了,你能稍微解释一下代码吗?
            • 无论替换到字符串是在同一行上还是在后面的行上,代码都可以正常工作。它不是逐行工作,而是逐场工作。最后的 1 打印行。 r 变量控制即将到来的“09 01”序列是否应该被替换。如果被替换,则 r 设置为 false。因此允许带有“09 01”的下一个字符串保持不变(因为之前没有“2a”检测)。一旦检测到“2a”,r 就会设置为 true,因此即将替换为字符串的替换
            • 最后一行的1个字符是什么意思?
            • 1 表示 awk 为真,过滤器为真,没有正则表达式,只是真,因此打印出这些行。看到这个:awk 1 /etc/hosts
            猜你喜欢
            • 2021-03-16
            • 2019-07-13
            • 2014-09-05
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2020-08-26
            • 1970-01-01
            • 2017-11-29
            相关资源
            最近更新 更多