【问题标题】:awk 'uniq' on a range of columns一系列列上的 awk 'uniq'
【发布时间】:2020-01-30 22:08:31
【问题描述】:

我正在尝试过滤掉列表的所有重复项,忽略前 n 列,最好使用 awk(但对其他实现开放)

我找到了固定列数的解决方案,但由于我不知道会有多少列,所以我需要一个范围。我找到的那个解决方案here

为清楚起见: 我想要实现的是history 的别名,它将过滤掉重复项,但保留 history_id 完整,最好不要弄乱订单。 历史就是这样的形式

ID    DATE       HOUR     command
 5612  2019-07-25 11:58:30 ls /var/log/schaubroeck/audit/2019/May/
 5613  2019-07-25 12:00:22 ls /var/log/schaubroeck/         
 5614  2019-07-25 12:11:30 ls /etc/logrotate.d/                       
 5615  2019-07-25 12:11:35 cat /etc/logrotate.d/samba     
 5616  2019-07-25 12:11:49 cat /etc/logrotate.d/named 

因此,此命令适用于最多四个参数的命令,但我需要将固定列替换为一个范围以考虑所有情况:

history | awk -F "[ ]" '!keep[$4 $5 $6 $7]++'

我觉得@kvantour 让我走上了正确的道路,所以我尝试了:

history | awk '{t=$0;$1=$2=$3=$4="";k=$0;$0=t}_[k]++' | grep cd

但这仍然会产生重复的行

 1102  2017-10-27 09:05:07 cd /tmp/
 1109  2017-10-27 09:07:03 cd /tmp/
 1112  2017-10-27 09:07:15 cd nagent-rhel_64/
 1124  2017-11-07 16:38:50 cd /etc/init.d/
 1127  2017-12-29 11:13:26 cd /tmp/
 1144  2018-06-21 13:04:26 cd /etc/init.d/
 1161  2018-06-28 09:53:21 cd /etc/init.d/
 1169  2018-07-09 16:33:52 cd /var/log/
 1179  2018-07-10 15:54:32 cd /etc/init.d/

【问题讨论】:

  • 使用_ 作为变量名而不是一个有意义的词甚至一个字母对任何程序的清晰度都没有帮助。 -F" " 也将 FS 设置为它已经设置的默认值。 edit您的问题提供简洁、可测试的样本输入和预期输出,以便我们为您提供帮助。
  • 如果请求一个空格作为字段分隔符,请使用-F"[ ]"。 @EdMorton 提到的默认值是任何空格和制表符序列。
  • 你可以使用 uniq -f 代替 awk。
  • 您说!keep[$4 $5 $6 $7]++ 有效,@kvantour 显示{...}!_[k]++,但您尝试使用{...}_[k]++.而不是(您放弃了!)。
  • @EdMorton 发现得很好!我在终端中犯了同样的错字!在我将 Chris Maes 的答案标记为我的解决方案(从线程中复制它)之后,我尝试了 kvantour 的解决方案,但我不明白为什么它突然起作用了。感谢您指出这一点,现在我可以重新充满信心地继续我的 awk 之旅......

标签: sorting awk filter uniq


【解决方案1】:

您提出的命令不会像您预期的那样工作。想象一下,您有两行,例如:

a b c d 12 13 1
x y z d 1 21 31

这两行都将被视为重复的键,在数组_ 中用于d12131

这可能是你感兴趣的:

$ history | awk '{t=$0;$1=$2=$3="";k=$0;$0=t}!_[k]++'

这里我们将原始记录存储在变量t 中。通过为其分配空值来删除记录的前三个字段。这将重新定义记录 $0 并将其存储在密钥 k 中。然后我们将记录重置为t 的值。我们使用键 k 进行检查,它现在包含除前 3 个以外的所有字段。

注意:将字段分隔符设置为-F" " 不会将其设置为单个空格,而是设置为任何空格序列(空格和制表符)。这也是默认行为。如果你想要一个空格,加-F"[ ]"

【讨论】:

  • 这是一个有用的解释。但它仍然无法正常工作......我在我的qustion中添加了命令和结果,因为格式混乱,在cmets中
  • @oneindelijk 当我对您提供的输入执行我的行时,我得到了正确的结果。所以我无法重现你的结果。你也设置了$4=="",而这不应该是这样。
  • 我试过 $4== 和没有。也许一开始的空间,使明显的第一列实际上是第二列......
  • 这是redhat 6.10,也许吧?
  • 虽然我选择sort -u -k4 | sort -n 选项,但我将此答案标记为解决方案,因为我(我)希望了解更多关于 awk 的信息。谢谢!
【解决方案2】:

你可以使用排序:

history | sort -u -k4
  • -u 独一无二的
  • -k4 仅对从第四个开始的所有列进行排序。

运行这个

 1102  2017-10-27 09:05:07 cd /tmp/
 1109  2017-10-27 09:07:03 cd /tmp/
 1112  2017-10-27 09:07:15 cd nagent-rhel_64/
 1124  2017-11-07 16:38:50 cd /etc/init.d/
 1127  2017-12-29 11:13:26 cd /tmp/
 1144  2018-06-21 13:04:26 cd /etc/init.d/
 1161  2018-06-28 09:53:21 cd /etc/init.d/
 1169  2018-07-09 16:33:52 cd /var/log/
 1179  2018-07-10 15:54:32 cd /etc/init.d/

产量:

 1124  2017-11-07 16:38:50 cd /etc/init.d/                                                                                                                                                                         
 1112  2017-10-27 09:07:15 cd nagent-rhel_64/                                                                                                                                                                      
 1102  2017-10-27 09:05:07 cd /tmp/                                                                                                                                                                                
 1169  2018-07-09 16:33:52 cd /var/log/

编辑如果你想保持顺序,你可以应用第二次排序:

history | sort -u -k4 | sort -n

【讨论】:

  • 双重排序可能会更好地保持时间顺序
  • 关闭,但第二次排序应该使用数字排序,所以history | sort -u -k4 | sort -n 是我一直在寻找的有效答案。谢谢!
  • 当然,我真傻,我只测试了你的数据,这些数据都是 4 位数长。如果这是您正在寻找的答案,那么您应该接受这个答案。如果其他答案对您有帮助,您应该点赞。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多