Answers:
正则表达式中的星号表示“匹配前面的元素0次或多次”。
在您使用的特殊情况下grep 'This*String' file.txt
,您尝试说:“嘿,grep,请给我匹配单词Thi
,然后小写s
零次或多次,然后再单词String
”。小写字母s
无处可寻Example
,因此grep会忽略ThisExampleString
。
在的情况下grep '*String' file.txt
,您说的是“ grep,请给我匹配一个空字符串-实际上什么都没有-在单词之前String
”。当然,这不是ThisExampleString
应该阅读的方式。(还有其他可能的含义 -您可以在有和没有-E
标志的情况下尝试使用-但其中的任何含义都不是您真正想要的含义。)
知道这.
意味着“任何单个字符”,我们可以这样做:grep 'This.*String' file.txt
。现在,grep命令将正确读取它:This
跟着重复任意次的任何字符(认为是选择ASCII字符),然后跟着String
。
*
是一个特殊字符,它应该被引用或转义比如像这样:grep 'This*String' file.txt
或者这样:grep This\*String file.txt
不被意外的结果感到惊讶。
strace grep .* file.txt |& head -n 1
和strace grep '.*' file.txt |& head -n 1
。实际上grep
也可以与任何Unicode字符一起使用(例如echo -ne ⇏ | grep ⇏
输出⇏
)
bash
。这意味着首先bash
解释其特殊字符,并且仅在执行完所有扩展之后,才将参数传递给生成的进程。-----例如,在Bash:中的此命令grep This.\*String file.txt
将/bin/grep
使用以下参数0:grep
,1 : This.*String
、 2:生成file.txt
。请注意,Bash删除了反斜杠,并且原先转义的字符已按原义*
传递。
grep This.*String file.txt
通常可以正常工作,因为很可能不会有与外壳通配符表达式匹配的文件This.*String
。在这种情况下,默认情况下,Bash将按原样传递包含的参数*
。
的*
在BRE元字符1个 S,ERE 1秒和PCRE 1 S匹配0或多个正好先前分组图案的(如果是分组图案前述*
元字符),前一个字符类的0或多个正好(如果字符类是*
(如果在该元字符之前没有分组模式或一个字符类),或者前一个字符出现0次或更多次*
;
这意味着在This*String
模式中,作为*
元字符而不是分组模式或字符类之前的*
元字符,该元字符匹配前一个字符(在此情况下为s
字符)出现0次或更多次:
% cat infile
ThisExampleString
ThisString
ThissString
% grep 'This*String' infile
ThisString
ThissString
若要匹配任何字符的0个或多个出现,您想匹配.
与任何字符匹配的元字符的0个或更多个出现:
% cat infile
ThisExampleString
% grep 'This.*String' infile
ThisExampleString
*
BRE和ERE中的元字符始终是“贪婪的”,即它将匹配最长的匹配项:
% cat infile
ThisExampleStringIsAString
% grep -o 'This.*String' infile
ThisExampleStringIsAString
这可能不是理想的行为;如果不是,则可以打开grep
PCRE引擎(使用该-P
选项)并附加?
元字符,将其放在*
和+
元字符之后可改变其贪婪程度:
% cat infile
ThisExampleStringIsAString
% grep -Po 'This.*?String' infile
ThisExampleString
1:基本正则表达式,扩展正则表达式和与Perl兼容的正则表达式
*
作为shell globbing字符(“通配符”)和正则表达式metacharacter都有特殊含义。您必须将两者都考虑在内,尽管如果您引用正则表达式,则可以防止shell对其进行特殊对待,并确保将shell不变地传递给grep
。尽管从概念上来说有点相似,但是*
对shell的含义与对shell的含义却大不相同grep
。
*
视为通配符。你说:
表达式是否用引号引起来没有区别。
这取决于运行命令时碰巧所在目录中的文件。对于包含目录分隔符的模式/
,它可能取决于整个系统中存在哪些文件。你应该总是引用正则表达式grep
--and 单引号通常best-- ,除非你确定你是好与九种可能令人惊讶的转变的外壳否则执行之前执行的grep
命令。
当shell遇到*
未加引号的字符时,它将用它表示“零个或多个字符”,并将包含该字符的单词替换为与该模式匹配的文件名列表。(以文件名开头的文件名.
除外-除非您的模式本身以文件名开头,.
或者您已将shell配置为包括它们在内。)这被称为“ 遍历” - 以及文件名扩展名和路径名扩展名。
的效果grep
通常是将第一个匹配的文件名当作正则表达式使用-即使对于人类读者来说很明显这并不意味着是正则表达式-而所有其他文件名会从glob被用作在其中搜索匹配项的文件。(您看不到该列表,它是不透明地传递给的grep
。)您几乎从不希望这种情况发生。
究其原因,这是有时不是一个问题-在您的特定情况下,至少到目前为止,它wasn't -是,*
将被单独留在家中,如果以下所有条件都为真:
有没有文件的名字相匹配。 ...或者您通常使用set -f
或等效项禁用了外壳中的Globing set -o noglob
。但这并不常见,您可能会知道自己做到了。
您使用的外壳程序的默认行为是*
在没有匹配的文件名时不理会。在您可能正在使用的Bash中就是这种情况,但并非在所有Bourne风格的shell中都是如此。(例如,流行的Shell Zsh中的默认行为是使glob (a)扩展或(b)产生错误。)...或者您已经更改了Shell的这种行为-这样做的方式各不相同跨壳。
您还没有以其他方式告诉你的shell,让水珠与被替换什么时,有没有匹配的文件,也没有失败,在这种情况下的错误消息。在Bash中,可以分别通过启用nullglob
或failglob
shell选项来完成。
有时您可以依靠#2和#3,但很少可以依靠#1。一个grep
与现在的作品可能会停止,当你有不同的文件,或者当您从不同的地方运行工作的不带引号模式命令。引用您的正则表达式,问题就消失了。
grep
命令将*
视为量词。其他答案(例如Sergiy Kolodyazhnyy和kos的答案)也以不同的方式解决了这个问题的这一方面。因此,我鼓励在阅读本答案其余部分之前或之后尚未阅读的人进行阅读。
假设*
确实做到了grep(应确保引用),grep
然后将其视为意味着它前面的项目可以出现任意次,而不必完全发生一次。它仍然可能发生一次。或者它可能根本不存在。或者可以重复。符合所有这些可能性的文本将被匹配。
我所说的“项目”是什么意思?
一个字符。由于b
匹配文字b
,b*
匹配零个或多个b
S,从而ab*c
匹配ac
,abc
,abbc
,abbbc
,等。
类似地,由于.
匹配的任何字符,.*
匹配零个或多个字符1,从而a.*c
匹配ac
,akc
,ahjglhdfjkdlgjdfkshlgc
,即使是acccccchjckhcc
等或者
一个字符类。由于[xy]
火柴x
或y
,[xy]*
匹配零个或多个字符,其中每一个或者是x
或y
,从而p[xy]*q
匹配pq
,pxq
,pyq
,pxxq
,pxyq
,pyxq
,pyyq
,pxxxq
,pxxyq
,等。
这也适用于速记形式像字符类的\w
,\W
,\s
,和\S
。由于\w
匹配任何单词字符,因此\w*
匹配零个或多个单词字符。要么
一个小组。由于\(bar\)
火柴bar
,\(bar\)*
匹配零个或更多bar
S,从而foo\(bar\)*baz
匹配foobaz
,foobarbaz
,foobarbarbaz
,foobarbarbarbaz
,等。
使用-E
或-P
选项,grep
将您的正则表达式分别视为ERE或PCRE,而不是BRE,然后用(
)
代替包围组\(
\)
,因此您可以使用(bar)
代替\(bar\)
和foo(bar)baz
代替foo\(bar\)baz
。
man grep
最后给出了对BRE和ERE语法的合理理解的解释,并grep
在开头列出了所有可接受的命令行选项。我建议将该手册页作为资源,以及GNU Grep文档和本教程/参考站点(我已链接至上面的许多页面)作为资源。
为了进行测试和学习grep
,我建议使用模式而不是文件名来调用它。然后它从您的终端接收输入。输入行;回显给您的行是包含您的模式匹配的文本的行。要退出,请在行的开头按Ctrl+ D,表示输入结束。(或者您可以像大多数命令行程序一样按Ctrl+ C。)例如:
grep 'This.*String'
如果使用该--color
标志,grep
将突出显示与正则表达式匹配的行的特定部分,这对于弄清楚正则表达式的作用以及一次查找后的查找都非常有用。默认情况下,grep --color=auto
当您从命令行运行时,Ubuntu用户具有一个Bash别名,可以grep
使它运行(对于此目的就足够了),因此您甚至不需要--color
手动传递。
1 因此.*
,在正则表达式中是*
指Shell glob中的含义。但是,不同之处在于,它会grep
自动在其中任何位置打印包含您的匹配项的行,因此通常不需要.*
在正则表达式的开头或结尾处都包含该行。
* != any number of unknown characters
。阅读文档。)