如何使用linux cmd基于列仅获取uniq行?

如何使用linux cmd基于列仅获取uniq行?

这是我的数据集:

col1,col2,col3
a,b,c
a,d,f
d,u,v
f,g,h
d,u,g
x,t,k

预期输出:

f,g,h
x,t,k

选择标准:

如果某件事多次发生col1,则所有关联的行都将被删除。

sort我可以使用 Linux或uniq其他方式解决这个问题吗?

答案1

这是一种“非缓冲” (1)两遍方法awk(仅适用于常规文件)。

awk -F',' 'NR==FNR{cnt[$1]++;next} FNR>1&&cnt[$1]==1' input.csv input.csv 

这将处理该文件两次,因此在命令行上将其作为参数声明两次。

  • 该参数-F','将字段分隔符设置为,
  • 在第一遍中,当NR全局行计数器 等于FNR每个文件行计数器时,我们记录在数组中遇到第 1 列中的每个值的频率cnt(将该值作为“数组索引”),但立即跳到下一行处理。
  • 在第二遍中,我们检查第一列当前值的出现计数器是否恰好为 1,并且文件中的行号是否大于 1(以跳过标题)。仅当这是 true 时才会打印当前行。这利用了awk规则块之外的表达式的语法,该表达式的计算结果是true指示awk打印当前行。

(1)对我发表的评论的回应非缓冲用引号引起来,因为该解决方案会将文件中的一些数据临时存储在 RAM 中,因此附带 RAM 使用情况。但是它不会逐字存储文件内容此外RAM 中的任何其他滚动保持数据(其中会考虑实际意义上的“缓冲”)。

答案2

假设该文件是/tmp/data您可以使用 perl 单行代码来完成的:

perl -e 'while(<STDIN>) { /(^\S+?),/; $show->{$1}=$_; $count->{$1}++;}; foreach(keys %$show) {print $show->{$_} if($count->{$_} == 1);}' < /tmp/data

或者更具可读性...:

while(<STDIN>) { #loop through all lines in the input and put the lines in "$_"
  /(^\S+?),/; #Everything before the first "," now ends up in "$1"
  $show->{$1} = $_; #a hash will be created with as keys the "$1" and as values the "$_"
  $count->{$1}++; #In the hash $count the number of occurrences will be increased everytime the same $1 appears
}
foreach(keys %$show) { #loop trough all lines
  print $show->{$_} if($count->{$_} == 1); #only print them if they occur once
}

答案3

awk唯一的解决方案

  1. 不遵守秩序

    awk -F, 'NR>1 { count[$1]++ ; line[$1]=$0 ;} 
       END { for ( c in count) if (count[c] ==1) print line[c]}' data
    
  2. 维持秩序

    awk -F, 'NR>1 { row[a]=$0; col[a]=$1; count[$1]++; ++a; } 
       END { for (i=0; i<a; ++i) if (count[col[i]]==1) print row[i]; }' data
    

在哪里

  • -F,告诉 awk 用作,分隔符

  • NR>1 第一行之后

  • count[$1]++计算第一列的元素

  • line[$1]=$0 商店线

  • END文件结束后

  • for ( c in count)循环遍历元素

  • if (count[c] ==1)如果只有一个

  • print line[c]打印行

  • acol[]用于存储顺序保留变体中的行顺序。

这可以单行,为了可读性我折叠起来

答案4

使用任何版本的强制性 POSIX 工具和输入中的任何字符进行装饰/排序/使用/取消装饰(除非您的输入实际上是一个带有引号字段的 CSV,其中可以包含逗号和/或换行符,但所有其他答案也会失败)并且保留输出的输入行的顺序,并且仅打开输入一次,因此如果输入来自管道或文件并且不将整个输入存储在内存中,它将起作用:

$ awk 'BEGIN{FS=OFS=","} NR>1{print ++cnt[$1], NR, $0}' file |
    sort -nt, -k1,1r -k2,2 |
    awk -F, '(!seen[$3]++) && ($1==1)' |
    cut -d, -f3-
f,g,h
x,t,k

相关内容