Regex 命令行将行与匹配的第一个字段(sed、awk等)匹配

Regex 命令行将行与匹配的第一个字段(sed、awk等)匹配,regex,sed,awk,Regex,Sed,Awk,将文本文件中的行与匹配的第一个字段进行匹配是一种快速而简洁的方法 样本输入: a|lorem b|ipsum b|dolor c|sit d|amet d|consectetur e|adipisicing e|elit 期望输出: b|ipsum b|dolor d|amet d|consectetur e|adipisicing e|elit 期望输出,备选方案: b|ipsum|dolor d|amet|consectetur e|adipisicing|elit b|ipsum|d

将文本文件中的行与匹配的第一个字段进行匹配是一种快速而简洁的方法

样本输入:

a|lorem
b|ipsum
b|dolor
c|sit
d|amet
d|consectetur
e|adipisicing
e|elit
期望输出:

b|ipsum
b|dolor
d|amet
d|consectetur
e|adipisicing
e|elit
期望输出,备选方案:

b|ipsum|dolor
d|amet|consectetur
e|adipisicing|elit
b|ipsum|dolor
d|amet|consectetur
e|adipisicing|elit
我可以想象有很多方法来编写它,但我怀疑有一种聪明的方法可以做到这一点,例如,使用sed、awk等。我的源文件大约为0.5 GB


这里有一些相关的问题,例如,但另一个问题会将太多内容加载到内存中。我需要一个流方法

对于固定宽度字段,可以使用uniq:

如果没有固定宽度字段,这里有两种awk解决方案:

awk -F'|' '{a[$1]++;b[$1]=(b[$1])?b[$1]RS$0:$0}END{for(k in a)if(a[k]>1)print b[k]}' file
b|ipsum
b|dolor
d|amet
d|consectetur
e|adipisicing
e|elit

awk -F'|' '{a[$1]++;b[$1]=b[$1]FS$2}END{for(k in a)if(a[k]>1)print k b[k]}' file
b|ipsum|dolor
d|amet|consectetur
e|adipisicing|elit

对于固定宽度字段,可以使用uniq:

如果没有固定宽度字段,这里有两种awk解决方案:

awk -F'|' '{a[$1]++;b[$1]=(b[$1])?b[$1]RS$0:$0}END{for(k in a)if(a[k]>1)print b[k]}' file
b|ipsum
b|dolor
d|amet
d|consectetur
e|adipisicing
e|elit

awk -F'|' '{a[$1]++;b[$1]=b[$1]FS$2}END{for(k in a)if(a[k]>1)print k b[k]}' file
b|ipsum|dolor
d|amet|consectetur
e|adipisicing|elit
使用awk:

awk -F '|' '!($1 in a){a[$1]=$2; next} $1 in a{b[$1]=b[$1] FS a[$1] FS $2}
    END{for(i in b) print i b[i]}' file
d|amet|consectetur
e|adipisicing|elit
b|ipsum|dolor
使用awk:

awk -F '|' '!($1 in a){a[$1]=$2; next} $1 in a{b[$1]=b[$1] FS a[$1] FS $2}
    END{for(i in b) print i b[i]}' file
d|amet|consectetur
e|adipisicing|elit
b|ipsum|dolor

这里有一种方法,您只需记住前一行,因此需要对输入文件进行排序

awk -F \| '
    $1 == prev_key {print prev_line; matches ++}
    $1 != prev_key {                            
        if (matches) print prev_line
        matches = 0
        prev_key = $1
    }                
    {prev_line = $0}
    END { if (matches) print $0 }
' filename
交替输出

awk -F \| '
    $1 == prev_key {
        if (matches == 0) printf "%s", $1 
        printf "%s%s", FS, prev_value
        matches ++
    }             
    $1 != prev_key {
        if (matches) printf "%s%s\n", FS, prev_value
        matches = 0                                 
        prev_key = $1
    }                
    {prev_value = $2}
    END {if (matches) printf "%s%s\n", FS, $2}
' filename

这里有一种方法,您只需记住前一行,因此需要对输入文件进行排序

awk -F \| '
    $1 == prev_key {print prev_line; matches ++}
    $1 != prev_key {                            
        if (matches) print prev_line
        matches = 0
        prev_key = $1
    }                
    {prev_line = $0}
    END { if (matches) print $0 }
' filename
交替输出

awk -F \| '
    $1 == prev_key {
        if (matches == 0) printf "%s", $1 
        printf "%s%s", FS, prev_value
        matches ++
    }             
    $1 != prev_key {
        if (matches) printf "%s%s\n", FS, prev_value
        matches = 0                                 
        prev_key = $1
    }                
    {prev_value = $2}
    END {if (matches) printf "%s%s\n", FS, $2}
' filename

这可能适用于GNU sed:

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

这将在模式空间中读取2行,然后检查两行中的键是否相同。如果是,则删除第二个键并重复。如果没有,则检查第一行中是否存在两个以上的字段,如果有,则打印出来,然后将其删除,否则只删除第一行。

这可能适用于GNU-sed:

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


这将在模式空间中读取2行,然后检查两行中的键是否相同。如果是,则删除第二个键并重复。如果没有,则检查第一行中是否存在两个以上的字段,如果有,则打印出来,然后删除,否则只删除第一行。

解释为什么这是所需的输出,因为它根本不明显。e、 g.您是否正在寻找一种工具,可以将b、d和e指定为所需的键值,或者您是否正在寻找该键值在输入中出现两次或其他情况?我想将行与匹配的第一个字段合并。对不起,这不清楚。此外,输入被排序。解释为什么这是期望的输出,因为它一点也不明显。e、 g.您是否正在寻找一种工具,可以将b、d和e指定为所需的键值,或者您是否正在寻找该键值在输入中出现两次或其他情况?我想将行与匹配的第一个字段合并。对不起,这不清楚。此外,输入是经过排序的。当输入文件很大时,会有很高的内存要求。我担心awk正在将所有内容加载到内存中,然后在最后调用它;但我的担心可能是毫无根据的。我试试这个。谢谢令我惊讶的是,您的方法可以在我的0.5GB输入文件上工作。0m19.184s处理时间。时间到了$1 in a{a[$1]=$2;next}$1 in a{b[$1]=b[$1]FS a[$1]FS$2}END{fori in b print i b[i]}'INFILE>当输入文件很大时,outfile会有很高的内存需求。我担心的是awk正在将所有内容加载到内存中,然后在最后调用它;但我的担心可能是毫无根据的。我试试这个。谢谢令我惊讶的是,您的方法可以在我的0.5GB输入文件上工作。0m19.184s处理时间。时间到了$1在a{a[$1]=$2;下一个}$1在a{b[$1]=b[$1]FS a[$1]FS$2}结束{fori in b print i b[i]}infle>OutfileThank。第二个字段的长度不可预测,通常大于100个字符。顺便说一句,uniq的这些参数在MacOS和Ubuntu中都不可用。公平地说,这两个awk脚本应该可以帮到你。你确定它们在你的Ubuntu机器上不可用吗?你有什么版本的coreutils。uniq-version-uniq GNU coreutils 8.21谢谢!第二个是我真正需要的。你的方法很有效;第一次处理为0m29.103s,第二次处理为0m34.036s。谢谢。第二个字段的长度不可预测,通常大于100个字符。顺便说一句,uniq的这些参数在MacOS和Ubuntu中都不可用。公平地说,这两个awk脚本应该可以帮到你。你确定它们在你的Ubuntu机器上不可用吗?你有什么版本的coreutils。uniq-version-uniq GNU coreutils 8.21谢谢!第二个是我真正需要的。你的方法很有效;第一次处理为0m29.103s,第二次处理为0m34.036s。但是OP如何获得所需的输出呢,备选方案?您的方法非常有效,0m16.330s处理。时间awk-F\|'$1==prev_键{打印prev_行;匹配+++}$1!=prev_key{if matches print prev_line;matches=0;prev_key=$1;}{prev_line=$0}END{if matches print$0}'infle>outfile但OP如何获得所需的输出,备选方案?您的方法运行良好,0m16.330s处理。时间awk-F\|'$1==prev_键{打印prev_行;匹配+++}$1!=prev_key{if matches print prev_line;matches=0;prev_key=$1;}{prev_line=$0}END{if matches print$0}'infle>outfile我尝试了第二种方法。速度很快,但我得到了一些错误的结果。谢谢你的样品。假点击?难以置信
如果你的真实输入看起来像你的样本输入,但如果你愿意分享你的输入和你得到的不受欢迎的输出,我很乐意看一看。Ed,我无意批评,错误可能在我这边。我所能说的是,当我运行一个快速测试时,输出并不是我所期望的。我的输入比我给出的示例要复杂得多,但基本上仍然是用管道分隔两个字段的想法。我认为没有必要再深入研究这个问题了。再次感谢。我试过你的第二种方法。速度很快,但我得到了一些错误的结果。谢谢你的样品。假点击?很难相信您的真实输入是否与示例输入相似,但如果您愿意分享您的输入以及您得到的不受欢迎的输出,我很乐意看一看。Ed,我无意批评,错误可能在我这边。我所能说的是,当我运行一个快速测试时,输出并不是我所期望的。我的输入比我给出的示例要复杂得多,但基本上仍然是用管道分隔两个字段的想法。我认为没有必要再深入研究这个问题了。再次感谢,谢谢你。我已经使用了awk,但是有一个sed解决方案是很有用的;还要注意的是,你的方法对我来说不起作用,至少在我的mac电脑上,上面的测试内容不起作用。谢谢。我已经使用了awk,但是有一个sed解决方案是很有用的;还请注意,您的方法对我不起作用,至少在我的mac上,上面的测试内容是这样的。