Bash:集合论

Bash:集合论,bash,awk,set-theory,Bash,Awk,Set Theory,我有以下以制表符分隔的表: 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

我有以下以制表符分隔的表:

    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中具有非空值且其余列a、E、F、H中具有空值的所有值(列1)

因此,示例输出为:

Z01
Z04
Z08
编辑:很抱歉输入错误。在分号分隔的表下面;实际输入是以制表符分隔的

;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

有几个简单的方法可以做到这一点。这里有一个更像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

有几个简单的方法可以做到这一点。这里有一个更像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

你会想要这样的东西:

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
awk-v pres='B,C,D,G,I,J''
开始{FS=“\t”}
FNR==1{
拆分(压力、tmp、/、/)
对于(tmp中的i){
前名称[tmp[i]]
}

对于(i=2;i你会想要这样的东西:

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
awk-v pres='B,C,D,G,I,J''
开始{FS=“\t”}
FNR==1{
拆分(压力、tmp、/、/)
对于(tmp中的i){
前名称[tmp[i]]
}

对于(i=2;i我喜欢这一个,如果您将它全部复制并粘贴到bash、comments和all中,它将运行

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
所以现在我可以设定我想要的模式了

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`
然后用一个函数将其泛化

>> 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 | table_match .......011
ZO1
ZO2
ZO3
ZO4
ZO5
ZO6
ZO7
ZO8

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

我喜欢这个,如果你把它全部复制粘贴到bash、comments和all中,它就会运行

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
所以现在我可以设定我想要的模式了

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`
然后用一个函数将其泛化

>> 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 | table_match .......011
ZO1
ZO2
ZO3
ZO4
ZO5
ZO6
ZO7
ZO8

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

在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
您可以在正则表达式文本
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
$awk'开始{FS=“\t”;OFS=“,”}
NR==1{next}
{ind=“”
对于(i=2;i给定值:

在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
您可以在正则表达式文本
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
$awk'开始{FS=“\t”;OFS=“,”}
NR==1{next}
{ind=“”
对于(i=2;i“直接”awk解决方案的问题是解析空间,并且awk不会看到空字段,因此我们必须在awk旁边使用sed

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
“直接”awk解决方案的问题是解析空间,而且事实上awk看不到空字段,因此我们必须在awk旁边使用sed

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


为什么这个答案被否决了?只是因为缺少了花括号?@tobi,谢谢你发现了缺少的括号!不知道为什么它被否决了。这不是一个动态的解决方案,但能解决问题。好吧,列的计数是错误的,这应该是有效的:
awk-F'\t'{if($2==“”&$3!=&$4!&&&&$5!=“”&$6==“”&$7=“”&&&$8!=“”&&$9=“”&&&&$10!=“”&&&$11!=“”)打印$1}'
您可以通过将条件移出块来删除
,如果
,也可以将空值合并为一个
$2$6$7$9=“”
显示C-like或Lisp-like或Prolog-like或任何其他类似语法的东西都没有用。这是一个awk脚本,所以只显示awk语法。为什么这个答案被否决了?只是因为缺少了花括号?@tobi,谢谢你发现了缺少的括号!不知道它为什么被否决了。这不是一个动态解决方案,但可以解决问题。嗯,th列的计数是错误的,这应该是有效的:
awk-F'\t'{if($2==”&&&$3!==“&&&&&$4!==“&&&&&&&&$5!=”&&&&$6==“&$7==“&$8!=”&$9==“&$10!=”“&$11!=”)打印$1}'
您可以通过将条件移出块来删除
if
。您还可以将空值合并为一个
$2$7$9==>
显示C-like或Lisp-like或Prolog-like或任何其他类似语法的东西都没有用。这是一个awk脚本,所以只需显示awk语法。输入正确的带括号的示例。在示例中输入分号或其他我们可以看到/使用的东西,而不是制表符,然后我们就可以测试一些东西,从而能够帮助您。@EdMorton但我的示例是以制表符分隔的,那么我应该如何给出一个真实的输入?您的真实输入由单个字符分隔,即制表符。您发布的示例输入未由单个字符分隔,并且不能在此网站上由制表符分隔,因此您不能发布“真实的”输入到这个站点,因此需要折衷。发布以逗号分隔的输入并简单地声明“在我的真实文件中,逗号是制表符”比发布以多个空格分隔的输入(需要不同的工具)更有用因此,我们有一个由单个字符分隔的输入来进行测试,并且可以对真正的输入进行任何必要的调整/声明,即制表符。放入适当的带括号的示例中。在示例中放入分号或其他我们可以看到/使用的东西来代替制表符,然后我们将有一些可以测试的东西,这样我们将能够帮助您。@E但我的示例是以制表符分隔的,那么我应该如何给出一个reali