我试图找到文件中不符合特定模式的所有行。
一段时间以来,我在history
使用 GNU bash
(版本 4 和 5)时遇到了一个问题,其中命令出现重复。我认为这是因为我.bashrc
有以下行:
PROMPT_COMMAND="history -a; history -n; $PROMPT_COMMAND"
由于我使用终端多路复用器(screen
和/或tmux
),上述命令会执行多次(因此echo $PROMPT_COMMAND
会导致history -a; history -n; history -a; history -n;
在某些情况下(特别是在不同的窗格/窗口/框架/缓冲区上同时执行操作时),我输入的最后一个命令在我的~/.bash_history
.这导致了如下条目:
#1596110297
yadm list -a | xargs -t ls -l
yadm list -a | xargs -t ls -l
不用说,这很烦人。 我只是(希望)找到了该问题的解决方案 更正:这并没有解决history
(通过将命令更改为PROMPT_COMMAND="history -a; history -n
)但是history
.
现在我想删除重复的条目。
因此,我目前正在尝试找到一个正则表达式来标记除以 开头的行#
及其后一行之外的所有内容。我的第一个想法是结合grep -v
(反转选择)和grep -A 1
(在匹配模式后额外获得一行)。但
grep -v "^#" -A 1 ~/.bash_history
没有得到我希望的结果。
因此我的问题是:有人知道如何使用 来做到这一点吗grep
?如果不是:我如何使用其他工具(sed
、、awk
...)来完成此任务?
答案1
据我了解,grep -v "^#" -A 1
意味着打印不以井号开头的行,并且每行后一行。但你不想要相反的结果吗,打印以下行做以井号开始,然后一行?
给定一个测试文件:
#123
echo this
echo this
#456
echo that
echo that
echo that
#789
echo third
grep -A1 ^# history.txt |grep -vxFe --
印刷:
#123
echo this
#456
echo that
#789
echo third
第二个grep
是去掉组分隔符grep -A
打印。
或者uniq history.txt
应该只打印每组连续的相同行中的一个。
答案2
使用 Raku(née Perl6)
这似乎是“触发器”操作符的工作,可以在多种脚本语言中使用。以下是使用 Raku 编程语言(以前称为 Perl6)的答案。首先创建一个更广泛的测试文件:
$ cat repeated_log.txt
#1596110297_1
A_yadm list -a | xargs -t ls -l
B_yadm list -a | xargs -t ls -l
#1596110297_2
C_yadm list -a | xargs -t ls -l
D_yadm list -a | xargs -t ls -l
E_yadm list -a | xargs -t ls -l
#1596110297_3
F_yadm list -a | xargs -t ls -l
G_yadm list -a | xargs -t ls -l
H_yadm list -a | xargs -t ls -l
I_yadm list -a | xargs -t ls -l
#1596110297_4
#1596110297_5
fff
现在使用 Raku 的触发器运算符来编写一行代码,它实现了“类似 sed”的行为。对于第一个正则表达式看到(在行首^^
)文字“#”字符的行,捕获将打开。一旦打开,捕获将忽略第一个正则表达式并根据第二个正则表达式进行计算,当它找到与缺少(在行开头^^
)“#”字符的行匹配时关闭。 “负”正则表达式在下面的代码中使用 实现<-[#]>
,它是一个负“枚举字符类”,也是 Raku 语言的一个真正功能:
$ raku -ne '.put if /^^ "#" / fff /^^ <-[#]> /;' repeated_log.txt
#1596110297_1
A_yadm list -a | xargs -t ls -l
#1596110297_2
C_yadm list -a | xargs -t ls -l
#1596110297_3
F_yadm list -a | xargs -t ls -l
#1596110297_4
#1596110297_5
实际上,第一个正则表达式(中缀运算符左侧fff
)可以使用<+[#]>
正“枚举字符类”来编写,以实现更并行的构造:
$ raku -ne '.put if /^^ <+[#]> / fff /^^ <-[#]> /;' repeated_log.txt
#1596110297_1
A_yadm list -a | xargs -t ls -l
#1596110297_2
C_yadm list -a | xargs -t ls -l
#1596110297_3
F_yadm list -a | xargs -t ls -l
#1596110297_4
#1596110297_5
另外,在我看来,您可以通过要求匹配或反对行首“#”后跟一个或多个数字来改进您的正则表达式,即<digit>+
,见下文:
$ raku -ne '.put if /^^ <+[#]> <digit>+ / fff /^^ <-[#]> <-digit>+ /;' repeated_log.txt
#1596110297_1
A_yadm list -a | xargs -t ls -l
#1596110297_2
C_yadm list -a | xargs -t ls -l
#1596110297_3
F_yadm list -a | xargs -t ls -l
#1596110297_4
#1596110297_5
[上面的所有代码都会删除以 B、D、E、G、H 和 I 开头的重复行。我注意到的唯一怪癖是两个连续的目标行(例如“#1596110297”)将出现在输出中,但不清楚对我来说,如果您的输入文件将包含这样的连续行]。