3

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

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)  

多谢 !!

4

3 回答 3

3

编辑:

用户84771

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

为了从类似于 Mysql 中的 group by 的“每个 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 循环对 quest 中的键(例如 ID1)执行 grep,然后执行我的表单代码行以确定哪个包含最长的数据值,并执行数字/uniq 排序以查看哪个是最大的。我们使用 tail 获取该值并将其附加到我们的 _LARGEST_PER_KEY 字符串中。(注意:我们添加 \n 字符作为分隔符)

  • 一旦 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 }' - 获取每行的行长,用 "lenghth of line" 打印出来:"line test"

sort -n -u - 数字排序(最大的数字是最后一项)。如果数据文件未排序,还可以确保整个文件被唯一排序。感谢 格伦的提示。

tail -1 - Grab 的最后一行,因为它是最大的

cut -d ":" -f2 - 如果你只想要确切的行,去掉行的长度,只需返回行

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

同样,我确信有一种更有效的方法来做到这一点,但这是我能够想出的。希望这可以帮助!

于 2013-08-07T18:17:42.823 回答
2

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

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

解释:

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

总而言之,这个 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

于 2013-08-07T18:51:29.170 回答
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使用该选项),并且记录由换行符分隔。

于 2013-08-07T18:21:13.253 回答