为什么这个带有多个文字搜索字符串的FINDSTR示例找不到匹配项?

11
有时候使用多个字面搜索字符串的FINDSTR命令会未能找到所有匹配项。例如,以下FINDSTR示例未能找到匹配项。
echo ffffaaa|findstr /l "ffffaaa faffaffddd"
为什么?

2
想知道有趣的事情吗?在 ffffaaa 后面加一个空格就可以了 =D - Anthony Miller
2
@Mechaflash - 它不一定是一个空格,它可以是任何字符。但是,然后将第二个搜索字符串扩展一个字符,如果再次失败,则会出现错误。似乎需要最小的大小差异才能出现错误。但是最小差异不是恒定的。我见过大小差异为2的失败情况。 - dbenham
刚刚发现了 findstr 的另一个有趣的“行为”:使用 /X 开关时,必须完全匹配才能输出一行;当要搜索的文本文件中的最后一行没有以新行符结尾时,无论是否给出 /L/R,或者搜索字符串是否在 /C: 之前,findstr 都不会返回它。 - aschipfl
2
@aschipfl - 我已经在Windows FINDSTR命令的未记录特性和限制是什么?上记录了此问题。即使存在换行符(0x0A),如果行不包含回车符(0x0D),它实际上会失败。 - dbenham
@aschipfl - 该信息在标题为正则表达式行位置锚点^和$以及位置选项/B /E /X下。 - dbenham
2个回答

16

显然这是一个长期存在的FINDSTR错误。根据情况,我认为它可能是一个致命性的错误。

我已经确认该命令在两台不同的Vista机器、一台Windows 7机器和一台XP机器上失败了。我发现这个findstr - broken ???链接报告了一个类似的搜索在Windows Server 2003上失败,但在Windows 2000上成功。

我进行了许多实验,似乎所有以下条件都必须满足才有可能出现错误:

  • 搜索使用多个文字字面量字符串
  • 搜索字符串具有不同长度
  • 一个较短的搜索字符串与一个较长的搜索字符串有一定重叠
  • 搜索区分大小写(没有/I选项)

在我看到的每一个错误中,总是其中一个较短的搜索字符串失败了。

搜索字符串的指定方式并不重要。使用多个/C:"search"选项和/G:file选项得到的都是相同错误的结果。

我能想到的唯一三种解决方法是:

  • 如果您不关心大小写,则使用/I选项。显然,这可能不符合您的需求。

  • 使用/R正则表达式选项。但如果你这样做的话,你必须确保你转义任何元字符,以便它匹配一个文字字面量搜索期望的结果。这也可能会有问题。

  • 如果您正在使用/V选项,则使用多个管道FINDSTR命令,每个命令只搜索一个字符串,而不是使用一个FINDSTR搜索多个字符串。如果您有很多要使用/G:file选项搜索的字符串,这也可能会有问题。

我讨厌这个错误!!!

注意 - 有关 FINDSTR 的不文档化功能和限制的全面列表,请参见此处


5
小心,dbenham,你很有可能成为 findstr 方面的专家,就像 Skeet 是 C# 方面的专家一样 :-) - paxdiablo

1
我无法确定为什么使用多个文字字符串时findstr可能会失败。但是,我可以提供一种解决这个烦人错误的方法。
假设文字搜索字符串在名为search_strings.txt的文本文件中列出...:
ffffaaa
faffaffddd

如果您想将其转换为正则表达式,可以在每个单个字符前插入反斜杠:

@echo off
setlocal EnableExtensions DisableDelayedExpansion
> "regular_expressions.txt" (
    for /F usebackq^ delims^=^ eol^= %%S in ("search_strings.txt") do (
        set "REGEX=" & set "STRING=%%S"
        for /F delims^=^ eol^= %%T in ('
            cmd /U /V /C echo(!STRING!^| find /V ""
        ') do (
            set "ESCCHR=\%%T"
            if "%%T"="<" (set "ESCCHR=%%T") else if "%%T"=">" (set "ESCCHR=%%T")
            setlocal EnableDelayedExpansion
            for /F "delims=" %%U in ("REGEX=!REGEX!!ESCCHR!") do (
                endlocal & set "%%U"
            )
        )
        setlocal EnableDelayedExpansion
        echo(!REGEX!
        endlocal
    )
)
endlocal

然后使用转换后的文件 regular_expressions.txt...

\f\f\f\f\a\a\a
\f\a\f\f\a\f\f\d\d\d

我需要进行正则表达式搜索,这似乎可以很好地处理多个搜索字符串:

echo ffffaaa| findstr /R /G:"regular_expressions.txt"

前面的反斜杠仅仅是用来转义每个字符,包括那些在正则表达式搜索中具有特殊意义的字符。

字符<>被排除在转义之外,以避免与单词边界冲突。当它们出现在搜索字符串的开头和结尾时,单词边界是通过\<\>表示的。

由于正则表达式在Windows XP之后的findstr版本中限制为254个字符(相对于字面字符串,其限制为511个字符),因此原始搜索字符串的长度限制为127个字符,因为每个这样的字符都由两个字符表示。


这里有一种替代方法,只转义元字符.*^$[]\"
@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "_META=.*^$[]\"^" & rem (including `"`)
> "regular_expressions.txt" (
    for /F usebackq^ delims^=^ eol^= %%S in ("search_strings.txt") do (
        set "REGEX=" & set "STRING=%%S"
        for /F delims^=^ eol^= %%T in ('
            cmd /U /V /C echo(!STRING!^| find /V ""
        ') do (
            set "CHR=%%T"
            setlocal EnableDelayedExpansion
            if not "!_META!"=="!_META:*%%T=!" set "CHR=\!CHR!"
            for /F "delims=" %%U in ("REGEX=!REGEX!!CHR!") do (
                endlocal & set "%%U"
            )
        )
        setlocal EnableDelayedExpansion
        echo(!REGEX!
        endlocal
    )
)
endlocal

这种方法的优点是,搜索字符串的长度不再仅限于127个字符,而是限制为254个字符减去每个出现的元字符,适用于Windows XP之后的findstr版本。

这里是另一种解决方法,使用不区分大小写的搜索并在第一位使用findstr,然后通过区分大小写的比较对结果进行后处理:

echo ffffaaa|findstr /L /I "ffffaaa faffaffddd"|cmd /V /C set /P STR=""^&if @^^!STR^^!==@^^!STR:ffffaaa=ffffaaa^^! (echo(^^!STR^^!) else if @^^!STR^^!==@^^!STR:faffaffddd=faffaffddd^^! (echo(^^!STR^^!)

双重转义的感叹号可以确保变量STR在显式调用的cmd实例中被扩展,即使在托管cmd实例中启用了延迟扩展。

顺便提一下,由于我称之为设计缺陷,使用findstr进行字面字符串搜索时,只要包含反斜杠,就永远不可靠,因为这些字符可能被用来转义后面的元字符,尽管这并不必要。例如,搜索字符串\.实际上匹配了.;要真正匹配\.,必须指定搜索字符串\\.。我不明白为什么在进行字面搜索时仍然识别元字符,这不是我所说的字面意思。


2
是的,“字面”搜索太荒谬了。FINDSTR可能是有史以来发布到生产环境中最糟糕的一堆*****之一。它最初是一个微软员工的个人工具,但在没有适当设计和调试的情况下成为Windows发布的标准部分。是的,您可以将每个字面字符串转换为正则表达式,但是您转义每个字符的策略严重限制了搜索长度,最多只能达到127个字符。字面字符串最多可达511个。正则表达式限于254个,但您的转义仅剩下127个。在XP上甚至更糟。 - dbenham

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