使用列名而不是数字过滤条件

Filtering on a condition using the column names and not numbers

我正在尝试根据两个条件过滤包含列的文本文件。由于文件的大小,我不能使用列号(因为有数千个并且没有编号)但需要使用列名。我已经搜索并尝试想出多种方法来执行此操作,但没有任何内容被 return 编辑到命令行。

以下是我尝试过的一些方法:

awk '($colname1==2 && $colname2==1) { count++ } END { print count }' file.txt 根据条件过滤掉列

head -1 file.txt | tr '\t' | cat -n | grep "COLNAME 尝试 return 与该列相关的可能列号。

示例文件为:

ID  ad   bd

1   a   fire
2   b   air
3   c   water
4   c   water
5   d   water
6   c   earth

输出将是: 2(ad=c 和 bd=water 的计数)

使用您的输入文件和隐含条件这应该有效

$ awk -v c1='ad' -v c2='bd' 'NR==1{n=split([=10=],h); for(i=1;i<=n;i++) col[h[i]]=i} 
                             $col[c1]=="c" && $col[c2]=="water"{count++} END{print count+0}' file

2

或者您也可以将 c1 和 c2 替换为脚本中的值。

找到您可以运行

的列索引
$ awk -v cols='ad bd' 'BEGIN{n=split(cols,c); for(i=1;i<=n;i++) colmap[c[i]]} 
                       NR==1{for(i=1;i<=NF;i++) if($i in colmap) print $i,i; exit}' file

ad 2
bd 3

或者也许用这条链

$ sed 1q file | tr -s ' ' \n | nl | grep -E 'ad|bd'

     2  ad
     3  bd

尽管由于正则表达式匹配可能会有误报...

您可以重写 awk 以更简洁

$ awk -v cols='ad bd' '{while(++i<=NF) if(FS cols FS ~ FS $i FS) print $i,i; 
                        exit}' file

ad 2
bd 3 

正如我在之前的评论中提到的,https://unix.stackexchange.com/a/359699/133219 中的答案显示了如何执行此操作:

awk -F'\t' '
    NR==1 {
        for (i=1; i<=NF; i++) {
            f[$i] = i
        }
    }
    ($(f["ad"]) == "c") && ($(f["bd"]) == "water") { cnt++ }
    END { print cnt+0 }
' file
2

我假设您的输入是制表符分隔的,因为您问题中命令中的 tr '\t' 看起来您正在尝试将制表符转换为换行符以将列名转换为数字。如果我错了,它们只是被任何白色 space 链分开,那么从上面删除 -F'\t'

使用 miller 工具包使用列名操作 tab-delimited 文件。下面是一个 one-liner 过滤 tab-delimited 文件(使用 --tsv 指定分隔符)并将结果与​​ header 一起写入 STDOUT。使用 tail 删除 header 并使用 wc.

计算行数
mlr --tsv filter '$ad == "c" && $bd == "water"' file.txt | tail -n +2 | wc -l

打印:

2

另请参见:

miller manual

注意miller can be easily installed, for example, using conda,像这样:

conda create --name miller miller