我正在学习Linux,但我面临的挑战似乎是我自己无法解决。这里是:
grep文件中的一行,该行连续包含4个数字,但不超过4。
我不确定该如何处理。我可以搜索特定数字,但不能搜索字符串中的数字。
\b\d{4}\b
我正在学习Linux,但我面临的挑战似乎是我自己无法解决。这里是:
grep文件中的一行,该行连续包含4个数字,但不超过4。
我不确定该如何处理。我可以搜索特定数字,但不能搜索字符串中的数字。
\b\d{4}\b
Answers:
有两种方法可以解释这个问题。我将解决这两种情况。您可能要显示以下行:
例如,(1)将显示1234a56789
,但(2)将不显示。
如果要显示包含四位数字的序列的所有行,这些行本身不属于任何更长的数字序列,则一种方法是:
grep -P '(?<!\d)\d{4}(?!\d)' file
这使用Perl正则表达式,Ubuntu grep
(GNU grep)通过Perl支持-P
。它不会12345
与之类的文本匹配,也不会与1234
或2345
其中的一部分匹配。但它将与1234
in相匹配1234a56789
。
在Perl正则表达式中:
\d
表示任何数字(这是一个简短的说法[0-9]
或[[:digit:]]
)。x{4}
匹配x
4次。({
}
语法并非特定于Perl正则表达式;它也通过扩展正则表达式存在grep -E
。)因此\d{4}
与相同\d\d\d\d
。(?<!\d)
是零宽度的负向后看断言。它的意思是“除非前面有” \d
。(?!\d)
是零宽度的否定超前断言。它的意思是“除非紧随其后\d
”。(?<!\d)
并且(?!\d)
不匹配四位数序列之外的文本;相反,如果它们是较长数字序列的一部分,则它们(一起使用时)将防止四个数字序列本身被匹配。
仅使用先行查找或仅预读是不够的,因为最右边或最左边的四位数子序列仍将匹配。
使用先后断言和先行断言的一个好处是您的模式仅匹配四位数字本身,而不匹配周围的文本。使用颜色突出显示(带有--color
选项)时,这很有用。
ek@Io:~$ grep -P '(?<!\d)\d{4}(?!\d)' <<< 12345abc789d0123e4
12345abc789d0123e4
在Ubuntu中,默认情况下,每个用户都alias grep='grep --color=auto'
在其~.bashrc
文件中。因此,当您运行一个简单的命令开头时grep
(这是扩展别名时),而标准输出是终端(这是检查的内容),则您会自动获得颜色突出显示。比赛通常以红色(接近朱红色)突出显示,但我已将其显示为斜体。这是屏幕截图:--color=auto
您甚至可以使用以下命令grep
打印仅匹配的文本,而不是整行-o
:
ek@Io:~$ grep -oP '(?<!\d)\d{4}(?!\d)' <<< 12345abc789d0123e4
0123
但是,如果您:
grep
不支持-P
或不想使用Perl正则表达式的系统上运行的命令,并且...然后您可以使用扩展的正则表达式来实现:
grep -E '(^|[^0-9])[0-9]{4}($|[^0-9])' file
这匹配四个数字,非数字字符(或行的开头或结尾)包围它们。特别:
[0-9]
匹配任何数字(如[[:digit:]]
或\d
Perl正则表达式中的数字),并{4}
表示“四次”。因此[0-9]{4}
匹配一个四位数的序列。[^0-9]
不在的范围相匹配的字符0
通过9
。它等效于[^[:digit:]]
(或\D
,在Perl正则表达式中)。^
,当它没有出现在[
]
方括号中时,匹配行的开头。同样,$
匹配行尾。|
装置或与括号里的分组(如在代数)。因此,(^|[^0-9])
匹配行的开头或非数字字符,而($|[^0-9])
匹配行的结尾或非数字字符。因此,匹配仅发生在同时包含四位数序列([0-9]{4}
)的行中:
(^|[^0-9])
),并且($|[^0-9])
)。另一方面,如果您要显示所有包含四位数序列但不包含任何四位数以上序列的行(即使是与另一只四位数序列分开的序列),则从概念上讲,目标是找到匹配一种模式但不匹配另一种模式的线。
因此,即使您知道如何使用单个模式,我也建议您使用matt的第二个建议,分别grep
为两种模式使用。
在执行此操作时,您不会从Perl正则表达式的任何高级功能中强烈受益,因此您可能不希望使用它们。但是与上述样式保持一致,这是使用(和花括号)代替以下方法简化了Matt的解决方案:\d
[0-9]
grep -P '\d{4}' file | grep -Pv '\d{5}'
由于使用[0-9]
,因此matt的方式更具可移植性-它将在grep
不支持Perl正则表达式的系统上运行。如果使用[0-9]
(或[[:digit:]]
)代替\d
,但继续使用{
}
,则可以更简洁地获得matt方法的可移植性:
grep -E '[0-9]{4}' file | grep -Ev '[0-9]{5}'
如果您确实喜欢使用以下grep
命令
grep
用管道分隔的)...然后您可以使用:
grep -Px '(\d{0,4}\D)*\d{4}(\D\d{0,4})*' file
该-x
标志grep
仅显示整行匹配的行(而不是包含匹配项的任何行)。
我使用了Perl正则表达式,因为在这种情况下,我认为简洁明了\d
并\D
大大提高了清晰度。但是,如果您需要一些可移植到grep
不支持系统的系统,则-P
可以用[0-9]
和[^0-9]
(或用[[:digit:]]
和[^[:digit]]
)替换它们:
grep -Ex '([0-9]{0,4}[^0-9])*[0-9]{4}([^0-9][0-9]{0,4})*' file
这些正则表达式的工作方式是:
在中间,\d{4}
或[0-9]{4}
匹配一个四位数的序列。我们可能有不止一种,但我们至少需要有一种。
在左侧,(\d{0,4}\D)*
或([0-9]{0,4}[^0-9])*
与零个或多个(*
)实例(不超过四位数字,后跟一个非数字)匹配。零位数(即为零)是“不超过四位数”的一种可能性。这匹配(a)空字符串或(b)以非数字结尾且不包含任何四位数以上的序列的任何字符串。
由于紧靠中心\d{4}
(或[0-9]{4}
)左边的文本必须为空或以非数字结尾,因此可以防止中心\d{4}
匹配四位数字,而四位数字恰好在其左边有另一个(第五位)数字。
在右侧,(\D\d{0,4})*
或([^0-9][0-9]{0,4})*
匹配零个或多个(*
)非数字实例,后跟不超过四个数字(与以前一样,可以是四个,三个,两个,一个,甚至根本没有)。此匹配的(a)的空字符串或(b)中的任何字符串开始在一个非数字的和不含有多于四个数字的任何序列。
由于紧靠中心\d{4}
(或[0-9]{4}
)右边的文本必须为空或以非数字开头,因此可以防止中心\d{4}
匹配四位数字,而四位数字恰好在其右边有另一个(第五位)数字。
这样可确保在某处出现四位数的序列,并且在任何地方都不会出现五位或更多位数的序列。
这样做是不错的,也没有错。但是,考虑使用此替代方法的最重要原因可能是,它阐明了使用替代方法(或类似方法)的好处,如上面和马特答案中所建议。grep -P '\d{4}' file | grep -Pv '\d{5}'
通过这种方式,很明显,您的目标是选择包含一件事而不包含另一件事的行。另外,语法更简单(因此许多读者/维护人员可能会更快地理解它)。
这将连续显示4个数字,但不会更多
grep '[0-9][0-9][0-9][0-9][^0-9]' file
注意^表示不是
尽管我不确定如何解决,但是有一个问题……如果数字是该行的末尾,那么它将不会显示。
但是,此较丑的版本适用于这种情况
grep '[0-9][0-9][0-9][0-9]' file | grep -v [0-9][0-9][0-9][0-9][0-9]
a12345b
,因为它匹配2345b
。
如果grep
不支持perl正则表达式(-P
),请使用以下shell命令:
grep -w "$(printf '[0-9]%.0s' {1..4})" file
哪里printf '[0-9]%.0s' {1..4}
会产生4倍[0-9]
。当您有长数字并且不想重复模式时(只用4
要查找的数字替换),此方法很有用。
使用-w
将查找整个单词。但是,如果您对字母数字字符串(例如)感兴趣,请在模式末尾1234a
添加[^0-9]
,例如
grep "$(printf '[0-9]%.0s' {1..4})[^0-9]" file
1234a12345
显示类似的行?