Windows FINDSTR命令有哪些未记录的功能和限制?

209
Windows FINDSTR命令的文档非常糟糕。可以通过FINDSTR /?HELP FINDSTR获得基本的命令行帮助,但它非常不充分。在https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/findstr上线有更多的文档。
许多FINDSTR功能和限制在文档中甚至没有提到。如果没有事先的知识和/或仔细的实验,也无法预料到。
因此,问题是 - 未记录的FINDSTR功能和限制是什么?
这个问题的目的是提供一个一站式库,其中包含许多未记录的功能,以便:
A)开发人员可以充分利用现有的功能。
B)开发人员不会浪费时间想知道为什么某些东西不起作用,当它看起来应该工作时。
请确保您了解现有的文档,然后再回答问题。如果信息已被HELP覆盖,则不属于此处。
这也不是展示FINDSTR有趣用途的地方。如果一个合乎逻辑的人可以根据文档预测特定使用FINDSTR的行为,那么它就不属于这里。

在同样的思路下,如果一个逻辑性强的人能够根据任何现有答案中包含的信息预测特定用法的行为,那么它也不属于这里。


16
或者,你可以完全放弃那个没有文档的糟糕 MS 工具,安装/使用grep。这个工具非常被理解和有文档说明 :-) 比如,可参考 https://dev59.com/JXE85IYBdhLWcg3wvGKm 。 - paxdiablo
22
如果你有条件使用 FINDSTR 以外的其他工具,那么强烈建议你这样做。但有些人所处的环境禁止使用第三方实用程序。 - dbenham
5
没关系。我认真考虑过自己加入一个类似于你评论的FINDSTR声明! :) - dbenham
50
我很震惊和失望,有人发现这个问题“不够建设性”,并投票关闭。这个问题经过了很多思考,特别是为了避免“观点、争论、辩论、民意调查或长时间的讨论”。这个问题已经发布了3.5个月了,没有出现所提到的任何负面情况。配对的答案充满了事实,并需要许多小时的艰苦研究和实验。 - dbenham
7
一些读者可能对findstr命令的历史背景感兴趣:http://blogs.msdn.com/b/oldnewthing/archive/2012/11/28/10372436.aspx - Harry Johnston
显示剩余15条评论
8个回答

306

前言
本回答中的许多信息都是基于在Vista机器上运行的实验收集而来的。除非明确说明,否则我没有确认这些信息是否适用于其他Windows版本。

FINDSTR输出
文档从未费心解释FINDSTR的输出。它暗示了匹配的行将被打印出来,但没有更多的解释。

匹配行的输出格式如下:

文件名:行号:行偏移量:文本

其中

文件名: = 包含匹配行的文件的名称。如果请求是针对单个文件的,或者搜索管道输入或重定向输入,则不打印文件名。当打印时,文件名将始终包括提供的任何路径信息。如果使用了/S选项,则会添加额外的路径信息。打印的路径始终相对于提供的路径,或者如果没有提供,则相对于当前目录。

注意 - 当搜索多个文件时,可以使用非标准(且文档不完善)通配符<>来避免文件名前缀。这些通配符的工作规则可以在此处找到。最后,您可以查看此示例以了解非标准通配符如何与FINDSTR一起使用lineNumber: = 匹配行的行号,表示为十进制值,其中1表示输入的第1行。仅在指定了/N选项时打印。 lineOffset: = 匹配行的十进制字节偏移量,其中0表示第一行的第一个字符。仅在指定了/O选项时才打印。这不是行内匹配的偏移量。它是从文件开头到行开头的字节数。
text = 匹配行的二进制表示,包括任何<CR>和/或<LF>。二进制输出不会留下任何内容,因此匹配所有行的示例将生成原始文件的精确二进制副本。
FINDSTR "^" FILE >FILE_COPY

/A 选项仅设置 fileName:、lineNumber: 和 lineOffset: 输出的颜色。 匹配行的文本始终以当前控制台颜色输出。 /A 选项仅在直接显示到控制台时才有效果。如果输出被重定向到文件或管道,则 /A 选项无效。请参见2018-08-18 Aacini 在答案中的编辑,了解当输出被重定向到 CON 时出现故障行为的描述。

大多数控制字符和许多扩展 ASCII 字符在 XP 上显示为点
XP 上的 FINDSTR 将匹配行中的大多数不可打印控制字符显示为点(句号)在屏幕上。以下控制字符是例外:0x09 制表符、0x0A 换行符、0x0B 垂直制表符、0x0C 换页符、0x0D 回车符。

XP FINDSTR 还将许多扩展 ASCII 字符转换为点。在 XP 上显示为点的扩展 ASCII 字符与在命令行中提供时转换的字符相同。请参见本帖子后面的“命令行参数的字符限制 - 扩展 ASCII 转换”部分。

控制字符和扩展ASCII字符如果输出被管道、重定向到文件或在FOR IN()子句中,不会在XP上转换成点号。
Vista和Windows 7始终以它们自己的形式显示所有字符,而不是点号。
返回代码(ERRORLEVEL):
  • 0(成功)
    • 至少在一个文件的至少一行中找到了匹配项。
  • 1(失败)
    • 在任何文件的任何行中都没有找到匹配项。
    • /A:xx选项指定的颜色无效。
  • 2(错误)
    • 同时指定了不兼容的选项/L/R
    • /A:/F:/C:/D:/G:后缺少参数。
    • /F:file/G:file指定的文件未找到。
  • 255(错误)

搜索数据源 (根据使用 Windows 7 的测试更新)
Findstr 可以从以下数据源中搜索数据:

  • 作为参数指定的文件名和/或使用 /F:file 选项。

  • 通过重定向的标准输入 findstr "searchString" <file

  • 来自管道的数据流 type file | findstr "searchString"

参数/选项优先于重定向,重定向优先于管道数据。

文件名参数和 /F:file 可以组合使用。可以使用多个文件名参数。如果指定了多个 /F:file 选项,则仅使用最后一个选项。通配符允许在文件名参数中使用,但不允许在由 /F:file 指定的文件中使用。

搜索字符串的来源 (根据使用 Windows 7 的测试更新)
/G:file/C:string 选项可以组合使用,可以指定多个 /C:string 选项。如果指定了多个 /G:file 选项,则只使用最后一个选项。如果使用了 /G:file/C:string 中的任何一个选项,则所有非选项参数都被认为是要进行搜索的文件。如果既没有使用 /G:file 也没有使用 /C:string,则第一个非选项参数被视为用空格分隔的搜索词列表。

使用 /F:FILE 选项时,文件名不能在文件内加引号。
文件名可能包含空格和其他特殊字符。大多数命令要求使用引号括起来。但是,使用 FINDSTR 的 /F:files.txt 选项时,必须在 files.txt 文件中的文件名不加引号。如果文件名加了引号,则无法找到该文件。

BUG - 短的8.3文件名可能会破坏/D和/S选项
与所有Windows命令一样,FINDSTR在查找要搜索的文件时将尝试匹配长名称和短的8.3名称。假设当前文件夹包含以下非空文件:

b1.txt
b.txt2
c.txt

以下命令将成功找到所有3个文件:
findstr /m "^" *.txt

b.txt2匹配成功,因为相应的短名称B9F64~1.TXT匹配成功。这与所有其他Windows命令的行为一致。

但是,在使用/D/S选项时存在一个错误,导致以下命令只能找到b1.txt

findstr /m /d:. "^" *.txt
findstr /m /s "^" *.txt

该漏洞会导致无法找到b.txt2,以及在同一目录中排序在b.txt2之后的所有文件名。可以找到排序在之前的其他文件,如a.txt。但是一旦触发了这个漏洞,像d.txt这样排序较晚的其他文件也会被错过。
每个搜索的目录都是独立处理的。例如,/S选项在父文件夹中未找到文件后会成功开始在子文件夹中搜索,但是一旦漏洞导致在子文件夹中错过了一个短文件名,则该子文件夹中的所有后续文件也将被错过。
如果在禁用NTFS 8.3名称生成的机器上创建相同的文件名,则命令可以正常工作。当然,b.txt2将无法找到,但c.txt将被正确找到。
并非所有的短名称都会触发该漏洞。我见过的所有有问题的行为实例都涉及扩展名超过3个字符的带有短8.3名称的文件,其起始与不需要8.3名称的普通名称相同。
已确认该漏洞存在于XP、Vista和Windows 7中。 不可打印字符和/P选项
/P选项会导致FINDSTR跳过包含以下十进制字节码的文件:
0-7,14-25,27-31。

换句话说,/P选项仅会跳过包含不可打印控制字符的文件。控制字符是小于或等于31(0x1F)的代码。FINDSTR将以下控制字符视为可打印:

 8  0x08  backspace
 9  0x09  horizontal tab
10  0x0A  line feed
11  0x0B  vertical tab
12  0x0C  form feed
13  0x0D  carriage return
26  0x1A  substitute (end of text)

所有其他的控制字符都被视为不可打印字符,它们的存在会导致/P选项跳过文件。

管道和重定向输入可能会添加<CR><LF>
如果输入是通过管道传递的,并且流的最后一个字符不是<LF>,那么FINDSTR将自动在输入末尾添加<CR><LF>。这在XP、Vista和Windows 7上已经得到确认。(我曾经认为Windows管道负责修改输入,但我后来发现实际上是FINDSTR在进行修改。)

对于Vista上的重定向输入也是如此。如果用作重定向输入的文件的最后一个字符不是<LF>,那么FINDSTR将自动在输入末尾添加<CR><LF>。然而,XP和Windows 7不会改变重定向输入。

在XP和Windows 7上,如果重定向的输入文件不以结尾,则FINDSTR会挂起。这是一个讨厌的“特性”。一旦到达重定向文件的末尾,FINDSTR将无限期地挂起。

如果输入是通过管道传输的,并且最后一行由单个字符组成且没有跟随,则FINDSTR将完全忽略最后一行。

示例-第一个只有一个字符且没有的命令无法匹配,但第二个有2个字符的命令可以正常工作,第三个具有终止换行符的一个字符的命令也可以正常工作。

> set /p "=x" <nul | findstr "^"

> set /p "=xx" <nul | findstr "^"
xx

> echo x| findstr "^"
x

由DosTips用户Sponge Belly在new findstr bug上报告。已在XP、Windows 7和Windows 8上确认。还没有关于Vista的消息。(我不再拥有Vista进行测试)。

选项语法
选项字母不区分大小写,因此/i/I是等效的。

选项可以以/-为前缀 选项可以在单个/-后连接。但是,连接的选项列表最多只能包含一个OFF或F:等多字符选项,并且多字符选项必须是列表中的最后一个选项。

以下是表达任何顺序包含“hello”和“goodbye”的不区分大小写正则表达式搜索的等效方式

  • /i /r /c:"hello.*goodbye" /c:"goodbye.*hello"

  • -i -r -c:"hello.*goodbye" /c:"goodbye.*hello"

  • /irc:"hello.*goodbye" /c:"goodbye.*hello"

选项也可以用引号括起来。因此,/i-i"/i""-i"都是等价的。同样地,/c:string"/c":string"/c:"string"/c:string"都是等价的。

如果搜索字符串以斜杠/或减号-开头,则必须使用/C/G选项。感谢Stephan在评论中报告了这个问题(已删除)。

如果使用了/c:string/g:file选项,则即使加上引号,命令也会因文件名参数以-开头而失败。这是因为没有搜索字符串参数,所以文件名参数被视为选项。最简单的解决方法是在文件参数前加上点和反斜杠,例如
findstr /c:"searchString" ".\-fileName.txt"

搜索字符串长度限制
在 Vista 系统中,单个搜索字符串的最大允许长度为 511 字节。如果任何搜索字符串超过 511,则结果将是一个带有 ERRORLEVEL 2 的 FINDSTR: Search string too long. 错误。

进行正则表达式搜索时,最大搜索字符串长度为 254。长度介于 255 和 511 之间的正则表达式将导致带有 ERRORLEVEL 2 的 FINDSTR: Out of memory 错误。长度 >511 的正则表达式将导致 FINDSTR: Search string too long. 错误。

在 Windows XP 中,搜索字符串长度显然更短。Findstr error: "Search string too long": How to extract and match substring in "for" loop? XP 限制为 127 字节,适用于字面和正则搜索。

行长度限制
作为命令行参数或通过 /F:FILE 选项指定的文件没有已知的行长度限制。成功运行了针对一个不包含单个 <LF> 的 128MB 文件的搜索。

Piped数据和重定向输入每行限制为8191个字节。这个限制是FINDSTR的“特性”。它不是管道或重定向本身的固有特性。使用重定向stdin或管道输入的FINDSTR永远不会匹配任何大于等于8k字节的行。大于等于8k的行会生成一个错误消息到stderr,但如果搜索字符串至少在一个文件的至少一行中找到,则ERRORLEVEL仍然为0。
默认搜索类型:字面值与正则表达式
/C:“string” - 默认值为/L字面值。明确将/L选项与/C:“string”组合肯定有效,但是多余的。

"string argument" - 默认取决于第一个搜索字符串的内容。 (请记住,<space>用于分隔搜索字符串。)如果第一个搜索字符串是包含至少一个未转义元字符的有效正则表达式,则所有搜索字符串都被视为正则表达式。否则,所有搜索字符串都将被视为字面值。例如,"51.4 200"将被视为两个正则表达式,因为第一个字符串包含一个未转义的点,而"200 51.4"将被视为两个字面值,因为第一个字符串不包含任何元字符。

/G:file - 默认取决于文件中第一行非空行的内容。如果第一个搜索字符串是包含至少一个未转义元字符的有效正则表达式,则所有搜索字符串都被视为正则表达式。否则,所有搜索字符串都将被视为字面值。

建议-始终在使用"string argument"/G:file时明确指定/L字面量选项或/R正则表达式选项。

Bug-指定多个字面搜索字符串可能会导致不可靠的结果

以下简单的FINDSTR示例未能找到匹配项,尽管它应该可以。
echo ffffaaa|findstr /l "ffffaaa faffaffddd"

这个bug已在Windows Server 2003,Windows XP,Vista和Windows 7上确认。
根据实验,如果满足以下所有条件,FINDSTR可能会失败:
- 搜索使用多个文字搜索字符串 - 搜索字符串长度不同 - 短搜索字符串与长搜索字符串有一定重叠 - 搜索区分大小写(没有/I选项)
在我看到的每一个失败中,总是较短的搜索字符串失败。
更多信息请参见为什么这个带有多个文字搜索字符串的FINDSTR示例找不到匹配项?

命令行参数中的引号和反斜杠
注意:用户MC ND的评论反映了这一部分实际上非常复杂的规则。涉及到三个不同的解析阶段:

  • 首先,cmd.exe可能需要将一些引号转义为^"(与FINDSTR无关)
  • 接下来,FINDSTR使用pre 2008 MS C/C++ argument parser,该解析器对"和\具有特殊规则。
  • 当参数解析器完成后,FINDSTR还会将\后跟字母数字字符视为文字,但将\后跟非字母数字字符视为转义字符

此突出显示部分的其余部分并不完全正确。它可以作为许多情况的指南,但是要完全理解,需要遵循上述规则。

在命令行搜索字符串中转义引号
命令行搜索字符串中的引号必须使用反斜杠进行转义,例如\"。这适用于文字和正则表达式搜索字符串。这已在XP、Vista和Windows 7上得到确认。

注意:引号也可能需要在CMD.EXE解析器中进行转义,但这与FINDSTR无关。例如,要搜索单引号,可以使用:

FINDSTR \^" file && echo found || echo not found

在命令行文字搜索字符串中转义反斜杠
文字搜索字符串中的反斜杠通常可以表示为\\\。它们通常是等效的。(在Vista中可能存在一些异常情况,其中必须始终转义反斜杠,但我没有Vista机器进行测试)

但是有一些特殊情况:

当搜索连续的反斜杠时,除了最后一个之外,其余都必须转义。最后一个反斜杠可以选择转义。

  • \\可以编码为\\\\\\\
  • \\\可以编码为\\\\\\\\\\\

在引号之前搜索一个或多个反斜杠是奇怪的。逻辑会认为引号必须被转义,并且每个前置反斜杠都需要被转义,但是这样不起作用!相反,必须双倍转义每个前置反斜杠,并正常转义引号:

  • \"必须编码为\\\\\"
  • \\"必须编码为\\\\\\\\\"

如前所述,一个或多个转义引号可能还需要使用^进行CMD解析器的转义

此部分中的信息已在XP和Windows 7上得到确认。

在命令行正则表达式搜索字符串中转义反斜杠

  • Vista only: 正则表达式中的反斜杠必须要么双倍转义,例如\\\\,要么在字符类集合中单独转义,例如[\\]

    在/G:FILE文字搜索字符串中转义引号和反斜杠
    在由/G:file指定的文字搜索字符串文件中,独立的引号和反斜杠不需要转义,但是可以转义。

    "\"是等价的。

    \\\是等价的。

    如果意图查找\\,则至少需要转义前导反斜杠。\\\\\\\都可以使用。

    如果意图查找",则至少需要转义前导反斜杠。\\"\\\"都可以使用。

    在/G:FILE正则表达式搜索字符串中转义引号和反斜杠
    这是唯一一个根据文档预期工作的情况。引号不是正则表达式元字符,因此不需要转义(但可以转义)。反斜杠是正则表达式元字符,因此必须转义。

    命令行参数的字符限制 - 扩展ASCII转换
    在命令行上的任何字符串中不能出现空字符(0x00)。其他单字节字符可以出现在字符串中(0x01 - 0xFF)。然而,FINDSTR会将许多在命令行参数中发现的扩展ASCII字符转换为其他字符。这有两个主要影响:

    1. 如果在命令行上用作搜索字符串,则许多扩展ASCII字符将无法与自身匹配。这个限制对于字面和正则搜索都是一样的。如果搜索字符串必须包含扩展ASCII,则应该使用/G:FILE选项。

    2. 如果名称包含扩展ASCII字符并且文件名在命令行上指定,则FINDSTR可能无法找到文件。如果要搜索的文件名包含扩展ASCII,则应该使用/F:FILE选项。

    以下是FINDSTR在命令行字符串上执行的扩展ASCII字符转换的完整列表。每个字符都表示为十进制字节码值。第一个代码表示在命令行中提供的字符,第二个代码表示它被转换成的字符。注意-此列表是在美国机器上编译的。我不知道其他语言可能对此列表产生什么影响。
    158 treated as 080     199 treated as 221     226 treated as 071
    169 treated as 170     200 treated as 043     227 treated as 112
    176 treated as 221     201 treated as 043     228 treated as 083
    177 treated as 221     202 treated as 045     229 treated as 115
    178 treated as 221     203 treated as 045     231 treated as 116
    179 treated as 221     204 treated as 221     232 treated as 070
    180 treated as 221     205 treated as 045     233 treated as 084
    181 treated as 221     206 treated as 043     234 treated as 079
    182 treated as 221     207 treated as 045     235 treated as 100
    183 treated as 043     208 treated as 045     236 treated as 056
    184 treated as 043     209 treated as 045     237 treated as 102
    185 treated as 221     210 treated as 045     238 treated as 101
    186 treated as 221     211 treated as 043     239 treated as 110
    187 treated as 043     212 treated as 043     240 treated as 061
    188 treated as 043     213 treated as 043     242 treated as 061
    189 treated as 043     214 treated as 043     243 treated as 061
    190 treated as 043     215 treated as 043     244 treated as 040
    191 treated as 043     216 treated as 043     245 treated as 041
    192 treated as 043     217 treated as 043     247 treated as 126
    193 treated as 045     218 treated as 043     249 treated as 250
    194 treated as 045     219 treated as 221     251 treated as 118
    195 treated as 043     220 treated as 095     252 treated as 110
    196 treated as 045     222 treated as 221     254 treated as 221
    197 treated as 043     223 treated as 095
    198 treated as 221     224 treated as 097
    

    任何一个大于0的字符,如果不在上述列表中,则被视为其自身,包括<CR><LF>。将奇怪的字符(如<CR><LF>)包含到环境变量中,并在命令行参数中使用延迟扩展是最简单的方法。
    /G:FILE和/F:FILE选项指定的文件中找到的字符串的字符限制 nul(0x00)字符可以出现在文件中,但它的作用类似于C字符串终止符。在nul字符之后的任何字符都会被视为另一个字符串,就好像它们在另一行上一样。 <CR><LF> 字符被视为终止字符串的行终止符,并且不包含在字符串中。
    所有其他单字节字符都完美地包含在字符串中。
    搜索Unicode文件 由于FINDSTR无法搜索nul字节,而Unicode通常包含许多nul字节,因此不能正确地搜索大多数Unicode(UTF-16、UTF-16LE、UTF-16BE、UTF-32)。
    然而,TYPE命令将带有BOM的UTF-16LE转换为单字节字符集,因此以下命令可用于处理带有BOM的UTF-16LE。
    type unicode.txt|findstr "search"
    

    注意,Unicode代码点如果不被您的活动代码页支持,则会转换为“?”字符。
    只要搜索字符串仅包含ASCII字符,就可以搜索UTF-8。但是,任何多字节UTF-8字符的控制台输出都将不正确。但是,如果将输出重定向到文件,则结果将以正确编码的UTF-8格式显示。请注意,如果UTF-8文件包含BOM,则BOM将被视为第一行的一部分,这可能会影响匹配行开头的搜索。
    如果将搜索字符串放在UTF-8编码的搜索文件中(无BOM),并使用/G选项,则可以搜索多字节UTF-8字符。
    行尾
    FINDSTR在每个之后立即断开行。是否有对行断开没有影响。

    跨行搜索
    如预期,.正则表达式元字符将不匹配<CR>或<LF>。但是可以使用命令行搜索字符串跨行搜索。必须显式匹配<CR>和<LF>字符。如果找到多行匹配,则仅打印匹配的第一行。 FINDSTR然后返回源文件中的第二行并重新开始搜索 - 类似于“向前查看”功能。

    假设TEXT.TXT具有以下内容(可能是Unix或Windows样式)

    A
    A
    A
    B
    A
    A
    

    然后这个脚本
    @echo off
    setlocal
    ::Define LF variable containing a linefeed (0x0A)
    set LF=^
    
    
    ::Above 2 blank lines are critical - do not remove
    
    ::Define CR variable containing a carriage return (0x0D)
    for /f %%a in ('copy /Z "%~dpf0" nul') do set "CR=%%a"
    
    setlocal enableDelayedExpansion
    ::regex "!CR!*!LF!" will match both Unix and Windows style End-Of-Line
    findstr /n /r /c:"A!CR!*!LF!A" TEST.TXT
    

    给出这些结果

    1:A
    2:A
    5:A
    

    使用/G:FILE选项在跨越换行符进行搜索是不精确的,因为匹配<CR>或<LF>的唯一方法是通过正则表达式字符类范围表达式,该表达式夹在EOL字符之间。

    • [<TAB>-<0x0B>]匹配<LF>,但它也匹配<TAB>和<0x0B>

    • [<0x0C>-!]匹配<CR>,但它也匹配<0x0C>和!

    注意:上面是正则表达式字节流的符号表示,因为我无法以图形方式表示字符。

    答案在下面的第二部分中继续...


54
完美无缺。如果互联网上的所有答案都像这样就好了。 - Mike Viens
2
编辑 - 在XP上将控制字符显示为点,并记录了由于短8.3文件名导致的已知的/S/D选项错误。 - dbenham
2
仅供参考(我不知道您是否已经知道,但我在您的回答中没有看到提到)。大多数“奇怪”的反斜杠+引号规则的原因是findstr是一个exe文件,并且一些规则控制了参数标记器如何处理反斜杠+引号,但一旦参数被解析,findstr代码就有一个需要编译为正则表达式实例的字符串。因此,某些反斜杠会被解释两次。 - MC ND
2
一个字面上的反斜杠不需要任何转义(findstr /l \ *.cmd),但是双引号内的字面上的反斜杠需要转义(findstr /l "\\" *.cmd)以避免被转义的引号。但是findstr字符串解析器会将一个字面上的反斜杠后跟一个非字母数字字符([a-zA-Z0-9])视为转义字符:findstr /l /c:"\ o" *.cmd搜索一个空格后跟一个o字符,因为反斜杠转义了空格,但是findstr /l /c:"\w" *.cmd搜索一个反斜杠后跟一个w字符(它是字母数字字符,所以没有被转义)。 - MC ND
2
@dbenham,不知道是否应该加上关于/A:选项的简短说明?FINDSTR帮助文件没有指定在搜索多个文件时只有文件名会被着色。第一次阅读帮助文档时,人们可能会推断它可以改变输出中找到的字符串的颜色。我想这从技术上讲并不是一个未记录的功能或限制,但微软没有明确指出这一点似乎很奇怪。SS64上的文档有说明。 - Squashman
显示剩余21条评论

75

答案续自上文第1部分 - 我遇到了30,000个字符的答案限制 :-(

正则表达式(regex)支持受限
FINDSTR对正则表达式的支持非常有限。如果不在HELP文档中,则不受支持。

除此之外,支持的正则表达式是以完全非标准的方式实现的,因此结果可能与来自grep或perl等工具的预期结果不同。

正则表达式行位置锚^和$
^匹配输入流的开头以及<LF>之后的任何位置。由于FINDSTR在<LF>之后也会换行,所以"^"的简单正则表达式将始终匹配文件内的所有行,即使是二进制文件。

$匹配<CR>之前的任何位置。这意味着包含$的正则表达式搜索字符串永远不会匹配Unix样式文本文件中的任何行,如果Windows文本文件缺少EOL标记<CR><LF>,它也不会匹配最后一行。

注意 - 如先前讨论的那样,FINDSTR的管道和重定向输入可能会附加<CR><LF>,而这在源中是不存在的。显然,这可能会影响使用$的正则表达式搜索。

^之前或$之后具有字符的任何搜索字符串都将始终无法找到匹配项。

位置选项/B /E /X
位置选项的工作方式与^$相同,除了它们也适用于字面搜索字符串。

/B的功能与正则表达式搜索字符串的开头的^相同。

/E的功能与正则表达式搜索字符串的结尾的$相同。

/X的功能与在正则表达式搜索字符串的开头和结尾都加上^$相同。

正则表达式单词边界
\<必须是正则表达式中的第一个术语。如果在它之前出现任何其他字符,正则表达式将不匹配任何内容。\<对应于输入的开头、行的开头(紧随<LF>后的位置)或任何“非单词”字符之后的位置。下一个字符不必是“单词”字符。

\>必须是正则表达式中的最后一个术语。如果在它之后出现任何其他字符,正则表达式将不匹配任何内容。\>对应于输入的结尾、<CR>之前的位置或任何“非单词”字符之前的位置。前面的字符不必是“单词”字符。

以下是“非单词”字符的完整列表,表示为十进制字节码。注意 - 此列表是在美国机器上编译的。我不知道其他语言可能会对此列表产生什么影响。

001   028   063   179   204   230
002   029   064   180   205   231
003   030   091   181   206   232
004   031   092   182   207   233
005   032   093   183   208   234
006   033   094   184   209   235
007   034   096   185   210   236
008   035   123   186   211   237
009   036   124   187   212   238
011   037   125   188   213   239
012   038   126   189   214   240
014   039   127   190   215   241
015   040   155   191   216   242
016   041   156   192   217   243
017   042   157   193   218   244
018   043   158   194   219   245
019   044   168   195   220   246
020   045   169   196   221   247
021   046   170   197   222   248
022   047   173   198   223   249
023   058   174   199   224   250
024   059   175   200   226   251
025   060   176   201   227   254
026   061   177   202   228   255
027   062   178   203   229

正则表达式字符类范围[x-y]
字符类范围的匹配与预期不符。请参阅此问题:为什么findstr在某些情况下无法正确处理大小写?,以及此答案:https://dev59.com/JXE85IYBdhLWcg3wvGKm#8767815

问题在于FINDSTR不会按字节码值(通常称为ASCII码,但ASCII仅定义为0x00 - 0x7F)对字符进行归类。大多数正则表达式实现将[A-Z]视为所有英语大写字母。但是FINDSTR使用的排序序列大致相当于SORT的工作方式。因此,[A-Z]包括完整的英文字母表,包括大写和小写字母(除了"a"),以及带有变音符号的非英文字符。

下面是FINDSTR支持的所有字符的完整列表,按照FINDSTR用于建立正则表达式字符类范围的排序序列排序。这些字符以它们的十进制字节码值表示。我认为如果使用代码页437查看字符,则排序序列最有意义。注意-此列表是在美国机器上编译的。我不知道其他语言可能对此列表产生什么影响。

001
002
003
004
005
006
007
008
014
015
016
017
018           
019
020
021
022
023
024
025
026
027
028
029
030
031
127
039
045
032
255
009
010
011
012
013
033
034
035
036
037
038
040
041
042
044
046
047
058
059
063
064
091
092
093
094
095
096
123
124
125
126
173
168
155
156
157
158
043
249
060
061
062
241
174
175
246
251
239
247
240
243
242
169
244
245
254
196
205
179
186
218
213
214
201
191
184
183
187
192
212
211
200
217
190
189
188
195
198
199
204
180
181
182
185
194
209
210
203
193
207
208
202
197
216
215
206
223
220
221
222
219
176
177
178
170
248
230
250
048
172
171
049
050
253
051
052
053
054
055
056
057
236
097
065
166
160
133
131
132
142
134
143
145
146
098
066
099
067
135
128
100
068
101
069
130
144
138
136
137
102
070
159
103
071
104
072
105
073
161
141
140
139
106
074
107
075
108
076
109
077
110
252
078
164
165
111
079
167
162
149
147
148
153
112
080
113
081
114
082
115
083
225
116
084
117
085
163
151
150
129
154
118
086
119
087
120
088
121
089
152
122
090
224
226
235
238
233
227
229
228
231
237
232
234

正则表达式字符类的限制和错误
不仅FINDSTR在正则表达式中最多只能使用15个字符类项,而且当尝试超过此限制时,它还不能正常处理。使用16个或更多字符类项将导致Windows弹出交互式窗口,显示 "查找字符串 (QGREP) 实用程序遇到问题,需要关闭。我们为给您带来的不便表示抱歉。" 消息文本根据Windows版本略有不同。以下是一个将失败的FINDSTR示例:

echo 01234567890123456|findstr [0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]

该bug由DosTips用户Judago在此处报告。在XP、Vista和Windows 7上均已确认。

如果正则表达式搜索包括字节码0xFF(十进制255),则搜索将失败(并可能无限期挂起)
任何包括字节码0xFF(十进制255)的正则表达式搜索都将失败。如果直接包括字节码0xFF或者它隐含地包括在一个字符类范围内,都会失败。请注意,FINDSTR字符类范围不是基于字节码值对字符进行排序。字符<0xFF>相对较早地出现在排序序列中,在<space><tab>字符之间。因此,任何包括<space><tab>的字符类范围都将失败。

具体行为会根据Windows版本略有不同。如果包括0xFF,则Windows 7会无限期挂起。XP不会挂起,但始终找不到匹配项,并偶尔打印以下错误消息:"The process tried to write to a nonexistent pipe."

我不再能够访问Vista机器,因此无法在Vista上进行测试。

正则表达式bug:.[^anySet]可能匹配文件结束符
正则表达式.元字符只应匹配除<CR><LF>之外的任何字符。存在一个bug,允许它匹配未以<CR><LF>终止的文件中的文件结束符。但是,.不会匹配空文件。

例如,一个名为"test.txt"的文件,包含一行单个字符x,没有以<CR><LF>结尾,将匹配以下内容:

findstr /r x......... test.txt

在XP和Win7上已确认存在此漏洞。

负字符集似乎也是如此。类似于[^abc]的东西将匹配文件结尾。正字符集,例如[abc]工作正常。我只在Win7上进行了测试。


1
findstr 处理大文件也存在缺陷。文件> 2GB 可能导致 findstr 挂起,但不总是发生。在确认错误时,我搜索了一个大小为 2.3GB 的文件,但并没有挂起。即使仅搜索单个文件,它也会挂起。解决方法是将 type 的输出导入到 findstr 中。 - Disillusioned
还值得一提的是,findstr 还支持使用多个 /c: 搜索字符串。我知道你的答案已经展示了这一点。但是这是一个未记录的功能,我在使用 findstr 几年之后才意外地发现了这个特性。 - Disillusioned
不。如我在评论中所提到的,使用 type 重定向输出是一种可行的解决方法。此外,所有行都以 CRLF 结尾。我会将一个会话作为答案发布,你可以自己尝试一下。也许你会注意到其他可能导致问题的地方。 - Disillusioned
1
经进一步调查,发现这似乎是您记录的“LF”问题的变体。我意识到我的测试文件没有以“LF”结尾,因为我使用了附加模式下的“copy”来创建它。我已经将一个命令行会话放入答案中以演示此问题(https://dev59.com/eWox5IYBdhLWcg3w_JPd#22943056)。请注意,输入**未**被重定向,但搜索却挂起了。完全相同的搜索命令在类似不以“LF”结尾的较小文件上**不会挂起**。 - Disillusioned
1
新发现(Win7):findstr /R /C:"^[0-9][0-9]* [0-3][0-9][0-9]-[0-9][0-9]:[0-5][0-9]:[0-5][0-9]\.[0-9][0-9]* [0-9]*\.[0-9]*"(15个字符类)-- ErrorLevel = -1073740791 (0xC0000409)错误对话框窗口查找字符串(QGREP)实用程序已停止工作;删除一个类或两个元字符(*\.)后,它可以正常工作... - aschipfl
显示剩余4条评论

9

findstr在搜索大文件时有时会意外挂起。

我还没有确认确切的条件或边界大小。我怀疑任何超过2GB的文件都可能存在风险。

我的经历是参差不齐的,因此这不仅仅与文件大小有关。这看起来可能是FINDSTR在XP和Windows 7上挂起如果重定向输入不以LF结尾的变体,但正如所示,当输入被重定向时,这个特定的问题会发生。

以下命令行会话(Windows 7)演示了如何在搜索3GB文件时findstr会挂起。

C:\Data\Temp\2014-04>echo 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890> T100B.txt

C:\Data\Temp\2014-04>for /L %i in (1,1,10) do @type T100B.txt >> T1KB.txt

C:\Data\Temp\2014-04>for /L %i in (1,1,1000) do @type T1KB.txt >> T1MB.txt

C:\Data\Temp\2014-04>for /L %i in (1,1,1000) do @type T1MB.txt >> T1GB.txt

C:\Data\Temp\2014-04>echo find this line>> T1GB.txt

C:\Data\Temp\2014-04>copy T1GB.txt + T1GB.txt + T1GB.txt T3GB.txt
T1GB.txt
T1GB.txt
T1GB.txt
        1 file(s) copied.

C:\Data\Temp\2014-04>dir
 Volume in drive C has no label.
 Volume Serial Number is D2B2-FFDF

 Directory of C:\Data\Temp\2014-04

2014/04/08  04:28 PM    <DIR>          .
2014/04/08  04:28 PM    <DIR>          ..
2014/04/08  04:22 PM               102 T100B.txt
2014/04/08  04:28 PM     1 020 000 016 T1GB.txt
2014/04/08  04:23 PM             1 020 T1KB.txt
2014/04/08  04:23 PM         1 020 000 T1MB.txt
2014/04/08  04:29 PM     3 060 000 049 T3GB.txt
               5 File(s)  4 081 021 187 bytes
               2 Dir(s)  51 881 050 112 bytes free
C:\Data\Temp\2014-04>rem Findstr on the 1GB file does not hang

C:\Data\Temp\2014-04>findstr "this" T1GB.txt
find this line

C:\Data\Temp\2014-04>rem On the 3GB file, findstr hangs and must be aborted... even though it clearly reaches end of file

C:\Data\Temp\2014-04>findstr "this" T3GB.txt
find this line
find this line
find this line
^C
C:\Data\Temp\2014-04>

注意,我已经在十六进制编辑器中验证了所有行都以 CRLF 结尾。唯一的异常是文件由于 复制操作的方式 而以 0x1A 结尾。但需要注意的是,这种异常并不会对“小”文件造成问题。
通过额外的测试,我已确认以下内容:
  • 使用带有 /b 选项的 copy 用于二进制文件可以防止添加 0x1A 字符,并且 findstr 不会在 3GB 的文件上挂起。
  • 以不同字符结尾的 3GB 文件同样会导致 findstr 挂起。
  • 0x1A 字符不会对“小”文件造成任何问题。(其他结束字符也是如此。)
  • 0x1A 后添加 CRLF 可以解决这个问题。(仅使用 LF 也足够。)
  • 使用 type 将文件连接到 findstr 中可以正常工作而且不会挂起。(这可能是由于 type| 的副作用导致插入了额外的行尾。)
  • 使用重定向输入 < 同样会导致 findstr 挂起。但这是预期的;就像 dbenham 的帖子 中所解释的那样:“重定向输入必须以 LF 结尾”。

1
+1,我能够确认在我的Win7机器上出现了问题。当最后一个字符不是“<LF>”时,大小恰好为2GiB的文件会挂起。而比它小两个字节的文件则不会挂起。非常讨厌! - dbenham

9
当几个命令被括号括起来,并且有针对整个块的重定向文件时:
< input.txt (
   command1
   command2
   . . .
) > output.txt

如果在代码块中文件保持打开状态,只要命令处于活动状态,就可以移动重定向文件的文件指针。BOTH MORE和FIND命令在处理前将Stdin文件指针移动到文件开头,因此相同的文件可能会在块内多次被处理。例如,下面的代码:

more < input.txt >  output.txt
more < input.txt >> output.txt

这段代码将产生与这个代码相同的结果:
< input.txt (
   more
   more
) > output.txt

这段代码:
find    "search string" < input.txt > matchedLines.txt
find /V "search string" < input.txt > unmatchedLines.txt

...产生与此相同的结果:

< input.txt (
   find    "search string" > matchedLines.txt
   find /V "search string" > unmatchedLines.txt
)

FINDSTR 与其他命令不同,它不会移动标准输入文件指针到当前位置。例如,以下代码在搜索行后插入新行:

call :ProcessFile < input.txt
goto :EOF

:ProcessFile
   rem Read the next line from Stdin and copy it
   set /P line=
   echo %line%
   rem Test if it is the search line
   if "%line%" neq "search line" goto ProcessFile
rem Insert the new line at this point
echo New line
rem And copy the rest of lines
findstr "^"
exit /B

我们可以借助一个辅助程序充分利用此功能,该程序允许我们移动重定向文件的文件指针,如此示例所示。
这种行为最初是由jeb此帖子中报告的。
编辑 2018-08-18:新的FINDSTR错误已报告 当使用FINDSTR命令以颜色显示字符,并将此命令的输出重定向到CON设备时,FINDSTR命令具有奇怪的错误。有关如何使用FINDSTR命令以不同颜色显示文本的详细信息,请参见此主题
当以这种形式重定向FINDSTR命令的输出到CON后,在输出所需颜色的文本后,会发生一些奇怪的事情:所有文本都被输出为“不可见”的字符,尽管更精确的描述是文本被输出为黑色文本覆盖在黑色背景上。如果使用COLOR命令重置整个屏幕的前景色和背景色,则原始文本将出现。然而,当文本是“不可见”的时候,我们可以执行SET /P命令,因此输入的所有字符都不会显示在屏幕上。这种行为可以用于输入密码。
@echo off
setlocal

set /P "=_" < NUL > "Enter password"
findstr /A:1E /V "^$" "Enter password" NUL > CON
del "Enter password"
set /P "password="
cls
color 07
echo The password read is: "%password%"

3
FINDSTR存在一个颜色错误问题,我在https://superuser.com/questions/1535810-is-there-a-better-way-to-mitigate-this-obscure-color-bug-when-piping-to-findstr/1538802?noredirect=1#comment2339443_1538802中描述并解决了这个问题。
简而言之,该错误的表现是:如果在代码的括号块内将输入管道传递给FINDSTR,则后续执行的命令中内联的ANSI转义颜色代码将停止工作。内联颜色代码的示例是:echo %magenta%Alert: Something bad happened%yellow%(其中品红和黄色是在.bat文件中先定义为相应的ANSI转义颜色代码的变量)。
我的初始解决方案是在FINDSTR之后调用一个不执行任何操作的子程序。不知何故,调用或返回“重置”了需要重置的内容。
后来我发现了另一种解决方案,这可能更有效率:将FINDSTR短语放在括号中,如下面的示例所示: echo success | ( FINDSTR /R success ) 在嵌套代码块中放置FINDSTR短语似乎可以隔离FINDSTR的颜色代码错误,使其不会影响嵌套块外部的内容。也许这种技术也可以解决其他不希望出现的FINDSTR副作用

1
很好的发现。但是你的规则可以简化(至少在我的企业Windows 10机器上)。FINDSTR会阻止同一命令块内后续命令的所有控制台转义序列工作。无论FINDSTR读取管道、重定向输入还是文件,都不会影响转义序列的失败。命令块是指括号内的任何一组命令,以及通过&、&&或||连接的命令。 - dbenham
@dbenham:很好的问题概括。你知道我提出的解决方案——将FINDSTR短语嵌套在括号内——是否也适用于一般情况?你知道我的解决方案是否有任何不良副作用吗? - Dolores Stevens
我没有进行详尽的测试,但是嵌套括号似乎是一种通用解决方案,我想不到任何可能的副作用。 - dbenham

2
我想报告一个与“要搜索的数据源”部分有关的错误,当在文件名中使用en dash(–)或em dash(—)时,第一次回答中的问题出现了错误。
更具体地说,如果您要使用第一种选项 - “指定为参数的文件名”,则无法找到文件。只要您使用第二个选项 - “通过重定向的stdin”或第三个选项 - “来自管道的数据流”,findstr将会找到该文件。
例如,这个简单的批处理脚本:
echo off
chcp 1250 > nul
set INTEXTFILE1=filename with – dash.txt
set INTEXTFILE2=filename with — dash.txt

rem 3 way of findstr use with en dashed filename
echo.
echo Filename with en dash:
echo.
echo 1. As argument
findstr . "%INTEXTFILE1%"
echo.
echo 2. As stdin via redirection
findstr . < "%INTEXTFILE1%"
echo.
echo 3. As datastream from a pipe
type "%INTEXTFILE1%" | findstr .
echo.
echo.
rem The same set of operations with em dashed filename
echo Filename with em dash:
echo.
echo 1. As argument
findstr . "%INTEXTFILE2%"
echo.
echo 2. As stdin via redirection
findstr . < "%INTEXTFILE2%"
echo.
echo 3. As datastream from a pipe
type "%INTEXTFILE2%" | findstr .
echo.

pause

将打印:

带破折号的文件名:

  1. 作为参数
    FINDSTR:无法打开带有-破折号.txt的文件名

  2. 通过重定向作为stdin
    我是带有破折号的文件。

  3. 作为来自管道的数据流
    我是带有破折号的文件。

带有em dash的文件名:

  1. 作为参数
    FINDSTR:无法打开带有-破折号.txt的文件名

  2. 通过重定向作为stdin
    我是带有em dash的文件。

  3. 作为来自管道的数据流
    我是带有em dash的文件。

希望这能帮助到您。

M.


1
嗨 matro,你的评论可能是正确的,但我不确定它们是否解决了实际问题。 - Wai Ha Lee
我相信这是一个Unicode的问题,FINDSTR不支持。CMD.EXE重定向可以正确地打开带有Unicode的文件名,TYPE命令也可以。但是在某个地方,FINDSTR将连字符和短划线都转换为普通破折号,当然操作系统无法找到该名称。如果您创建另一个文件,将en-dash和/或em-dash替换为短横线,则FINDSTR将搜索该文件,如果提供了包含en-dash或em-dash的名称。 - dbenham
我会将这个问题归类为限制而不是错误。 - dbenham
实际上,这不是一个Unicode问题,而是扩展ASCII问题。我已经在我的原始答案中记录了这个问题,标题为“命令行参数的字符限制 - 扩展ASCII转换”。FINDSTR将许多扩展ASCII代码转换为“相关”的真正ASCII,包括en-dash和em-dash。 - dbenham

2

findstr命令ErrorLevel(或退出代码)设置为以下值之一,前提是没有无效或不兼容的开关,并且没有搜索字符串超过适用的长度限制:

  • 在所有指定文件中的一行中至少遇到一个匹配项,则为0
  • 否则为1

当满足以下条件时,认为一行包含匹配项:

  • 未给出/V选项且搜索表达式至少出现一次;
  • 给出了/V选项且搜索表达式未出现;

这意味着/V选项也会更改返回的ErrorLevel,但它并不仅仅是恢复它!

例如,当您有一个名为test.txt的文件,其中有两行,其中一行包含字符串text,而另一行则没有。无论是findstr "text" "test.txt"还是findstr /V "text" "test.txt"都会返回ErrorLevel值为0
基本上可以说:如果findstr返回至少一行,则将ErrorLevel设置为0,否则设置为1
请注意,/M选项不影响ErrorLevel值,它只改变输出。
(仅为完整起见:find command命令在/V选项和ErrorLevel方面的行为完全相同;/C选项不会影响ErrorLevel。)

-1

/D 多个目录的提示:在搜索字符串之前放置目录列表。这些都可以工作:

findstr /D:dir1;dir2 "searchString" *.*
findstr /D:"dir1;dir2" "searchString" *.*
findstr /D:"\path\dir1\;\path\dir2\" "searchString" *.*

如预期,如果您不以\开头,则路径是相对于位置的。如果目录名称中没有空格,则用"包围路径是可选的。结尾的\是可选的。位置的输出将包括您提供的任何路径。无论是否使用"包围目录列表,它都可以正常工作。

我在这里没有看到任何未记录的内容。 /D选项在内置帮助中有描述。这不是关于如何使用FINDSTR的一般提示的问题。它严格意味着列出未记录的功能,限制和/或错误。 - dbenham
1
@dbenham 确实它并不是真正的未记录,但我发现我必须与findstr讨价还价才能得到我想要的结果,并分享我发现的确实有效的内容,以便人们不会浪费时间尝试无效的命令。希望有所帮助。(我很遗憾您不喜欢我的意见 - 它只是旨在建设性地提供帮助) - gordon
2
在我看来,/D开关在内置帮助中已经清楚地描述了:/D:dirlist 搜索一个分号分隔的目录列表,并且它位于搜索字符串之前,所以我不明白关于/D开关的“你找到了什么”(以及哪些“命令不起作用”)... - Aacini
在许多编程语言中,属性的顺序并不重要。我理解 findstr 的文档列出了 /D 属性。是的,我对该功能的文档记录没有异议,只是它没有记录属性顺序很重要这一点。我很少使用命令行,所以当我拼凑一个命令时,没有意识到顺序很重要,我只是按照字母顺序添加属性(C 在 D 之前)。我感到非常沮丧,并分享了我的“发现”经验,供那些不常使用命令行的人参考。 - gordon
3
可选属性的顺序通常不重要。findstr文档指定strings部分是非可选的,必须将其放在可选属性之后和可选文件名列表之前。如果“您找到”的是使用命令而不遵循其使用格式会导致错误,则此点已经有很好的记录。请参阅命令语法: “语法按您必须键入命令及其后面的任何参数的顺序显示”。 - Aacini

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接