Почему этот пример FINDSTR с несколькими литеральными строками поиска не находит соответствия?

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

echo ffffaaa|findstr /l "ffffaaa faffaffddd"

Почему?


person dbenham    schedule 19.01.2012    source источник
comment
хочешь узнать что-нибудь смешное? поставить пробел после ffffaaa и все работает =D   -  person Anthony Miller    schedule 19.01.2012
comment
@Mechaflash - это не обязательно пробел, это может быть любой символ. Но затем удлините вторую строку поиска на один символ и, если снова не удастся. Кажется, существует минимальная разница в размерах, необходимая для появления ошибки. Но минимальная разница не является константой. Я видел, что разница в размере 2 терпит неудачу.   -  person dbenham    schedule 19.01.2012
comment
... только что обнаружил еще одно интересное поведение findstr: с заданным переключателем /X строка должна соответствовать точно для вывода; когда последняя строка в текстовом файле для поиска не заканчивается новой строкой, findstr не возвращает ее (независимо от того, задано ли /L или /R, или поиск строке предшествует /C:)...   -  person aschipfl    schedule 06.08.2015
comment
@aschipfl - я уже задокументировал эту проблему на странице Каковы недокументированные функции и ограничения команды Windows FINDSTR?. Это на самом деле терпит неудачу, если строка не содержит возврата каретки (0x0D), даже если присутствует новая строка (0x0A).   -  person dbenham    schedule 07.08.2015
comment
@aschipfl — информация находится под заголовками Якоря позиции строки регулярного выражения ^ и $ и Позиционные параметры /B /E /X   -  person dbenham    schedule 07.08.2015


Ответы (2)


По-видимому, это давняя ошибка 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.

person dbenham    schedule 19.01.2012
comment
Осторожно, dbenham, вы, скорее всего, станете гуру findstr почти так же, как Skeet — гуру C# :-) - person paxdiablo; 19.01.2012

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

person aschipfl    schedule 04.06.2017
comment
Да, буквальный поиск смешон. FINDSTR, вероятно, является одной из худших куч дерьма, когда-либо выпущенных в производство. Первоначально это был личный инструмент сотрудника MS, и он стал стандартной частью выпуска Windows без надлежащего проектирования и отладки. И да, вы можете преобразовать каждую литеральную строку в регулярное выражение, но ваша стратегия экранирования каждого символа сильно ограничивает длину поиска до 127 символов. Максимальное количество литеральных строк — 511. Количество регулярных выражений ограничено 254, но ваши escape-символы оставляют только 127. В XP это еще хуже. - person dbenham; 05.06.2017