【问题标题】:how can I match column fields and group their values together?如何匹配列字段并将它们的值组合在一起?
【发布时间】:2021-03-28 13:01:28
【问题描述】:

我正在对我使用 pdfgrep 创建的一些文件进行排序,以列出我拥有的某些 PDF 的页码。它产生了以下输出:

./Buddhism in the Shadow of Brahmanism.pdf:111:      Then, rising from his seat, covering one shoulder with his robe, the king
./Buddhism in the Shadow of Brahmanism.pdf:182:branch who has adopted the yellow robes of Buddhism; he is sur-
./Buddhism in the Shadow of Brahmanism.pdf:229:       resolve that his body, his bowl, and his monastic robe (which had been
./Buddhism in the Shadow of Brahmanism.pdf:230:robe. In this way, Mahākāśyapa (or at least his body) is to act as a sort
./Buddhism in the Shadow of Brahmanism.pdf:230:corpse to his disciples and displays to them the Buddha’s robe, and they
./Buddhism in the Shadow of Brahmanism.pdf:230:offer him the robe that the Buddha had confided to him. Only then will
./Introduction to the History of Indian Buddhism.pdf:31:the robes of a Buddhist monk in an effort to convert them, he was Sciequia. For
./Introduction to the History of Indian Buddhism.pdf:54:monks, and in particular on retreat, robes, and chastity, p. 308.—On the life of
./Introduction to the History of Indian Buddhism.pdf:97:are the Kat.hināvadāna, which deals with the bowl, the staff, and the robes of
./Introduction to the History of Indian Buddhism.pdf:111:of a sort of robe.
./Introduction to the History of Indian Buddhism.pdf:112:cover his nakedness, and who rejects all other robes as superfluous.
./Introduction to the History of Indian Buddhism.pdf:127:noon, after having taken his robe and his bowl,
./Introduction to the History of Indian Buddhism.pdf:127:bowl and his robe, he went to the place where the Cāpāla caitya6 was located,

我想要做的是将第二列上与文件名匹配的页码组合在一起,我希望输出看起来像:

./Buddhism in the Shadow of Brahmanism.pdf:111, 182, 229, 230
./Introduction to the History of Indian Buddhism.pdf:31, 54, 97, 111, 112, 127

我尝试使用 awk 解析第一个值,然后在同一个文件上使用这些结果仅打印页码,这样我就可以 grep 结果并稍后附加到文件名之后,如下所示:

awk -F : '{print $1}' parsing_file | uniq | while read line; do awk -v number="$line" -F : '$1 == "$number" { print $2 }' parsing_file; done 

但这并没有通过,我猜 uniqwhile read 可能会被删除,也许只使用带有 awk 的数组?

我在这里看到过类似的事情:

https://unix.stackexchange.com/questions/167280/awk-group-by-and-sum-column-values

但我不想总结列上的值,而只是想将它们组合在一起。

谢谢

【问题讨论】:

  • 您得到的第一个答案可能是最好的答案,也可能不是。通过立即接受它,您会阻止其他人发布答案,因此您可能永远找不到更好的方法来做您想做的事情。
  • 感谢您指出这一点,Ed。确实,您是对的,我并没有过多关注该答案的完整输出,我很高兴您以另一种可能性回答了它,即使我已经将其标记为已解决。我仍然需要更好地掌握论坛使用的机制。感谢您的耐心和时间!
  • 不客气。这部分至少很容易——提出一个问题,给它几个小时或一天的时间看看你得到什么答案(根据你的需要提供反馈/问题/cmets)然后然后接受一个你要使用的。

标签: awk


【解决方案1】:

使用您展示的示例,请尝试以下操作。用 GNU awk 编写和测试。

awk  -v OFS=":" '
match($0,/^\.\/.*\.pdf:[0-9]+/){
  value=substr($0,RSTART,RLENGTH)
  split(value,arr,":")
  if(!seen[arr[1],arr[2]]++){
    name[arr[1]]=(name[arr[1]]?name[arr[1]]", ":"")arr[2]
  }
}
END{
  for(key in name){
    print key,name[key]
  }
}
'  Input_file

您显示的示例输出如下:

./Buddhism in the Shadow of Brahmanism.pdf:111, 182, 229, 230
./Introduction to the History of Indian Buddhism.pdf:31, 54, 97, 111, 112, 127

说明:为上述添加详细说明。

awk  -v OFS=":" '                   ##Starting awk program from here.
match($0,/^\.\/.*\.pdf:[0-9]+/){    ##Using match function to match from starting ./ till .pdf : digits as per shown samples.
  value=substr($0,RSTART,RLENGTH)   ##Creating value with matched sub string here.
  split(value,arr,":")              ##Splitting value into array arr with : delimiter.
  if(!seen[arr[1],arr[2]]++){
     name[arr[1]]=(name[arr[1]]?name[arr[1]]", ":"")arr[2]  ##Creating name array with index of book name and its value it digits as per needed output.
  }
}
END{                                ##Starting END block of this program from here.
  for(key in name){                 ##Traversing through name here.
    print key,name[key]             ##Printing key and array value here.
  }
}
' Input_file                        ##Mentioning Input_file name here.

注意:之前的上述解决方案没有处理同一段落的重复数字,因此我编辑了解决方案以在 Ed 回答后处理这种情况。

【讨论】:

  • 在这种情况下,我绝不建议您更改 SUBSEP(但将其设置为与 OFS 相同的值并没有什么问题,例如在方便存储和打印复合索引时 - arr[$1,$2,$3]=7;... for (i in arr) print i, arr[i] )。我在那一点上说的是您已经将字符串存储在value 中,其中包含与arr[1] ":" arr[2] 相同的字符串,因此您可以使用value 作为索引,而不是创建一个几乎相同的新复合索引arr[1] SUBSEP arr[2].
  • 如果 OP 真实数据的标题中有 :s,那么您的 split(value,arr,":") 将失败,就像将 FS 设置为 : 会在标题中出现 : 一样(或者,不太可能,页码)。如果 OP 真实数据的标题与您的正则表达式不匹配(例如,它们不是 PDF),那么他们很可能希望以与示例输入中完全相同的方式处理它们。如果他们的输入行看起来不像那样,那么他们必须在问题中提供该信息,以便我们知道如何处理它们。
  • @EdMorton,先生,当然得分了,谢谢您一如既往的好建议,我会努力向他们学习,干杯,注意安全,保持健康先生。
  • 不客气。但是,主要问题是如果不将您的答案变成我的答案就无法解决,那就是将所有数据存储在内存中,然后在 END 部分循环打印,这对于解决这些类型的问题总是更糟糕的方法在内存使用和效率方面比在每次键值更改时对键值上的输入进行排序只是打印数据。
  • 很公平 - 如果您可以提前对输入进行排序以便一次打印一个键,那么您应该这样做,但如果由于某种原因您无法对其进行排序,那么您必须至少存储所有在内存中的输出用于在 END 中输出。
【解决方案2】:
$ cat tst.awk
BEGIN { FS=OFS=":" }
$1 != title {
    if ( title != "" ) {
        print title, pages
    }
    title = $1
    pages = $2
    delete seen
    next
}
!seen[$2]++ {
    pages = pages ", " $2
}
END {
    print title, pages
}

$ awk -f tst.awk file
./Buddhism in the Shadow of Brahmanism.pdf:111, 182, 229, 230
./Introduction to the History of Indian Buddhism.pdf:31, 54, 97, 111, 112, 127

在我写这篇文章时(在那个答案被接受之后),上述内容和@Ravinder's answer 之间的区别是:

  1. 这取决于您的输入按标题和页码排序,如您问题中的示例所示,而 Ravinders 没有。如果您的真实数据未排序,请运行sort -t':' -k1,1 -k2,2n file | awk ...'
  2. 这不会将所有标题和页码读入内存,它只会一次读取 1 个标题的页码,因此无论您的输入文件有多大,它都能正常工作。
  3. 这将按照它们在输入中出现的顺序而不是随机顺序输出标题。
  4. 这会通过仅输出每个标题的唯一页码而不是相同的页码来产生预期的输出 每次出现在输入中时都会输出页码(例如 111, 182, 229, 230111, 182, 229, 230, 230, 230
  5. 这会产生预期的输出,方法是在标题和第一个页码之间放置 : 而不是空白。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-10-07
    • 1970-01-01
    • 1970-01-01
    • 2021-10-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多