shell:选择唯一的平面文件行

时间:2013-08-07 17:55:10

标签: bash shell sorting pipe uniq

我有一个看起来像这样的平面文件

cat file

ID1, VALUE1_1
ID1, VALUE1_2
ID1, VALUE1_3
ID2, VALUE2_1
ID2, VALUE2_1
ID3, VALUE3_1
ID3...

从数据样本中可以看出,对于每个ID,该ID有几个值,它们可以是任何值 - 相同或不相同。对我而言,我并不关心它正在捡到什么价值。任何价值对我都有用。

所以我只想要每个ID中的一个值。我真的不在乎哪一个,但如果我必须选择,我会说最长的一行。

ID1, VALUE1_2
ID2, VALUE2_1
ID3, VALUE3_1

它可能是用Python完成的,但在Shell本身有一个简单的方法,我可以使用sed或awk,但请不要写一整段awk代码,请...

它可能看起来像:

# Pseudo code
# sort -k 1 file | uniq (max(length) by id)  

非常感谢!!

3 个答案:

答案 0 :(得分:3)

编辑:

user84771

所以我完全根据你的说法重写了我的答案。它有几行,但希望这是你正在寻找的:

为了在Mysql中找到类似于组的“每个ID”中的最大行,我会执行以下操作。

给出以下文本文件:

[root@dev7 ~]# cat stackoverflow2.log 
ID1, fdsgfdsggfdsgsdfg
ID1, fdsgsdfg
ID1, fdsgfdgdsfgdsgsdfgdgffdsgfsdg
ID1, fdsgsdfg
ID2, fdgsfdsgfdshshdsfhdfghdsfhdfhdshsdfhsfdh
ID2, fsfgsdgf
ID3, fdgfdgdgfdggfdg
[root@dev7 ~]# 

我会做以下事情:

_DATAFILE=stackoverflow2.log
_KEYS=$(awk '{ $1=$1; print $1}' ${_DATAFILE} | uniq | sed "s,\,,,g" | xargs )
_LARGEST_PER_KEY=""
echo $_KEYS
for i in ${_KEYS}; do
  _LARGEST_PER_KEY="${_LARGEST_PER_KEY}\n$(grep "$i" ${_DATAFILE} | uniq | awk '{ print length ":", $0 }' | sort -n -u | tail -1 | cut -d ":" -f2 | awk '{ $1=$1; print}')"
done;
echo -e ${_LARGEST_PER_KEY}

解释发生了什么。

  • _DATAFILE - 此变量是您的输入文件。
  • _KEYS - 此变量返回第一列中的所有键(uniq和已排序的相关数据)。我用xargs来制作 确保所有的钥匙都放在一条直线上,以便下一步。
  

[root @ dev7~]#_KEYS = $(awk'{$ 1 = $ 1; print $ 1}'$ {_ DATAFILE} | uniq |   sed“s,\ ,,, g”| xargs)

     

[root @ dev7~] #echo $ _KEYS

     

ID1 ID2 ID3

  • _LARGEST_PER_KEY - 这个变量将在我们完成后用于您的结果。我们在for循环之前定义它。

  • for循环为任务中的键执行grep(例如ID1),然后执行我的代码行以确定哪一个包含最长的数据值,并执行numeric / uniq排序以查看哪一个是最大的。我们使用tail获取该值并将其附加到_LARGEST_PER_KEY字符串。 (注意:我们添加\ n字符作为分隔符)

  • ONCE完成for循环后,我们使用echo -e回显结果,以确保在屏幕上正确评估换行符:

  

[root @ dev7~] #echo -e $ {_ LARGEST_PER_KEY}

     

ID1,fdsgfdgdsfgdsgsdfgdgffdsgfsdg

     

ID2,fdgsfdsgfdshshdsfhdfghdsfhdfhdshsdfhsfdh

     

ID3,fdgfdgdgfdggfdg

注意:由于我们在开头对所有内容进行了排序,因此没有理由再次排序。

澄清说明:

  

awk'{$ 1 = $ 1; print}' - 这将删除尾随空格(行尾/行尾)

     

uniq - 删除重复项

     

awk'{print length“:”,$ 0}' - 获取每一行的行长,用“行的长度”打印出来:“行测试”

     

sort -n -u - 数字排序(最大数字是最后一项)。还确保整个文件在数据文件中唯一排序   到达未分类。谢谢你的提示   Glenn

     

尾巴-1 - 抓住最大的

以来的最后一行      

cut -d“:” - f2 - 如果你只想要确切的行,摆脱行的长度只需返回行

     

awk'{$ 1 = $ 1; print}' - 这将删除尾随空格(行尾/行尾)

再次,我确定这是一种更有效率的方法,但这是我能够提出的。希望这有帮助!

答案 1 :(得分:2)

这将找到每个ID的第一行:

awk -F, '!seen[$1]++' file

说明:

  • awk关联数组不必预先声明,因此第一次遇到ID时,seen[$1]的值为零(对于数字上下文)。
  • seen[$1]++ 后递增关联数组元素,以便表达式在第一次看到ID时计算为零,并在任何其他时间计算为某个正整数。
  • awk将零视为false,将任何其他数字视为true,因此我们使用!运算符否定后增量表达式。现在我们只有在第一次看到ID时才有真正的表达式:!seen[$1]++
  • awk程序看起来像condition1 {body1} condition2 {body2} ...
    • body仅在其对应的condition评估为true时执行。
    • 如果条件存在但主体被省略,则默认操作为{print}
    • 要完成,当主体存在但条件被省略时,默认条件的计算结果为true,并且将对每条记录执行操作。

总而言之,只要表达式的计算结果为true,这个awk程序就会打印当前记录,这只是第一次看到ID。


如果您真的想要每个ID的最长行:

awk '
    length($2) > max[$1] {max[$1] = length($2); line[$1] = $0}
    END {for (id in line) {print line[id]}}
' file

这可能会改变id的顺序(关联数组是无序集合)。如果这是一个问题,您可以随时将其输入sort

答案 2 :(得分:1)

这个awk脚本应该做你想要的,假设文件已经排序:

 awk 'prev!=$1{print}{prev=$1}' datafile

测试:

$ cat datafile
ID1, VALUE1_1
ID1, VALUE1_2
ID1, VALUE1_3
ID2, VALUE2_1
ID2, VALUE2_1
ID3, VALUE3_1
$  awk 'prev!=$1{print}{prev=$1}' datafile
ID1, VALUE1_1
ID2, VALUE2_1
ID3, VALUE3_1

说明:

  • prev!=$1{print}部分表示:如果变量prev的值与记录中的第一个字段不同,则打印该行
  • {prev=$1}部分表示:将变量prev设置为记录中第一个字段的值。

默认情况下,字段由空格分隔(除非使用-F选项),并且记录由换行符分隔。