【问题标题】:Combine lines with matching first field将行与匹配的第一个字段合并
【发布时间】:2018-03-25 20:49:04
【问题描述】:

几年来,我经常需要将(排序的)文本行与匹配的第一个字段组合起来,但我从未找到一种优雅的(即单行 unix 命令行)方法来做到这一点。我想要的类似于 unix join 命令可能实现的功能,但 join 需要 2 个文件,每个键最多出现一次。我想从一个文件开始,其中一个键可能出现多个图块。

我有一个 ruby​​ 和 perl 脚本来执行此操作,但没有办法将我的算法缩短为单行。经过多年的 unix 使用,我仍在学习 commpasteuniq 等新技巧,我怀疑有一种聪明的方法可以做到这一点。

有一些相关的问题,比如join all lines that have the same first column to the same lineCommand line to match lines with matching first field (sed, awk, etc.);和Combine lines with matching keys——但这些解决方案从来没有真正提供干净可靠的解决方案。

这里是示例输入:

apple:A fruit
apple:Type of: pie
banana:tropical fruit
cherry:small burgundy fruit
cherry:1 for me to eat
cherry:bright red

这是示例输出:

apple:A fruit;Type of: pie
banana:tropical fruit
cherry:small burgundy fruit;1 for me to eat;bright red

这是我理想的语法:

merge --inputDelimiter=":" --outputDelimiter=";" --matchfield=1 infile.txt

“匹配字段”实际上是可选的。它可能永远是第一个领域。分隔符的后续出现应视为纯文本。

如果您能想到一个简短而优雅的算法,我不介意 perl、ruby、awk 单行。这应该能够处理数百万行输入。有任何想法吗?

【问题讨论】:

    标签: unix join awk command-line bsd


    【解决方案1】:

    使用 awk 一个衬垫

    awk -F: -v ORS="" 'a!=$1{a=$1; $0=RS $0} a==$1{ sub($1":",";") } 1' file
    

    输出:

    apple:A fruit;Type of: pie
    banana:tropical fruit
    cherry:small burgundy fruit;1 for me to eat;bright red
    

    设置ORS="";默认为\n
    我们之所以设置ORS=""(输出记录分隔符)是因为我们不希望 awk 在每条记录的末尾包含换行符。我们想通过我们自己的逻辑以我们自己的方式来处理它。实际上,我们在每条记录的开头都包含换行符,它的第一个字段与前一个字段不同。

    a!=$1:当变量a(最初为null)与第一个字段$1不匹配时,例如。 apple在第一行,然后设置a=$1$0=RS $0$0或简单地whole record变为"\n"$0(基本上在记录的开头添加换行符)。当第一个字段 ($1) 与前一行的 $1 不同时,a!=$1 将始终满足,因此这是根据第一个字段分离我们的记录的标准。

    a==$1:如果匹配,则可能意味着您正在迭代属于前一个记录集的记录。在这种情况下,将第一次出现的 $1: (注意 : )替换为例如。 apple:;$1":" 也可以写成$1FS 其中FS is :

    如果您的文件中有数百万行,那么这种方法将是最快的,因为它不涉及任何预处理,而且我们没有使用任何其他数据结构,例如数组来存储您的密钥或记录。

    【讨论】:

    • 感谢您的精彩解释。
    • @MichaelD:欢迎迈克尔。
    【解决方案2】:
    for F in `cut -f1 -d ':' infile.txt | sort | uniq`; do echo "$F:$(grep $F infile.txt | cut -f2- -d ':' | paste -s -d ';' - )"; done
    

    不确定它是否符合“优雅”的条件,但它可以工作,尽管我确信数百万行的速度不会很快 - 随着grep 调用数量的增加,它会显着减慢。您希望有多少百分比的匹配字段是唯一的?

    【讨论】:

    • 感谢 unix 字符串。我预计一个键/匹配字段大约有 1-5 次重复,因此在一百万行中,可能有 300k 个键。
    • 啊,300k grep 调用是不合理的。感谢您的反馈
    • 不过,这仍然是 unix 哲学的一个很好的例子。
    【解决方案3】:

    发现 awk 语言:

    awk -F':' '{ v=substr($0, index($0,":")+1); a[$1]=($1 in a? a[$1]";" : "")v }
               END{ for(i in a) print i,a[i] }' OFS=':' infile.txt
    

    输出:

    apple:A fruit;Type of: pie
    banana:tropical fruit
    cherry:small burgundy fruit;1 for me to eat;bright red
    

    【讨论】:

    • 感谢@RomanPerekhrest,这行得通。比我过去尝试过的其他一些 awk 解决方案更好,这些解决方案会在复杂的线路上中断。也就是说,我仍然喜欢一个更短、语法更简单的命令,但我很高兴有一个单行。
    【解决方案4】:

    我认为这个可以完成这项工作

     awk -F':' '$1!=a{if(b);print b;b=""}a=$1{$1="";if(!b)b=a;b=b$0}END{print b}' infile
    

    【讨论】:

    • 你能解释一下吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-03-28
    • 2021-08-19
    • 2014-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-07
    相关资源
    最近更新 更多