我是 bash 脚本编写的新手,想将一些次要代码更改为我当前的脚本。
我有一个文件,其中包含日期以及日期后面的“活动”或“不活动”一词,类似于以下内容:
2019-02-17 not active
2019-02-18 active
2019-02-19 not active
2019-02-19 not active
2019-02-19 active
2019-02-19 not active
2019-02-19 not active
2019-02-19 active
2019-02-20 active
2019-02-21 not active
2019-02-22 not active
我想删除“2019-02-19 not active”的所有重复项,同时保留“2019-02-19 active”的一份副本。非常感谢您的意见。谢谢!
答案1
使用GNU uniq
,你可以做到
sort file | uniq -w 10
选项-w
将比较限制为 10 个字符,因此每个日期只能生存一次。排序使active
第一个出现,因此它将被留下。
例如,如果这个问题的未来读者碰巧在没有 GNU 的系统上uniq
,您可以使用。sed
删除重复行的经典方法是
sed '$!N;/^\(.*\)\n\1$/!P;D'
该N;P;D
模式始终在模式空间中保留两行,但如果第二行不同,则仅打印第一行。我们可以更改此脚本以仅检查日期部分中的重复项:
sed '$!N;/^\([^ ]*\) .*\n\1/!P;D'
现在我们只需要注意选择active
以下几行:
sed '$!N;/^\([^ ]*\) .*\n\1/!P;//s/\(.*\)\(\n\).*not.*/\2\1/;D'
第一部分保持不变:仅打印日期更改后的行(或在最后一行)。但如果日期相同(地址中的空模式//
意味着重复上一个模式),通常会保留第二行。但是,如果第二行包含not
在其中,我们最好保留第一行(active
或not active
),因此该s
命令使第一行成为第二行(在空行之后,无论如何都会将其删除D
)。
我承认这不如 GNU 版本优雅,但至少它仍然是一句俏话。
答案2
一个独特的排序可以做到这一点。
$ sort -u input.txt
2019-02-17 not active
2019-02-18 active
2019-02-19 active
2019-02-19 not active
2019-02-20 active
2019-02-21 not active
2019-02-22 not active
顺便说一句,sort input.txt | uniq
做同样的事情,并且uniq
有一个-f
在确定唯一性时跳过字段的选项,-d
以及-D
如果您想打印非唯一行而不是仅打印唯一行的选项。
或者,如果您希望每个日期只有一个输出行,并且任何“活动”条目优先于“非活动”(或其他)条目:
perl -lane '
$date=shift @F;
$day{$date} = join(" ",@F) unless ($day{$date} eq "active");
END {print $_ . " " . $day{$_} for (sort keys %day)}' input.txt
2019-02-17 not active
2019-02-18 active
2019-02-19 active
2019-02-20 active
2019-02-21 not active
2019-02-22 not active
这将构建一个哈希 ( %day
),其中日期作为键,其余字段作为值。任何给定日期的当前或上次看到的值将替换先前看到的值除非任何给定日期的值已经是“活动的”。在这种情况下,当天的值将不会被替换。
读取所有输入后,将排序并打印 %day 哈希值。
这比依赖第二个字段的排序顺序更普遍有用和可重用。例如,如果您希望“zzzzz”优先而不是“action”。如果第二个字段可能包含在“操作”之前排序的内容(例如数字),也很有用