2017-07-28 117 views
0

我有以下制表符分隔表:击:集合论

A B C D E F G H I J 
ZO1  X1 X2 X3   X4  X5 X6 
ZO2 X7 X8 X9 X10  X11 X12 X13 X14 X15 
ZO3 X16 X17 X18 X19   X20  X21 X22 
ZO4  X23 X24 X25   X26  X27 X28 
ZO5  X29 X30       
ZO6  X31 X32 X33 X34 X35 X36 X37 X38 X39 
ZO7 X40 X41 X42 X43 X44 X45 X46 X47 X48 X49 
ZO8  X50 X51 X52   X53  X54 X55 

(X ##是一个随机字符串)

而且我想在#1列中提取值,即履行一定的条件。一个示例性条件是:检索列B,C,D,G,I,J中具有非空值的所有值(列1),以及剩余列A,E,F,H中的空值。

所以示例输出将是:

Z01 
Z04 
Z08 

编辑:对不起为穷人输入。在以分号分隔的表格下方;真正的输入是TAB分隔

;A;B;C;D;E;F;G;H;I;J 
ZO1;;X1;X2;X3;;;X4;;X5;X6 
ZO2;X7;X8;X9;X10;;X11;X12;X13;X14;X15 
ZO3;X16;X17;X18;X19;;;X20;;X21;X22 
ZO4;;X23;X24;X25;;;X26;;X27;X28 
ZO5;;X29;X30;;;;;;; 
ZO6;;X31;X32;X33;X34;X35;X36;X37;X38;X39 
ZO7;X40;X41;X42;X43;X44;X45;X46;X47;X48;X49 
ZO8;;X50;X51;X52;;;X53;;X54;X55 
+0

放在适当entabbed例子。 – dawg

+1

把分号或别的东西,我们可以看到/使用,而不是你的例子中的标签,然后我们将有我们可以测试的东西,所以将能够帮助你。 –

+0

@EdMorton,但我的例子是制表符分隔的,我应该如何给出一个现实的输入? – rororo

回答

4

我喜欢这个,它会如果您运行将其复制并粘贴到bash,评论和所有内容中。

tail -n +2 file    `# Grab the bit of the file you car about` \ 
| sed 's/;/|;/'   `# Protect the first column`    \ 
| sed 's/;[^;][^;]*/1/g' `# Change all the filled values to 1`  \ 
| sed 's/;/0/g'   `# Change the empty values to 0` 

该命令的输出看起来是这样的:

ZO1|0111001011 
ZO2|1111011111 
ZO3|1111001011 
ZO4|0111001011 
ZO5|0110000000 
ZO6|0111111111 
ZO7|1111111111 
ZO8|0111001011 

所以,现在我可以将我在寻找的模式。具有功能

>> function table_match() { 
    cat       `# Grab the stdin`      \ 
    | sed 's/;/|;/'   `# Protect the first column`   \ 
    | sed 's/;[^;][^;]*/1/g' `# Change all the filled values to 1` \ 
    | sed 's/;/0/g'   `# Change the empty values to 0`  \ 
    | grep "|${1}"    `# Grab the match you want`   \ 
    | sed 's/|.*//'   `# Clear out the garbage`; 
} 


>> tail -n +2 file | table_match 0111001011 
ZO1 
ZO4 
ZO8 

tail -n +2 file    `# Grab the bit of the file you car about` \ 
| sed 's/;/|;/'   `# Protect the first column`    \ 
| sed 's/;[^;][^;]*/1/g' `# Change all the filled values to 1`  \ 
| sed 's/;/0/g'   `# Change the empty values to 0`   \ 
| grep "|0111001011"  `# Grab the match you want`    \ 
| sed 's/|.*//'   `# Clear out the garbage` 

然后标识摹eneralize它我可以做其他的东西太...点外卡...克林星...漂亮。

>> tail -n +2 file | table_match .......011 
ZO1 
ZO2 
ZO3 
ZO4 
ZO5 
ZO6 
ZO7 
ZO8 

>> tail -n +2 file | table_match 01* 
ZO1 
ZO4 
ZO5 
ZO6 
ZO8 
+0

您可以将前三个'sed'替换组合成一个单独的命令:'sed -e'first'-e'second'-e'third'' ... – dawg

+1

@EdMorton Yeah看起来像。我不得不假装输入,因为我不能复制和粘贴,我有一些不好的字符。 – gbtimmon

+0

奇怪的是,用0/1替换字段的两行不起作用 – rororo

0

几个简单的方法来做到这一点。这里有一个以上的C的语法:

awk -F'\t' '{if($2=="" && $3!="" && $4!="" && $5!="" && $6=="" && $7=="" && $8!="" && $9=="" && $10!="" && $11!="") print $1}' table_file 

而在awk的原生语法另一个更压缩的版本,如karakfa在下面的评论建议:

awk -F'\t' '$3!="" && $4!="" && $5!="" && $8!="" && $10!="" && $11!="" && $2$6$7$9 == "" {print $1}' table_file 
+0

为什么这个答案被降低了?只是因为大括号缺失? – rororo

+0

@tobi,感谢您发现缺失的支架!不知道为什么它被投票。这不是一个动态的解决方案,而是完成这项工作。 – flu

+0

好吧,列的数目是错误的,这应该工作:'awk -F'\ t''{if($ 2 ==“”&& $ 3!=“”&& $ 4!=“”&& $ 5!=“” && $ 6 ==“”&& $ 7 ==“”&& $ 8!=“”&& $ 9 ==“”&& $ 10!=“”&& $ 11!=“”)print $ 1}'' – rororo

1

你会想是这样的:

awk -v pres='B,C,D,G,I,J' ' 
    BEGIN { FS="\t" } 
    FNR==1 { 
     split(pres,tmp,/,/) 
     for (i in tmp) { 
      presNames[tmp[i]] 
     } 
     for (i=2; i<=NF; i++) { 
      if ($i in presNames) { 
       mustBePresent[i] 
      } 
     } 
     next 
    } 
    { 
     pass = 1 
     for (i=1; i<=NF; i++) { 
      if (($i == "") && (i in mustBePresent)) { pass = 0 } 
      if (($i != "") && !(i in mustBePresent)) { pass = 0 } 
     } 
     if (pass) { 
      print $1 
     } 
    } 
' file 

由于您未提供样本输入,我们可以轻松地复制/粘贴以进行测试,因此未经测试。

其实我喜欢创造领域比上述所以这里好一点的位图的@gbtimmon's approach是你会怎么做,在AWK:

awk -v pres='B,C,D,G,I,J' ' 
    BEGIN { FS="\t" } 
    FNR==1 { 
     split(pres,tmp,/,/) 
     for (i in tmp) { 
      presNames[tmp[i]] 
     } 
     req = 1 
     for (i=2; i<=NF; i++) { 
      req = req ($i in presNames ? 1 : 0) 
     } 
     next 
    } 
    { 
     act = 1 
     for (i=2; i<=NF; i++) { 
      act = act ($i == "" ? 0 : 1) 
     } 
     if (act == req) { 
      print $1 
     } 
    } 
' file 
1

考虑:

$ printf "\tA\tB\tC\tD\tE\tF\tG\tH\tI\tJ 
ZO1\t\tX1\tX2\tX3\t\t\tX4\t\tX5\tX6 
ZO2\tX7\tX8\tX9\tX10\t\tX11\tX12\tX13\tX14\tX15 
ZO3\tX16\tX17\tX18\tX19\t\t\tX20\t\tX21\tX22 
ZO4\t\tX23\tX24\tX25\t\t\tX26\t\tX27\tX28 
ZO5\t\tX29\tX30\t\t\t\t\t\t\t 
ZO6\t\tX31\tX32\tX33\tX34\tX35\tX36\tX37\tX38\tX39 
ZO7\tX40\tX41\tX42\tX43\tX44\tX45\tX46\tX47\tX48\tX49 
ZO8\t\tX50\tX51\tX52\t\t\tX53\t\tX54\tX55\n" > file 

在Ruby:

$ sed -E '1 s/^(.*)$/hdr\1/' /tmp/file | 
    ruby -e 'require "csv" 
      options={:col_sep=>"\t", :headers=>true} 
      CSV.parse($<, options){ |r| 
       puts r["hdr"] if ("B|C|D|G|I|J".split("|").map{ |e| r[e]!=nil }.all? \ 
          && "A|E|F|H".split("|").map { |e| r[e]==nil }.all?) } ' 
ZO1 
ZO4 
ZO8 

或者少一点简洁:

$ sed -E '1 s/^(.*)$/hdr\1/' /tmp/file | 
ruby -e 'require "csv" 
     options={:col_sep=>"\t", :headers=>true} 
     CSV.parse($<, options) 
      .select { |r| "B|C|D|G|I|J".split("|").map{ |e| r[e]!=nil }.all? } 
      .select { |r| "A|E|F|H".split("|").map { |e| r[e]==nil }.all? } 
      .map { |r| puts r["hdr"] } ' 

两种情况:

  1. 使用sed插入hdr字段,因为标题行少一个下面的数据字段;
  2. 使用CSV模块读取修改后的文件;
  3. CSV模块中的空白字段分配为nil。用它来选择你描述的逻辑。

使用gbtimmon used,在Ruby中真值表方法:

$ sed -E '1 s/^(.*)$/hdr\1/' file | 
ruby -e 'require "csv" 
     options={:col_sep=>"\t", :headers=>true} 
     tt=CSV.parse($<, options) 
      .map { |r| [r[0], r[1..-1].map { |e| e==nil ? "0" : "1" }.join ] } 
      .group_by { |hdr, bits| bits } 
      .map { |bits,lol| [bits, lol.map(&:first)] }.to_h 
     tt.map { |k, a| puts "#{k} => #{a.join(%q(,))}" if k=~/^./ } ' 
0111001011 => ZO1, ZO4, ZO8 
1111011111 => ZO2 
1111001011 => ZO3 
0110000000 => ZO5 
0111111111 => ZO6 
1111111111 => ZO7 

您可以在正则表达式字面k=~/^./生产所需的结果添加任何正则表达式。

随着awk

$ awk 'BEGIN { FS="\t"; OFS=", " } 
     NR==1 { next } 
      { ind="" 
       for (i=2;i<=NF;i++) 
       ind=ind ($i=="" ? "0" : "1") 
      map[ind]=map[ind] ? map[ind] OFS $1 : $1 
      } 
     END { for(e in map) printf "%s => %s\n", e, map[e] }' file 
0111111111 => ZO6 
0111001011 => ZO1, ZO4, ZO8 
0110000000 => ZO5 
1111111111 => ZO7 
1111011111 => ZO2 
1111001011 => ZO3 

表条目将进来一个无序的结果,但然后管,为了sedgrep以选择该行(或行的一部分)所需的(或向右awk环在内部结束)。

最佳

+0

这个awk解决规则! – rororo

0

问题的“直” awk的解决方案是空间的分析,事实上,awk将看不到空字段,因此我们必须用sed awk的旁边。

sed -rn 's/([[:alpha:]]+)|([[:blank:]]{4})/,&/gp' filename | sed -rn 's/[[:blank:]]//gp' filename | awk -F , 'NR > 1 { if ($3 == "" && $4 != "" && $5 != "" && $6 != "" && $7 == "" && $8 == "" && $9 != "" && $10 == "" && $11 != "" && $12 != "") { print $2 } }' 



sed -rn 's/([[:alpha:]]+)|([[:blank:]]{4})/,&/gp' filename | sed -rn 's/[[:blank:]]//gp' filename 

首先用sed在任意字符或4个空格之前加一个逗号。然后运行第二个sed语句以删除空格。

这给你留下

,,A,B,C,D,E,F,G,H,I,J 
,ZO1,,X1,X2,X3,,,X4,,X5,X6 
,ZO2,X7,X8,X9,X10,,X11,X12,X13,X14,X15 
,ZO3,X16,X17,X18,X19,,,X20,,X21,X22 
,ZO4,,X23,X24,X25,,,X26,,X27,X28 
,ZO5,,X29,X30,,,,,, 
,ZO6,,X31,X32,X33,X34,X35,X36,X37,X38,X39 
,ZO7,X40,X41,X42,X43,X44,X45,X46,X47,X48,X49 
,ZO8,,X50,X51,X52,,,X53,,X54,X55 

然后用awk来处理这些数据:

awk -F , 'NR > 1 { if ($3 == "" && $4 != "" && $5 != "" && $6 != "" && $7 == "" && $8 == "" && $9 != "" && $10 == "" && $11 != "" && $12 != "") { print $2 } }' 

使用,作为字段分隔符,然后核对一定的条件下分隔的字段。

输出:

ZO1 
ZO4 
ZO8