使用列名而不是数字过滤条件
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
can be easily installed, for example, using conda
,像这样:
conda create --name miller miller
我正在尝试根据两个条件过滤包含列的文本文件。由于文件的大小,我不能使用列号(因为有数千个并且没有编号)但需要使用列名。我已经搜索并尝试想出多种方法来执行此操作,但没有任何内容被 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
can be easily installed, for example, using conda
,像这样:
conda create --name miller miller