【问题标题】:Awk substring doesnt yield expected resultawk 子字符串不会产生预期的结果
【发布时间】:2017-05-30 08:23:49
【问题描述】:

我有一个文件,其内容如下:

C2:0301,353458082243570,353458082243580,0;
C2:0301,353458082462440,353458082462450,0;
C2:0301,353458082069130,353458082069140,0;
C2:0301,353458082246230,353458082246240,0;
C2:0301,353458082559320,353458082559330,0;
C2:0301,353458080153530,353458080153540,0;
C2:0301,353458082462670,353458082462680,0;
C2:0301,353458081943950,353458081943960,0;
C2:0301,353458081719070,353458081719080,0;
C2:0301,353458081392470,353458081392490,0;

Field 2 and Field 3(考虑将,作为分隔符),包含15 digit IMEI numberrangesnot个人IMEI numbersIMEI 的常用格式是 8-digits(TAC)+6-digits(Serial number)+0(padded)IMEI 中的 6 digits(Serial number) 部分定义了开始和结束范围,其他一切都保持不变。因此,为了在范围内找到单个 IMEIs(这正是我想要的),我需要来自 6 digits(Serial number)unary increment loop 来自 starting IMEI number in Field-2 直到 6 digits(Serial number) 来自 ending IMEI number @987654339 @。我正在使用下面的AWK 脚本:

awk -F"," '{v = substr($2,9,6); t = substr($3,9,6); while(v <= t) printf "%s%0"6"s%s,%s\n", substr($3,1,8),v++,substr($3,15,2),$4;}' TEMP.OUT.merge_range_part1_21

它给了我以下结果:

353458082243570,0
353458082243580,0
353458082462440,0
353458082462450,0
353458082069130,0
353458082069140,0
353458082246230,0
353458082246240,0
353458082559320,0
353458082559330,0
353458080153530,0
353458082462670,0
353458082462680,0
353458081943950,0
353458081943960,0
353458081719070,0
353458081719080,0
353458081392470,0
353458081392480,0
353458081392490,0

除了result中的以下行之外,上述内容与预期相同:

 353458080153530,0

result 实际上来自input file 中的下面一行:

 C2:0301,353458080153530,353458080153540,0;

input file 中上述行的expected output 是:

  353458080153530,0
  353458080153540,0

我需要知道我的脚本出了什么问题。

【问题讨论】:

  • 不仅是一种情况,还有你输入的最后 3 行:353458081392470,0 353458081392480,0 353458081392490,0 - 这个353458081392480,0 不应该在那里
  • @RomanPerekhrest 没关系。基本上增量必须发生在substr($2,9,6)。在这种情况下,139247 会增加到139249,因此我有353458081392470,0 353458081392480,0 353458081392490,0,这正是我所期望的。我的问题是我的脚本没有处理前导零的情况。例如上面的问题案例substr($2,9,6)015353。它应该递增到015354,我应该同时拥有353458080153530,0353458080153540,0。这没有发生
  • 忽略我的回答,因为它不适用于 while 循环的第二次迭代
  • 在语句末尾设置 v=y 应该可以使其在额外的增量上起作用。
  • 我不知道你想做什么,别管它可能有什么问题。你能简化你的例子并阐明你的要求吗?

标签: bash shell awk scripting


【解决方案1】:

您的脚本的问题是您从 2 个字符串变量 v 和 t 开始(键入为字符串,因为它们是字符串操作的结果,substr()),然后使用 v++ 将其中的一个转换为数字将去除前导零,但随后您将与 v &lt;= t 进行字符串比较,因为与数字或字符串或数字字符串相比的字符串 (t) 始终是字符串比较。是的,您可以向每个变量添加零以强制进行数字比较,但恕我直言,这更像您真正想要做的:

$ cat tst.awk
BEGIN { FS=","; re="(.{8})(.{6})(.*)" }
{
    match($2,re,beg)
    match($3,re,end)
    for (i=beg[2]; i<=end[2]; i++) {
        printf "%s%06d%s\n", end[1], i, end[3]
    }
}

$ gawk -f tst.awk file
353458082243570
353458082243580
353458082462440
353458082462450
353458082069130
353458082069140
353458082246230
353458082246240
353458082559320
353458082559330
353458080153530
353458080153540
353458082462670
353458082462680
353458081943950
353458081943960
353458081719070
353458081719080
353458081392470
353458081392480
353458081392490

当使用适当的变量完成时,不需要转换。另请注意,使用上述内容,您无需重复声明相同或相对的数字来提取您关心的字符串部分,您只需声明要跳过的字符数 (8) 和要选择的数字 (6)一次。上面使用 GNU awk 作为第三个参数来匹配()。

【讨论】:

    【解决方案2】:

    问题出在脚本的while(v &lt;= t) 部分。我相信leading 0s 比赛没有正常进行。所以我在while循环中进行比较时确保它们是casted into int。 AWK 文档说您可以使用 value+0 将值转换为 int。所以我在 awk 脚本中的 while(v &lt;= t) 需要更改为 while(v+0 &lt;= t+0) 。所以下面的 AWK 脚本:

     awk -F"," '{v = substr($2,9,6); t = substr($3,9,6); while(v <= t) printf "%s%0"6"s%s,%s\n", substr($3,1,8),v++,substr($3,15,2),$4;}' TEMP.OUT.merge_range_part1_21
    

    改为:

    awk -F"," '{v = substr($2,9,6); t = substr($3,9,6); while(v+0 <= t+0) printf "%s%0"6"s%s,%s\n", substr($3,1,8),v++,substr($3,15,2),$4;}' TEMP.OUT.merge_range_part1_21
    

    唯一的改变让我得到了失败案例的预期值。例如在我的input file

    C2:0301,353458080153530,353458080153540,0;
    

    现在给我个人 IMEI:

    353458080153530,0
    353458080153540,0
    

    【讨论】:

    • 为了提高效率,而不是每次比较变量时都加零,你应该在初始化它们时加零v = substr($2,9,6)+0; t = substr($3,9,6)+0,也不应该在循环中多次调用 substr() 来获得每次都使用相同的子字符串。为什么写printf "..%0"6"s..\n" 而不仅仅是printf "..%06s..\n"
    • @EdMorton 感谢您的意见。我会合并这些。
    【解决方案3】:

    使用 if 语句检查变量 v 中的前导零,并相应地设置 y:

    awk -F"," '{v = substr($2,9,6); t = substr($3,9,6); while(v <= t) { if (substr(v,1,1)=="0") { v++;y="0"v } else { v++;y=v } ;printf %s%0"6"s%s,%s\n", substr($3,1,8),y,substr($3,15,2),$4;v=y } }' TEMP.OUT.merge_range_part1_21
    

    确保 while 条件包含在大括号中,并且 v 在 if 条件中递增。

    在语句的末尾设置 v=y 以允许它处理额外的增量。

    【讨论】:

    • 这还能用吗?它给了我很多错误。此外,您的代码仅处理 1 个0。我正在寻找任意数量的前导 0 的通用代码。所以基本上substr($2,9,6) 总是必须是六位数。如果有情况说000153000156,也应该处理
    • 它适用于您提供的示例数据,但没有考虑多个零。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-07
    • 1970-01-01
    • 2015-02-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多