对于文件中的每一行,如果这些值小于另一个字段中的值,则将特定列中的字段打印到 NF

Jac*_*ack 2 awk

我有一个具有以下格式的文件,其中每行的字段数是可变的:

NC_000001.11_NM_001005484.2 69270   234 69037   65565   69037
NC_000001.11_NM_001005484.2 69511   475 69037   65565   69037
NC_000001.11_NM_001005484.2 69761   725 69037   65565   69037
NC_000001.11_NM_001385640.1 942155  20  942136  924432  925922  930155  931039  935772 939040   939272  941144  942136  942410  942559  943253  943698  943908  
Run Code Online (Sandbox Code Playgroud)

对于每一行,我想打印前四个字段。对于其余字段($5 到 NF),如果这些字段中的值小于 $4 中的值,我想打印该字段。

输出示例:

NC_000001.11_NM_001005484.2 69270   234 69037   65565   
NC_000001.11_NM_001005484.2 69511   475 69037   65565   
NC_000001.11_NM_001005484.2 69761   725 69037   65565   
NC_000001.11_NM_001385640.1 942155  20  942136  924432  925922  930155  931039  935772 939040   939272  941144  
Run Code Online (Sandbox Code Playgroud)

我尝试过多种不同的 awk 选项,但都失败了。awk 新手,希望得到任何帮助。

Ed *_*ton 5

如果您不关心输出中的空白,那么您需要的是:

$ cat tst.awk
{
    out = $1 OFS $2 OFS $3 OFS $4
    for (i=5; i<=NF; i++) {
        if ( $i < $4 ) {
            out = out OFS $i
        }
    }
    print out
}
Run Code Online (Sandbox Code Playgroud)

$ awk -f tst.awk file
NC_000001.11_NM_001005484.2 69270 234 69037 65565
NC_000001.11_NM_001005484.2 69511 475 69037 65565
NC_000001.11_NM_001005484.2 69761 725 69037 65565
NC_000001.11_NM_001385640.1 942155 20 942136 924432 925922 930155 931039 935772 939040 939272 941144
Run Code Online (Sandbox Code Playgroud)

column如果您愿意,您可以通过管道进行视觉对齐:

$ awk -f tst.awk file | column -t
NC_000001.11_NM_001005484.2  69270   234  69037   65565
NC_000001.11_NM_001005484.2  69511   475  69037   65565
NC_000001.11_NM_001005484.2  69761   725  69037   65565
NC_000001.11_NM_001385640.1  942155  20   942136  924432  925922  930155  931039  935772  939040  939272  941144
Run Code Online (Sandbox Code Playgroud)

否则,如果您希望输出中的间距看起来像输入中的间距(即前 4 个字段看起来有 1 个或更多空格,其余字段有 2 个或更多空格)并假设某些行可能只有 4或更少的字段,然后使用任何 POSIX awk(对于字符类和正则表达式间隔):

$ cat tst.awk
BEGIN { OFS="\t" }
match($0,/([^[:space:]]+[[:space:]]+){3}[^[:space:]]+/) {
    out = substr($0,RSTART,RLENGTH)
    for (i=5; i<=NF; i++) {
        if ( $i < $4 ) {
            out = out OFS $i
        }
    }
    $0 = out
}
{ print }
Run Code Online (Sandbox Code Playgroud)

如果 $4 之后的字段应以制表符分隔:

$ awk -f tst.awk file
NC_000001.11_NM_001005484.2 69270   234 69037   65565
NC_000001.11_NM_001005484.2 69511   475 69037   65565
NC_000001.11_NM_001005484.2 69761   725 69037   65565
NC_000001.11_NM_001385640.1 942155  20  942136  924432  925922  930155  931039  935772  939040  939272  941144
Run Code Online (Sandbox Code Playgroud)

或者如果它们应该用空格分隔:

$ awk -f tst.awk file | column -s$'\t' -t
NC_000001.11_NM_001005484.2 69270   234 69037   65565
NC_000001.11_NM_001005484.2 69511   475 69037   65565
NC_000001.11_NM_001005484.2 69761   725 69037   65565
NC_000001.11_NM_001385640.1 942155  20  942136  924432  925922  930155  931039  935772  939040  939272  941144
Run Code Online (Sandbox Code Playgroud)

上面保留了前 4 个字段之间的空白,以便它只是您输入中的制表符和/或空格的任何组合,然后在每 5 个及后续字段之前打印一个制表符,您可以使用它来更改为column等效的如果您愿意,可以留空,两者看起来都像问题中的输入和输出。

我正在构建一个out在上面的循环中命名的新字符串,并将其分配给$0循环之后的一次,而不是修改$0$i在循环内,因为每次更改$iawk 都必须从它的字段重新构建$0,并且每次更改$0awk 都必须重新分割$0为字段,因此两者效率都很低,并且可能会导致意外错误,具体取决于字段的内容,因此您不应在循环内修改$0或 ,$i除非您有一个非常具体的目的需要这样做。