【问题标题】:Print sorted output with awk to avoid pipe sort command使用 awk 打印排序的输出以避免管道排序命令
【发布时间】:2020-09-25 05:16:36
【问题描述】:

我正在尝试匹配包含 (123) 的行,然后将字段 2 替换为 x+ 的空格,这将给出 4 列。然后将第 3 列的顺序更改为第 4 列。 最终打印按第 3 列排序,然后按第 4 列排序。

我可以通过这种方式在awk输出之后得到输出管道sort命令。

$ echo "
0:   1920x1663+0+0     kpwr(746)
323: 892x550+71+955    kpwr(746)
211: 891x550+1003+410  kpwr(746)
210: 892x451+71+410    kpwr(746)
415: 891x451+1003+1054 kpwr(746)
1:   894x532+70+330    kpwr(123)
324: 894x532+1001+975  kpwr(123)
2:   894x631+1001+330  kpwr(123)
212: 894x631+70+876    kpwr(123)
61:  892x1+71+375      kpwr(0)
252: 892x1+71+921      kpwr(0)" | 
awk '/\(123\)/{b = gensub(/(.+)x(.+)\+(.+)\+(.+)/, "\\1 \\2 \\4 \\3", "g", $2); print b}' |  
sort -k3 -k4 -n

894 532 330 70
894 631 330 1001
894 631 876 70
894 532 975 1001

我怎样才能只使用 awk 而不需要管道 sort 来获得相同的输出?感谢您的帮助。

【问题讨论】:

  • 你最好通过管道发送到sortawk 没有任何出色的排序能力,因为 awk 中的数组是关联的。 awk 是根据需要操作列数据的正确工具,但 sort 是之后重新排序行的正确工具。

标签: awk


【解决方案1】:

您可以从awk (gnu) 本身获取它:

awk '/\(123\)/{
   $2 = gensub(/(.+)x(.+)\+(.+)\+(.+)/, "\\1 \\2 \\4 \\3", "g", $2)
   split($2, a)           # split by space and store into array a
   # store array by index 3 and 4
   rec[a[3]][a[4]] = (rec[a[3]][a[4]] == "" ? "" : rec[a[3]][a[4]] ORS) $2
}
END { 
   PROCINFO["sorted_in"]="@ind_num_asc" # sort by numeric key ascending
   for (i in rec)         # print stored array rec
      for (j in rec[i])
         print rec[i][j]
}' file
894 532 330 70
894 631 330 1001
894 631 876 70
894 532 975 1001

【讨论】:

  • 只是我的眼睛还是那个代码字体变成了斜体?
  • 其实一开始我以为只有我的眼睛才能看到斜体:) 但我想知道为什么?
  • 啊,不,这是语法高亮错误地将您的 awk 代码标识为带有 HLJS 强调的东西。一个不同的代码标签将解决这个问题。
  • 非常感谢@MartijnPieters 我以前从未在代码标签中使用过none
【解决方案2】:

你能处理 GNU awk 吗?:

$ gawk '
BEGIN {
    PROCINFO["sorted_in"]="@val_num_asc"              # for order strategy
}
/\(123\)$/ {                                          # pick records
    split($2,t,/[+x]/)                                # split 2nd field
    if((t[4] in a) && (t[3] in a[t[4]])) {            # if index collision
        n=split(a[t[4]][t[3]],u,ORS)                  # split stacked element
        u[n+1]=t[1] OFS t[2] OFS t[4] OFS t[3]        # add new data
        delete a[t[4]][t[3]]                          # del before rebuilding
        for(i in u)                                   # sort on whole record
            a[t[4]][t[3]]=a[t[4]][t[3]] ORS u[i]      # restack to element
    } else
        a[t[4]][t[3]]=t[1] OFS t[2] OFS t[4] OFS t[3] # no collision, just add
}
END {
    PROCINFO["sorted_in"]="@ind_num_asc"              # strategy on output

    for(i in a)           
        for(j in a[i])
            print a[i][j]
}' file

输出:

894 532 330 70
894 631 330 1001
894 631 876 70
894 532 975 1001

使用碰撞数据,例如:

1:   894x532+70+330    kpwr(123)  # this 
1:   123x456+70+330    kpwr(123)  # and this, notice order
324: 894x532+1001+975  kpwr(123)
2:   894x631+1001+330  kpwr(123)
212: 894x631+70+876    kpwr(123)

输出将是:

123 456 330 70                    # ordered by the whole record when collision
894 532 330 70
894 631 330 1001
894 631 876 70
894 532 975 1001

【讨论】:

  • @JamesBrown 感谢您的解决方案和解释,这对理解代码有很大帮助。它可以显示正确的输出。
  • 您之前添加1: 123x456+70+330 kpwr(123) 的代码在输出中给了我重复的行。我看到您修改了代码,现在输出正确。非常感谢您让我们知道这个问题。
  • 是的,我忘记在重建之前重置a[][] 中的元素。对此感到抱歉。
  • 没问题。非常感谢你的帮助。我不确定哪种解决方案会更好,因为我不是专家。我选择了anubhava,因为它更短。那是我的标准。谢谢
【解决方案3】:

我几乎完成了写作,我的解决方案与@anubhava 的相同,因此对他的解决方案进行了一些调整 :) 这个将在这里处理多行相同的值。

awk '
BEGIN{
  PROCINFO["sorted_in"]="@ind_num_asc"
}
/\(123\)/{
   $2 = gensub(/(.+)x(.+)\+(.+)\+(.+)/, "\\1 \\2 \\4 \\3", "g", $2)
   split($2, a," ")
   arr[a[3]][a[4]] = (arr[a[3]][a[4]]!=""?arr[a[3]][a[4]] ORS:"")$2
}
END { 
   for (i in arr){
      for (j in arr[i]){ print arr[i][j] }
   }
}' Input_file

【讨论】:

  • 非常感谢您提供处理具有相同值的多行的解决方案和功能。
  • Ravinder 非常适合处理同一个键的多个值。
  • 只是一个小点,val == "" 更好地检查awk 中的空字符串。如果 val 等于 0,则仅检查 val 会返回 false。
  • @anubhava,当然,先生,现在看起来不错,先生,请确认一次。
猜你喜欢
  • 1970-01-01
  • 2013-12-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-04
  • 2018-01-16
  • 2011-07-15
相关资源
最近更新 更多