使用UNIX命令基于起始值连接行

时间:2014-08-30 16:35:11

标签: unix awk sed grep

这里我再次提出了另一个UNIX要求(因为我在UNIX中的知识仅限于基本命令)。

我有一个看起来像这样的文件(并且有大约3000万行)

123456789012,PID=1,AID=2,EQOSID=1,PDPTY=IPV4,PDPCH=2-0
123456789012,PID=2,AID=1,EQOSID=1,PDPTY=IPV4,PDPCH=2-0
123456789012,PID=3,AID=8,EQOSID=1,PDPTY=IPV4,PDPCH=2-0
234567890123,PID=2,AID=1,EQOSID=1,PDPTY=IPV4,PDPCH=2-0
234567890123,PID=3,AID=8,EQOSID=1,PDPTY=IPV4,PDPCH=2-0
345678901234,PID=1,AID=2,EQOSID=1,PDPTY=IPV4,PDPCH=2-0
345678901234,PID=2,AID=1,EQOSID=1,PDPTY=IPV4,PDPCH=2-0
345678901234,PID=3,AID=8,EQOSID=1,PDPTY=IPV4,PDPCH=2-0
456789012345,PID=2,AID=1,EQOSID=1,PDPTY=IPV4,PDPCH=2-0
567890123456,PID=2,AID=1,EQOSID=1,PDPTY=IPV4,PDPCH=2-0
567890123456,PID=3,AID=8,EQOSID=1,PDPTY=IPV4,PDPCH=2-0

最终输出应该是这样的(没有在连接部分重复的第一个值)

123456789012,PID=1,AID=2,EQOSID=1,PDPTY=IPV4,PDPCH=2-0,PID=2,AID=1,EQOSID=1,PDPTY=IPV4,PDPCH=2-0,PID=3,AID=8,EQOSID=1,PDPTY=IPV4,PDPCH=2-0
234567890123,PID=2,AID=1,EQOSID=1,PDPTY=IPV4,PDPCH=2-0,PID=3,AID=8,EQOSID=1,PDPTY=IPV4,PDPCH=2-0
345678901234,PID=1,AID=2,EQOSID=1,PDPTY=IPV4,PDPCH=2-0,PID=2,AID=1,EQOSID=1,PDPTY=IPV4,PDPCH=2-0,PID=3,AID=8,EQOSID=1,PDPTY=IPV4,PDPCH=2-0
456789012345,PID=2,AID=1,EQOSID=1,PDPTY=IPV4,PDPCH=2-0
567890123456,PID=2,AID=1,EQOSID=1,PDPTY=IPV4,PDPCH=2-0,PID=3,AID=8,EQOSID=1,PDPTY=IPV4,PDPCH=2-0

但是,如果上面的输出有点复杂,那么下面的输出也可以。因为我可以将文件加载到Oracle11g中并删除冗余列。

123456789012,PID=1,AID=2,EQOSID=1,PDPTY=IPV4,PDPCH=2-0,123456789012,PID=2,AID=1,EQOSID=1,PDPTY=IPV4,PDPCH=2-0,123456789012,PID=3,AID=8,EQOSID=1,PDPTY=IPV4,PDPCH=2-0
234567890123,PID=2,AID=1,EQOSID=1,PDPTY=IPV4,PDPCH=2-0,234567890123,PID=3,AID=8,EQOSID=1,PDPTY=IPV4,PDPCH=2-0
345678901234,PID=1,AID=2,EQOSID=1,PDPTY=IPV4,PDPCH=2-0,345678901234,PID=2,AID=1,EQOSID=1,PDPTY=IPV4,PDPCH=2-0,345678901234,PID=3,AID=8,EQOSID=1,PDPTY=IPV4,PDPCH=2-0
456789012345,PID=2,AID=1,EQOSID=1,PDPTY=IPV4,PDPCH=2-0
567890123456,PID=2,AID=1,EQOSID=1,PDPTY=IPV4,PDPCH=2-0,567890123456,PID=3,AID=8,EQOSID=1,PDPTY=IPV4,PDPCH=2-0

5 个答案:

答案 0 :(得分:2)

使用awk就足够了;这是各种各样的控制中断报告。由于具有相同键的行被组合在一起 - 这是非常重要的一点 - 它非常简单。

awk -F, '{   if ($1 != saved)
             {
                 if (saved != 0) print saved "," list
                 saved = $1
                 list = ""
             }
             pad = ""
             for (i = 2; i <= NF; i++) { list = list pad $i; pad = "," }
         }
         END { if (saved != 0) print saved, list }'

您可以将数据作为标准输入提供,也可以列出最终单引号后要处理的文件。

示例输出:

123456789012,PID=1,AID=2,EQOSID=1,PDPTY=IPV4,PDPCH=2-0PID=2,AID=1,EQOSID=1,PDPTY=IPV4,PDPCH=2-0PID=3,AID=8,EQOSID=1,PDPTY=IPV4,PDPCH=2-0
234567890123,PID=2,AID=1,EQOSID=1,PDPTY=IPV4,PDPCH=2-0PID=3,AID=8,EQOSID=1,PDPTY=IPV4,PDPCH=2-0
345678901234,PID=1,AID=2,EQOSID=1,PDPTY=IPV4,PDPCH=2-0PID=2,AID=1,EQOSID=1,PDPTY=IPV4,PDPCH=2-0PID=3,AID=8,EQOSID=1,PDPTY=IPV4,PDPCH=2-0
456789012345,PID=2,AID=1,EQOSID=1,PDPTY=IPV4,PDPCH=2-0
567890123456 PID=2,AID=1,EQOSID=1,PDPTY=IPV4,PDPCH=2-0PID=3,AID=8,EQOSID=1,PDPTY=IPV4,PDPCH=2-0

代码使用saved来跟踪它正在累积的键列值。当键列更改时,打印出保存的值(如果有)并重置新的行集。最后,打印出保存的值(如果有的话)。因此,代码可以优雅地处理空文件。


Perl选项

#!/usr/bin/env perl
use strict;
use warnings;
my $saved = "";
my $list;
while (<>)
{
    chomp;
    my($key,$value) = ($_ =~ m/^([^,]+)(,.*)/);
    if ($key ne $saved)
    {
        print "$saved$list\n" if $saved;
        $saved = $key;
        $list = "";
    }
    $list .= $value;
}
print "$saved$list\n" if $saved;

或者,如果你真的想,你可以保存写循环(并使用严格和警告):

perl -n -e 'chomp;
($key,$value) = ($_ =~ m/^([^,]+)(,.*)/);
if ($key ne $saved)
{
    print "$saved$list\n" if $saved;
    $saved = $key;
    $list = "";
}
$list .= $value;
} END {
print "$saved$list\n" if $saved;'

这可能会被压缩成一条(相当长的)线。 } END {是一块Perl怪异的东西; -n选项创建一个循环while (<>) { … }并将-e参数中的脚本插入其中,因此}中的} END {将终止该循环,然后创建一个END块,由Perl提供的}结束。是,记录和支持;是的,非常奇怪(所以我不会这样做;我使用首先显示的Perl脚本)。

答案 1 :(得分:2)

这个awk脚本做你想要的:

BEGIN { FS = OFS = "," }
NR == 1 { a[++n] = $1 }
a[1] != $1 { for(i=1; i<=n; ++i) printf "%s%s", a[i], (i<n?OFS:ORS); n = 1 }
{ a[1] = $1; for(i=2;i<=NF;++i) a[++n] = $i }
END { for(i=1; i<=n; ++i) printf "%s%s", a[i], (i<n?OFS:ORS) }

它将所有具有相同第一列的字段存储在数组中。当第一列不同时,它会打印出数组的所有元素。像awk -f join.awk file一样使用它。

输出:

123456789012,PID=1,AID=2,EQOSID=1,PDPTY=IPV4,PDPCH=2-0,PID=2,AID=1,EQOSID=1,PDPTY=IPV4,PDPCH=2-0,PID=3,AID=8,EQOSID=1,PDPTY=IPV4,PDPCH=2-0
234567890123,PID=2,AID=1,EQOSID=1,PDPTY=IPV4,PDPCH=2-0,PID=3,AID=8,EQOSID=1,PDPTY=IPV4,PDPCH=2-0
345678901234,PID=1,AID=2,EQOSID=1,PDPTY=IPV4,PDPCH=2-0,PID=2,AID=1,EQOSID=1,PDPTY=IPV4,PDPCH=2-0,PID=3,AID=8,EQOSID=1,PDPTY=IPV4,PDPCH=2-0
456789012345,PID=2,AID=1,EQOSID=1,PDPTY=IPV4,PDPCH=2-0
567890123456,PID=2,AID=1,EQOSID=1,PDPTY=IPV4,PDPCH=2-0,PID=3,AID=8,EQOSID=1,PDPTY=IPV4,PDPCH=2-0

答案 2 :(得分:1)

以下是一些Python选项,如果你决定走这条路线......首先将适用于多个输入文件和非顺序相同的索引。其次,它不会将整个文件读入内存。

(注意,我知道这不是惯例,但我故意使用UpperCase作为变量来明确什么是用户定义的变量以及什么是特殊的python单词。)

#! /usr/bin/env python
# -*- coding: utf-8 -*-

"""
concatenate comma-separated values based on first value

Usage: 
   catfile.py *.txt > output.dat

"""
import sys

if len(sys.argv)<2:
    sys.stderr.write(__doc__)
else:
    FileList = sys.argv[1:]
    IndexList = []
    OutDict = {}
    for FileName in FileList:
        with open(FileName,'rU') as FStream:
            for Line in FStream:
                if Line:
                    Ind,TheRest = Line.rstrip().split(",",1)
                    if Ind not in IndexList:
                        IndexList.append(Ind)
                    OutDict[Ind] = OutDict.get(Ind,"") + "," + TheRest

    for Ind in IndexList:
        print Ind + OutDict[Ind]

这是一个不同的版本,它不会将整个文件加载到内存中,但要求所有相同的索引都按顺序出现,并且它只在一个文件上运行:

#! /usr/bin/env python
# -*- coding: utf-8 -*-
"""
concatenate comma-separated values based on first value

Usage: 
   catfile.py *.txt > output.dat

"""
import sys

if len(sys.argv)<2:
    sys.stderr.write(__doc__)
else:
    FileName = sys.argv[1]
    OutString = ''
    PrevInd = ''
    FirstLine = True
    with open(FileName,'rU') as FStream:
        for Line in FStream:
            if "," in Line:
                Ind,TheRest = Line.rstrip().split(",",1)
                if Ind != PrevInd:
                    if not FirstLine:
                        print PrevInd+OutString
                    PrevInd = Ind
                    OutString = TheRest
                    FirstLine = False
                else:
                    OutString += ","+TheRest
        print Ind + OutString

更一般地说,您可以通过将其保存为catfile.py然后执行python catfile.py inputfile.txt > outputfile.txt来运行它们。或者对于长期解决方案,创建一个scripts目录,将其添加到$PATH,使用chmod u+x catfile.py使其可执行,然后您只需从任何目录中键入脚本的名称即可。但这是你想要研究的另一个话题。

答案 3 :(得分:0)

没有数组的方式:

BEGIN { FS = OFS = "," ; ORS = "" }
{
    if (lid == $1) { $1 = "" ; print $0 }
    else { print sep $0 ; lid = $1 ; sep = "\n" }
}
END { if (NR) print }

注意:如果您最后不需要换行符,请删除END块。

答案 4 :(得分:0)

这可能适合你(GNU sed):

sort file | sed -r ':a;$!N;s/^(([^,]*),.*)\n\2/\1/;ta;P;D'

对文件进行排序(如果需要),然后删除重复项出现的换行符和键。