【问题标题】:Numeric expression in if condition of awkawk 的 if 条件中的数值表达式
【发布时间】:2016-03-01 19:13:39
【问题描述】:

AWK 编程的新手。我有一个 file1 条目为:

15>000000513609200>000000513609200>B>I>0011>>238/PLMN/000100>File Ef141109.txt>0100-75607-16156-14 09-11-2014
15>000000513609200>000000513609200>B>I>0011>Danske Politi>238/PLMN/000200>>0100-75607-16156-14 09-11-2014
15>000050354428060>000050354428060>B>I>0011>Danske Politi>238/PLMN/000200>>4100-75607-01302-14 31-10-2014

我想写一个 awk 脚本,如果从第 3 个字段中减去的第 2 个字段是 0,那么它会打印第 2 个字段。否则,如果 (difference > 0),那么它会打印从第 2 个开始加 1 的所有中间数字在第 3 个字段结束的字段。不会有第三场小于第二场的情况。所以忽略那个条件。

我正在做的事情是:

 awk 'NR > 2 { print p } { p = $0 }' file1 | awk -F">" '{if ($($3 - $2) == 0) print $2; else l = $($3 - $2); for(i=0;i<l;i++) print $2++; }'

((有人告诉我 awk 在语法上接近 C ))

但在我看来,字符串到数字或数字到字符串的转换并没有在正确的时间发生在正确的位置。不是应该由 AWK 自动处理吗?

我得到的输出:

513609200
513609201
513609200

这并不像预期的那样安静。一个明显的问题是它忽略了前面的 0。

请帮助我修改 AWK 脚本以获得所需的结果。

注意:

awk 'NR &gt; 2 { print p } { p = $0 }' file1 只是删除我原始文件1 中的第一个也是最后一个条目。所以需要修复的部分是:

awk -F">" '{if ($($3 - $2) == 0) print $2; else l = $($3 - $2); for(i=0;i<l;i++) print $2++; }'

【问题讨论】:

  • @anubhava 如果第 3 和第 2 字段的差异为 0,则完全按原样打印第 2 字段。如果第 3 个字段大于第 2 个字段,则从第 2 个字段开始打印,每次递增 1,一直打印到第 3 个字段。数字应完全以 15 位格式打印(请参阅我的 fie 内容)
  • 我会尽力理解您的解释,但显示准确的预期输出数据会更清楚。
  • else 后面有 2 个语句,但没有大括号。不管怎样,for 语句正在执行。
  • 是的,awk 是close to C in terms of syntax,但这绝对不意味着你可以假设它是 C 语法,因为如果你这样做了,即使你设法编写了一个在那里执行并产生预期输出的脚本如果您学会了如何以惯用方式使用 awk,那么几乎肯定会是一种更好的方法。获取 Arnold RObbins 所著的《Effective Awk Programming, 4th Edition》一书。
  • @glennjackman 抱歉,刚刚重新访问了您的评论。无需将v 更改为$v。我现在知道区别了。但仍然存在一个问题。无论 AWK 对变量 v 执行什么数字处理,它都会自动删除前导 0。但我想要前导 0,因为它是最后一个字符串。如何执行此操作。我的意思是:输入:1,004402146016260,004402146016300 输出:1,44021460162601,4402146016270.....,1,4402146016300 预期输出:1,0044021460162601,004402146016270.....,1,004402146016300。请注意,可以有任意数量的前导 0,而不仅仅是 2。

标签: regex linux bash shell awk


【解决方案1】:

在 awk 中,将$ 视为一个运算符,以检索命名字段编号($0 是一种特殊情况)

  • $1 是字段 1 的值
  • $NFNF 变量中给定字段的值

因此,$($3 - $2) 将尝试获取表达式 ($3 - $2) 给出的字段编号 的值。

您需要更少的$ 标志

awk -F">" '{
    if ($3 == $2) 
        print $2
    else {
        v=$2
        while (v < $3) 
            print v++
    }
}'

【讨论】:

  • FWIW 我倾向于认为$ 就像一个包含所有字段值的数组。所以$1 就像array_of_fields[1]
  • @glenn jackman 谢谢。它对我有用,我只需将 while (v&lt;$3) 更改为 while($v&lt;$3)print $v++
  • 您是否也将v=$2 更改为v=2?否则你错过了我的意思。避免在计算中使用 $ 也更有效:每次你做 $v++ awk 重新分配 $0
  • @glennjackman 抱歉,刚刚重新查看了您的评论,发现我弄错了。无需将v 更改为$v。我现在知道区别了。但仍然存在一个问题。无论 AWK 对变量 v 做什么数字处理,它都会自动删除前导 0。但是我想要前导 0,因为它是最后一个字符串,因为我稍后在我的脚本中将它用于字符串匹配。如何强制执行此操作,以使前导 0 不会自动删除。我的意思是:
  • 你想要像printf "%015d\n", v 这样的东西——使用你喜欢的宽度代替 15,但保留前导零。
【解决方案2】:

通常,这会起作用,但您的数字超出了awk 整数范围,因此您需要另一种解决方案来处理它们。我发布此内容是为了启动其他解决方案并更好地说明您的规范。

$ awk -F'>' '{for(i=$2;i<=$3;i++) print i}' file

请注意,这将跳过您认为不可能发生的行

一个小规模的例子

$ cat file_0
x>1000>1000>etc
x>2000>2003>etc
x>3000>2999>etc

$ awk -F'>' '{for(i=$2;i<=$3;i++) print i}' file_0
1000
2000
2001
2002
2003

显然,gawk 的较新版本具有任意精度整数的 --bignum 选项,如果您有一个可以解决您的问题但我无权验证的兼容版本。

【讨论】:

    【解决方案3】:

    对于无法通过 bigint 支持访问 gawk 的任何人,如果需要某种“大整数”支持,考虑其他选项可能会更简单。由于 ruby​​ 具有类似 awk 的操作模式, 让我们在这里考虑红宝石。

    要开始,只需记住四件事:

    1. 使用 -n 和 -a 选项调用 ruby​​(-n 用于类似 awk 的循环;-a 用于将行自动解析为字段 ($F[i]));
    2. awk 的 $n 变成 $F[n-1];
    3. 需要将数字字符串显式转换为整数;
    4. 要指定要在命令行上执行的行,请使用“-e TEXT”选项。

    因此直接翻译为:

    awk -F'>' '{for(i=$2;i<=$3;i++) print i}' file
    

    应该是:

    ruby -an -F'>' -e '($F[1].to_i .. $F[2].to_i).each {|i| puts i }' file
    

    为了防止出现空行,下面的脚本会稍微好一点:

    ($F[1].to_i .. $F[2].to_i).each {|i| puts i } if $F.length > 2
    

    这可以像上面那样调用,或者如果脚本在一个文件中(比如 script.rb),使用咒语:

    ruby -an -F'>' script.rb file
    

    给定OP输入数据,输出为:

    513609200
    513609200
    50354428060
    

    左填充可以通过多种方式完成——例如参见this SO page

    【讨论】:

      猜你喜欢
      • 2011-08-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-05-29
      • 2020-09-20
      • 1970-01-01
      • 2015-03-19
      相关资源
      最近更新 更多