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

我正在尝试查找 txt 格式的字符串,每次找到它时,都会查找要更改为另一个字符串的特定字符串。此外,该字符串后面是十六进制 txt 的长度

想象下一个十六进制 txt:

示例 1:

09 06 07 04 00 00 01 00 1d 03 4b 2c a1 2a 02 01   
b7 09 01 47 30 22 a0 0a 80 08 33 04 03 92 22 14   
17 f0 a1 0b 80 00 81 00 84 01 00 86 00 85 00 83   
07 91 94 71 06 00 07 19

示例 2:

09 06 07 04 00 00 01 00 2b 03 4b 27 a1 25 02 01   
00 09 01 66 30 1d a0 0a 80 08 33 04 03 92 22 14   
17 f0 a1 06 82 00 84 00 85 00 82 07 91 94 71 06   
00 07 19

我需要每次遇到 4b 序列时查找 09 01 序列并替换为 03 02。这仅在 4b 后面的十六进制值是剩余序列的总长度时才有效。在第一种情况下,2c 表示两行和 12 个十六进制值(2c-> 两行和 12 个十六进制值[c=12 in hexadecimal])。第二种情况,27代表两行,7个十六进制值。

我正在尝试这样的事情,但我不知道如何检查十六进制长度

gawk -i inplace  ' { for ( i = 1; i <= NF; ++i ) {

        if ( $i == "4b" )
            r = 1
        if ( r && $i == "09" && $(i+1) == "01" ) {
            r = 0
            $i = "03"
            $++i = "02"
        }
    }
  }
  1 ' hexadecimal.txt 

提前致谢

问题 2-十六进制数组:

'正在尝试以 txt 格式查找字符串 1,每次找到它时,然后查找特定字符串 2 以更改为另一个字符串 3。但是,我们必须注意剩余序列的长度。我的意思是,字符串 1 后面的总是十六进制的静止序列的长度。因此,由于字符串 1 可以多次出现,我想检查它后面的十六进制数是否是剩余序列的长度。

我需要每次遇到5a序列寻找06 01序列并替换为02 01。这只有在5a后面的十六进制值是剩余序列的总长度时才有效

想象下一个十六进制 txt:

76 9d 6c 17 09 01 03 0e 19 0b 12 06 00 12 04 25 55 21 32 25 80 0b 12 93 00 12 04 94 71 06 00 07 19 56 62 54 48 04 00 00 00 01 6b 1e 28 1c 06 07 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 5a 2c a1 2a 02 01 b2 06 01 47 30 22 a0 0a 80 08 33 04 03 92 22 14 17 f0 a1 0b 80 00 81 00 84 01 00 86 00 82 22 83 07 91 94 71 06 00 08 69

00 11 86 05 01 01 01 a0 11 60 0f 58 02 07 80 a1 09 06 07 04 00 23 01 22 2b 03 5a 27 a9 25 02 01 00 02 01 3e 30 1d 22 0a 80 08 33 04 03 92 22 14 17 f0 a1 06 82 00 84 00 85 00 82 07 91 94 71 06 00 07 19

09 06 07 04 00 00 01 00 2b 03 5a 27 a1 25 02 01 00 06 01 66 30 1d a0 0a 80 08 33 04 03 92 22 14 17 f0 a1 06 22 00 22 00 85 00 82 07 91 94 71 06 00 07 19

解释:

第一段:2c表示剩余序列的长度,即两行12个十六进制值(2c=两行12个十六进制值[c=12 in hexadecimal])

第二段:27代表两行7位十六进制值。

第三段:27代表两行7位十六进制值。

【问题讨论】:

  • 请解释一下(length of the) resting sequence的意思;另外,2c27 指的是输入数据的哪一部分(即linehexa 指的是什么)?
  • 一个序列(例如,4b)能否在数据中多次出现?
  • 在开发脚本时不要使用-i inplace。先得到你想要的输出,然后再添加-i inplace。它根本不需要成为您问题的一部分。用列和行中的字符串来写你的问题——不要假设我们知道什么是“hexa”和“resting sequence”或任何其他特定于你的域的术语,否则你会严重限制将成为能够帮助你。
  • @markp-fuso (the length of the) 静止序列是指剩下多少个十六进制值。 2c 表示序列的长度。如果你看一下 2c,你有 2 行完整的十六进制参数和 12 个十六进制值(c=12)。 27 也是如此。4b 可以多次出现,这就是为什么我想用 4b 后面的十六进制值进行检查
  • 我认为描述2c27 的更好方法是: 文件的内容是两位十六进制代码; 2c27 表示文件中剩余的十六进制代码的数量,即2c(hex) == 44(decimal) => 文件中剩余的44个2位十六进制代码(在2c之后)

标签: bash awk sed


【解决方案1】:

给你

    gawk ' NR==FNR { totalRecords += NF; lines = NR; next }
    {
    for ( i = 1; i <= NF; ++i ) {
        
        if ( $i == "4b" )
        {
            expected = strtonum( "0x"$(i+1) );
            rest = totalRecords-16*(NR-lines-1)-i-1
            if(expected == rest) {
                r = 1
            }
            
        }
        if (r && $i == "09" && $(i+1) == "01" ) {
            r = 0
            $i = "03"
            $++i = "02"
        }
    }
    }
    1 ' hexadecimal.txt hexadecimal.txt

代码首先计算文件中的记录总数及其第一遍中的总行数。

然后,在第二遍遇到“0b”时,它会计算找到预期的记录数 $(i+1) 并根据文件中的记录总数计算剩余记录的实际数(rest) , 文件中的行数和当前行数。

因此,当遇到“09 01”时,程序也会检查预期记录数等于实际剩余记录数的条件。

【讨论】:

  • 它看起来不错,但不起作用。我试图弄清楚发生了什么
  • 你能解释一下吗
  • 它对我来说很好用。我用一些解释编辑了我的答案。我还稍微修改了代码,以便在遇到“4b”时将条件(预期 == 休息)存储在 r 中。我还删除了 -i inplace 以便您可以在不更改文件的情况下看到命令的结果。也许您使用的是修改后的文件而不是原始文件。当您检查一切正常时,您可以添加 -i inplace。
【解决方案2】:

来自 cmets 和分析的假设和其他细节:

  • 输入数据由一系列 2 位十六进制(十进制)代码组成
  • 所需的搜索模式(例如,序列 = 4b)仅在源数据中出现一次(或者,如果它确实出现多次,我们只对匹配角度的第一次出现感兴趣)李>
  • 我们希望替换的模式(例如,序列 = 09 01)可能在源数据中出现多次
  • 我们希望替换的模式可以分成两行(例如,序列 = 09\n01
  • 如果我们发现 a) 匹配我们的搜索模式(即sequence = 4b)并且 b) 下一个 2 位十六进制代码是剩余的数量数据集中的 2 位十六进制代码(例如,(0x)2c == 44(decimal)),然后 c) 进行替换
  • 输入和输出数据限制为每行 16 组 2 位十六进制代码

一些示例数据:

$ cat hexa1.txt
09 06 07 04 00 00 01 00 1d 03 4b 2c a1 2a 02 01   # sequence=4b, length of resting sequence = 2c(hex) = 44(decimal)
b7 09 01 47 30 22 a0 0a 80 08 33 04 03 92 22 14   # replace '09 01' with '03 02'
17 f0 a1 0b 80 00 81 00 84 01 00 86 00 85 00 83
07 91 94 71 06 00 07 19                           # 44x 2-digit hex codes after '4b 2c'

$ cat hexa2.txt
09 06 07 04 00 00 01 00 1d 03 4b 2c a1 2a 02 01   # sequence=4b, length of resting sequence = 2c(hex) = 44(decimal)
b7 09 01 47 30 22 a0 0a 80 08 33 04 03 92 22 14   # replace '09 01' with '03 02'
17 f0 a1 0b 80 00 81 00 84 01 00 86 00 85 00 09   # replace '09\m01' with '03\n02'
01 91 94 71 06 00 07 19                           # 44x 2-digit hex codes after '4b 2c'

$ cat hexa3.txt
09 06 07 04 00 00 01 00 1d 03 4b 2c a1 2a 02 01   # sequence=4b, length of resting sequence = 2c(hex) = 44(decimal)
b7 09 01 47 30 22 a0 0a 80 08 33 04 03 92 22 14   # do nothing since only ...
17 f0 a1 0b 80 00 81 00 84 01 00 86 00 85 00 83
07 91 94 71 06 00 07                              # 43x 2-digit hex codes after '4b 2c'

基于 OP 当前的awk 解决方案:

$ cat hexa.bash
#!/usr/bin/bash

datfile="${1}"

sequence='4b'                                             # hardcoded for now; OP can parameterize this as needed

awk -v seq="${sequence}" -v outwidth=16 -v RS="" --non-decimal-data '

{ totallen=NF                                             # total number of 2-digit hex codes

  # process fields

  for ( i=1 ; i<=NF ; i++ )
      { seqlen = strtonum("0x"$(i+1))                     # convert next 2-digit hex code to decimal
        if ( $i == seq && seqlen == (totallen - i - 1) )  # if current field matches our input "seq" and the next field reflects the number of 2-digit hex codes in the rest of the input data ...
           r=1                                            # then set our replacement flag to 1
        if ( r && $i == "09" && $(i+1) == "01" )          # if the replacement flag == 1 ...
                                                          # and the current/next fields are "09" and "01", respectively ...
           { $i="03"                                      # then replace the current/next
             $++i="02"                                    # fields with "03" and "02"
           }
      }

  # print fields to stdout, use "outwidth" to determine
  # max number of fields to place on a line of output

  pfx=""
  for ( i=1 ; i<=NF ; i++ )
      { printf "%s%s", pfx, $i
        pfx=" "
        if ( i % outwidth == 0 )                         # for every outwidth (16th) field add a newline character
           { printf "\n"
             pfx=""
           }
      }
  if ( pfx == " " )
     printf "\n"                                         # append newline character to last line of output
}
' "${datfile}" > "${datfile}.new"

echo "++++++++++++++++++ differences between input (${datfile}) and output (${datfile}.new):"

sdiff "${datfile}" "${datfile}.new"

地点:

  • -v seq="${sequence}" - 将搜索序列作为 awk 变量“seq”传入
  • -v outwidth=16 - 每行输出限制为 16 个 2 位十六进制代码
  • -v RS="" - 清除行分隔符,即将输入数据视为一个长单行;这将允许我们找到跨越 2 行的匹配项(即,将 09\n01 转换为 09 01
  • 注意:搜索和替换模式(09 0103 02,分别)是针对这个特定练习进行硬编码的; OP 可以根据需要对这些参数进行参数化
  • UDPATE:借用@restricteur 的strtonum() 将2 位十六进制代码转换为十进制

针对我们的示例数据集运行上述操作:

# one pair of "09 01" replaced:

$ hexa.bash  hexa1.txt
++++++++++++++++++ differences between input (hexa1.txt) and output (hexa1.txt.new):
09 06 07 04 00 00 01 00 1d 03 4b 2c a1 2a 02 01                 09 06 07 04 00 00 01 00 1d 03 4b 2c a1 2a 02 01
b7 09 01 47 30 22 a0 0a 80 08 33 04 03 92 22 14               | b7 03 02 47 30 22 a0 0a 80 08 33 04 03 92 22 14
17 f0 a1 0b 80 00 81 00 84 01 00 86 00 85 00 83                 17 f0 a1 0b 80 00 81 00 84 01 00 86 00 85 00 83
07 91 94 71 06 00 07 19                                         07 91 94 71 06 00 07 19

# two pairs of "09 01" replaced:

$ hexa.bash  hexa2.txt
++++++++++++++++++ differences between input (hexa2.txt) and output (hexa2.txt.new):
09 06 07 04 00 00 01 00 1d 03 4b 2c a1 2a 02 01                 09 06 07 04 00 00 01 00 1d 03 4b 2c a1 2a 02 01
b7 09 01 47 30 22 a0 0a 80 08 33 04 03 92 22 14               | b7 03 02 47 30 22 a0 0a 80 08 33 04 03 92 22 14
17 f0 a1 0b 80 00 81 00 84 01 00 86 00 85 00 09               | 17 f0 a1 0b 80 00 81 00 84 01 00 86 00 85 00 03
01 91 94 71 06 00 07 19                                       | 02 91 94 71 06 00 07 19

# no replacements:

$ hexa.bash  hexa3.txt
++++++++++++++++++ differences between input (hexa3.txt) and output (hexa3.txt.new):
09 06 07 04 00 00 01 00 1d 03 4b 2c a1 2a 02 01                 09 06 07 04 00 00 01 00 1d 03 4b 2c a1 2a 02 01
b7 09 01 47 30 22 a0 0a 80 08 33 04 03 92 22 14                 b7 09 01 47 30 22 a0 0a 80 08 33 04 03 92 22 14
17 f0 a1 0b 80 00 81 00 84 01 00 86 00 85 00 83                 17 f0 a1 0b 80 00 81 00 84 01 00 86 00 85 00 83
07 91 94 71 06 00 07                                            07 91 94 71 06 00 07

【讨论】:

  • 嗨@mar​​kp-fuso。很抱歉再次打扰您,但解决方案得到了十六进制无序圆顶时间。你能看一下问题2吗? (我在另一个问题上方发帖)感谢您的耐心等待,我是这方面的初学者
  • 我不明白你说的hexadecimal unordered是什么意思;我可以看到一个问题是您的原始问题是基于每个文件的单个代码字符串,而您现在基本上添加了一个基于单个文件中的多组代码的全新问题;问题是我的回答(上面)专门针对您的第一个问题(即每个文件一组代码),不适用于您的新问题(每个文件多组代码)
  • 一个单一的 SO Q&A 不是为持续不断的、不断变化的问题和答案流而设计的;如果第一个问题已得到解答,您将需要查看 What should I do when someone answers my question?;如果在使用答案/解决方案后发现新问题,则打开一个新问题,但在该新问题中,您应该展示您尝试过的内容(回复:使用第一个问题的答案)并提供有关新问题的详细信息问题
【解决方案3】:

4b 可以多次出现,这就是为什么我要检查 4b 后面的十六进制值。

那么请您尝试以下方法:

awk 'function replace(j, len) {                         # this function modifies global variable a and i
        len = strtonum("0x" a[i+1])                     # calculate the length of the resting sequence
        i += 2                                          # increment the index by two for "4b" and the length
        for (j = 0; j < len; j++) {                     # iterate among the sequence
            if (a[i] == "09" && a[i+1] == "01") {       # if the replacees are found
                a[i++] = "03"; a[i] = "02"; j++         # then replace them and increment j to avoid overlap
            }
            i++                                         # increment the global index
        }
    }
    {
        for (i = 1; i <= NF; i++) a[++n] = $i           # read the entire strings in the array a
    }
    END {
        for (i = 1; i <= n; i++) {                      # search for the bytes in turn
            if (a[i] == "4b") replace()                 # if "4b" is found, call the function
        }                                               # note that the index "i" is incremented in the function
        for (i = 1; i <= n; i++) {                      # format and output the array
            printf(a[i] (i%16? " " : "\n"))             # put a whitespace or newline depending on the column position
        }
        if (n%16) printf("\n")                          # if the column ends before 16th, add a newline
    }
' example.txt

作为包含多个4bs 的示例,我将提供的两个示例合并如下:

09 06 07 04 00 00 01 00 1d 03 4b 2c a1 2a 02 01   
b7 09 01 47 30 22 a0 0a 80 08 33 04 03 92 22 14   
17 f0 a1 0b 80 00 81 00 84 01 00 86 00 85 00 83   
07 91 94 71 06 00 07 19 2b 03 4b 27 a1 25 02 01   
00 09 01 66 30 1d a0 0a 80 08 33 04 03 92 22 14   
17 f0 a1 06 82 00 84 00 85 00 82 07 91 94 71 06   
00 07 19

上面输入文件的输出:

09 06 07 04 00 00 01 00 1d 03 4b 2c a1 2a 02 01
b7 03 02 47 30 22 a0 0a 80 08 33 04 03 92 22 14
17 f0 a1 0b 80 00 81 00 84 01 00 86 00 85 00 83
07 91 94 71 06 00 07 19 2b 03 4b 27 a1 25 02 01
00 03 02 66 30 1d a0 0a 80 08 33 04 03 92 22 14
17 f0 a1 06 82 00 84 00 85 00 82 07 91 94 71 06
00 07 19 

您会看到09 01 序列在两个位置被03 02 替换。

【讨论】:

    猜你喜欢
    • 2021-03-16
    • 2019-07-13
    • 2017-06-16
    • 2012-09-11
    • 2014-07-17
    • 2019-05-14
    • 2016-09-09
    • 2013-12-03
    • 2010-11-17
    相关资源
    最近更新 更多