Почему этот пример FINDSTR с несколькими литеральными строками поиска не находит соответствия? - PullRequest
8 голосов
/ 19 января 2012

В следующем примере FINDSTR не удается найти соответствие.

echo ffffaaa|findstr /l "ffffaaa faffaffddd"

Почему?

Ответы [ 2 ]

13 голосов
/ 19 января 2012

Очевидно, что это давняя ошибка FINDSTR. Я думаю, что это может быть серьезной ошибкой, в зависимости от обстоятельств.

Я подтвердил, что команда не работает на двух разных компьютерах Vista, Windows 7 и XP. Я обнаружил эту findstr - неисправную ??? ссылку, которая сообщает, что подобный поиск завершается ошибкой в ​​Windows Server 2003, но он успешно выполняется в Windows 2000.

Я провел ряд экспериментов, и кажется, что для вероятности отказа должны быть выполнены все следующие условия:

  • Для поиска используются несколько литеральных строк поиска
  • Строки поиска имеют разную длину
  • Короткая строка поиска имеет некоторое перекрытие с более длинной строкой поиска
  • Поиск выполняется с учетом регистра (опция /I отсутствует)

При каждом сбое, которое я видел, всегда происходит сбой одной из более коротких строк поиска.

Неважно, как указаны строки поиска. Тот же ошибочный результат достигается с помощью нескольких опций /C:"search", а также с опцией /G:file.

Единственные 3 обходных пути, которые мне удалось найти:

  • Используйте опцию /I, если вас не волнует дело. Очевидно, что это может не соответствовать вашим потребностям.

  • Используйте опцию регулярного выражения /R. Но если вы это сделаете, вы должны убедиться, что вы избегаете мета-символов в поиске, чтобы он соответствовал ожидаемому результату буквального поиска. Это также может быть проблематично.

  • Если вы используете опцию /V, то используйте несколько конвейерных команд FINDSTR с одной строкой поиска каждая вместо одного FINDSTR с несколькими поисками. Это также может быть проблемой, если у вас есть много строк поиска, для которых вы хотите использовать опцию /G:file.

Я ненавижу эту ошибку !!!!

Примечание - См. Каковы недокументированные функции и ограничения команды Windows FINDSTR? для получения полного списка особенностей FINDSTR.

1 голос
/ 05 июня 2017

Я не могу сказать, почему 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"

Предыдущие обратные косые черты просто экранируют каждый символ, включая те, которые имеют особое значение при поиске по регулярному выражению.

Символы < и > исключены из экранирования, чтобы избежать конфликтов с границами слов, которые были выражены \< и \> при появлении в начале и в конце строки поиска. соответственно.

Поскольку регулярные выражения ограничены 254 символами для findstr версий, предшествующих Windows XP (в отличие от буквенных строк, которые ограничены 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 символами минус 1 для каждого встречающегося вышеупомянутого метасимвола, применяя для findstr версий после Windows XP.


Вот еще один обходной путь, использующий поиск без учета регистра с 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, никогда не будет работать надежно, если они содержат обратную косую черту, поскольку они все равно могут использоваться для экранирования после метасимволов, хотя и не обязательно ; например, строка поиска \. фактически соответствует .; чтобы действительно соответствовать \. буквально, необходимо указать строку поиска \\.. Я не понимаю, почему метасимволы все еще распознаются при выполнении буквального поиска, это не то, что я называю буквальным.

...