选择性地从sed中的CSV文件中删除子字段

时间:2015-09-16 14:40:47

标签: regex sed

我有一个CSV文件my.csv,其中的字段由;分隔。每个字段包含由|分隔的任意数量(有时为零)的子字段,如下所示:

aa5|xb1;xc3;ba7|cx2|xx3|da2;ed1
xa2|bx9;ab5;;af2|xb5
xb7;xa6|fc5;fd6|xb5;xc3|ax9
df3;ab5|xc7|de2;da5;ax2|xd8|bb1

我想删除除|以外的所有内容的所有子字段(包含相应的x),即得到如下输出:

xb1;xc3;xx3;
xa2;;;xb5
xb7;xa6;xb5;xc3
;xc7;;xd8

现在我使用sed

分多步执行此操作
sed -i 's/^[^;x]*;/;/g' my.csv #In 1st fields without x.
sed -i 's/;[^;x]*;/;;/g' my.csv #In middle field without x.
sed -i 's/;[^;x]*$/;/g' my.csv #In last field without x.
sed -i 's/^[^;x][^;]*|x/x/g' my.csv #In 1st fields with x. before x.
sed -i 's/;[^;x][^;]*|x/;x/g' my.csv #In non-1st fields with x. before x.
sed -i 's/|[^x][^;]*//g' my.csv #In fields with x. after x.

有没有办法做到一行或至少更简单?我遇到了如何匹配“行开始OR';'”的问题。

在我的情况下,保证不会有多个以x开头的子字段。然而,从理论上讲,如果不是这样,如何解决问题也是有用的(例如,将字段ab1|xa2|bc3|xd4|ex5转换为xa2|xd4)。

4 个答案:

答案 0 :(得分:1)

使用sed

sed ':;s/\(^\||\|;\)[^x;|][^;|]*/\1/;t;s/|//g' file

只需循环删除不以x开头的字段,然后删除条形码。

输出

xb1;xc3;xx3;
xa2;;;xb5
xb7;xa6;xb5;xc3
;xc7;;xd8

答案 1 :(得分:1)

你可以使用这个awk:

awk 'BEGIN{FS=OFS=";"} {for (i=1; i<=NF; i++) {
      gsub(/(^|\|)[^x][^|]*/, "", $i); sub(/^\|/, "", $i)}} 1' file
xb1;xc3;xx3;
xa2;;;xb5
xb7;xa6;xb5;xc3
;xc7;;xd8

这也会将ab1|xa2|bc3|xd4|ex5转换为xa2|xd4,即从<{1}}开始的多个字段

答案 2 :(得分:1)

考虑使用Perl:

perl -ple '$_ = join(";", map { join "|", grep /^x/, split /\|/ } split(/;/, $_, -1))'

这从split(/;/, $_, -1)开始,将行(此时为$_)拆分为以分号分隔的字段数组。负限制参数使得尾随空字段(如果存在)不会被丢弃。

该数组的元素是

  1. map表达式和
  2. 中进行了转换
  3. join再次使用分号。
  4. map表达式中的转换是

    1. split沿着|
    2. grep ping /^x/(即删除那些与正则表达式不匹配的内容),
    3. join再次使用|
    4. 我认为这种结构化方法比正则表达式魔法更强大。

      在一行末尾丢失空字段的旧代码:

      perl -F\; -aple '$_=join(";", map { join("|", grep(/^x/, split(/\|/, $_))) } @F)'
      

      这使用-a进行自动拆分,看起来更好但没有对所需的字段拆分进行细粒度控制。

答案 3 :(得分:0)

要求救援!

awk -F";" -vOFS=";" '
       {line=sep="";
        for(i=1;i<=NF;i++) {
            c=split($i,s,"|"); 
            for(j=1;j<=c;j++) 
               if(s[j]~/^x/) {
                   line=line sep s[j];
                   sep=OFS
               }
        } 
        print line}'

进一步拆分每个元素进行模式检查,将结果组合成一行,在每一行设置第一个元素后设置分隔符。