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

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

echo ffffaaa|findstr /l "ffffaaa faffaffddd"

Почему?

Ответ 1

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

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

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

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

В каждой ошибке, которую я видел, она всегда является одной из коротких строк поиска, которая терпит неудачу.

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

Единственными тремя обходными решениями, которые я смог придумать, являются:

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

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

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

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

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

Ответ 2

Я не могу сказать, почему 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^^[email protected]^^!STR:ffffaaa=ffffaaa^^! (echo(^^!STR^^!) else if @^^!STR^^[email protected]^^!STR:faffaffddd=faffaffddd^^! (echo(^^!STR^^!)

Восклицательные знаки с двойным экранированием гарантируют, что переменная STR расширяется в явно вызванном экземпляре cmd даже в случае, если задержка с задержкой включена в экземпляре хостинга cmd.


Кстати, из-за того, что я называю дефектом дизайна, поиск с литеральными строками с использованием findstr никогда не работает надежно, как только они содержат обратную косую черту, потому что они все еще могут быть использованы для выхода из метасимволов, хотя это и не обязательно; например, строка поиска \. действительно соответствует .; чтобы действительно соответствовать \. буквально, вы должны указать строку поиска \\.. Я не понимаю, почему мета-символы все еще распознаются при выполнении литералов, это не то, что я называю буквальным.