Windows FINDSTR命令的文档记录非常糟糕。通过FINDSTR /?,或HELP FINDSTR,但它远远不够。在https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/findstr上有更多的在线文档。

文档中甚至没有提到FINDSTR的许多特性和限制。如果没有事先的知识和/或仔细的实验,也无法预测它们。

那么问题是——没有记录的FINDSTR特性和限制是什么?

这个问题的目的是提供许多未记录的特性的一站式存储库,以便:

A)开发人员可以充分利用现有的功能。

B)开发人员不会浪费时间去思考为什么有些东西看起来应该不能工作。

在回复之前,请确保您了解现有的文档。如果信息包含在HELP中,那么它就不属于这里。

这里也不是展示FINDSTR有趣用法的地方。如果一个有逻辑的人可以根据文档预测FINDSTR的特定用法的行为,那么它就不属于这里。

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


当前回答

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

我还没有确认具体的条件和边界大小。我怀疑任何大于2GB的文件都可能存在风险。

我在这方面有过复杂的经历,所以这不仅仅是文件大小的问题。如果重定向输入不以LF结束,这看起来可能是挂在XP和Windows 7上的FINDSTR的变体,但正如所演示的,当输入没有重定向时,这个特定的问题就会出现。

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

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终止。但是请注意,这种异常不会对“小”文件造成问题。

通过额外的测试,我确认如下:

Using copy with the /b option for binary files prevents the addition of the 0x1A character, and findstr doesn't hang on the 3GB file. Terminating the 3GB file with a different character also causes a findstr to hang. The 0x1A character doesn't cause any problems on a "small" file. (Similarly for other terminating characters.) Adding CRLF after 0x1A resolves the problem. (LF by itself would probably suffice.) Using type to pipe the file into findstr works without hanging. (This might be due to a side effect of either type or | that inserts an additional End Of Line.) Use redirected input < also causes findstr to hang. But this is expected; as explained in dbenham's post: "redirected input must end in LF".

其他回答

当几个命令被括在括号中,并且有重定向文件到整个块:

< input.txt (
   command1
   command2
   . . .
) > output.txt

... 然后,只要块中的命令处于活动状态,文件就保持打开状态,因此这些命令可能会移动重定向文件的文件指针。MORE和FIND命令在处理Stdin文件之前都将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则不同;它不会将Stdin文件指针从当前位置移动。例如,这段代码在搜索行之后插入新行:

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命令以颜色显示文本的详细信息,请参阅本主题。

When the output of this form of FINDSTR command is redirected to CON, something strange happens after the text is output in the desired color: all the text after it is output as "invisible" characters, although a more precise description is that the text is output as black text over black background. The original text will appear if you use COLOR command to reset the foreground and background colors of the entire screen. However, when the text is "invisible" we could execute a SET /P command, so all characters entered will not appear on the screen. This behavior may be used to enter passwords.

@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%"

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

当在所有指定文件的一行中至少遇到一个匹配时,返回0; 1本;

在以下情况下,一行被认为包含匹配项:

没有/V选项且搜索表达式至少出现一次; 给出/V选项,不出现搜索表达式;

这意味着/V选项也改变了返回的ErrorLevel,但它不只是恢复它!

例如,当你有一个文件test.txt有两行,其中一行包含字符串文本,但另一行不包含,findstr "text" "test.txt"和findstr /V "text" "test.txt"都返回0的ErrorLevel。

基本上你可以说:如果findstr返回至少一行,ErrorLevel设置为0,否则为1。

注意,/M选项不会影响ErrorLevel值,它只是改变输出。

(只是为了完整起见:find命令对于/V选项和ErrorLevel的行为完全相同;/C选项不会影响ErrorLevel。)

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

我还没有确认具体的条件和边界大小。我怀疑任何大于2GB的文件都可能存在风险。

我在这方面有过复杂的经历,所以这不仅仅是文件大小的问题。如果重定向输入不以LF结束,这看起来可能是挂在XP和Windows 7上的FINDSTR的变体,但正如所演示的,当输入没有重定向时,这个特定的问题就会出现。

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

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终止。但是请注意,这种异常不会对“小”文件造成问题。

通过额外的测试,我确认如下:

Using copy with the /b option for binary files prevents the addition of the 0x1A character, and findstr doesn't hang on the 3GB file. Terminating the 3GB file with a different character also causes a findstr to hang. The 0x1A character doesn't cause any problems on a "small" file. (Similarly for other terminating characters.) Adding CRLF after 0x1A resolves the problem. (LF by itself would probably suffice.) Using type to pipe the file into findstr works without hanging. (This might be due to a side effect of either type or | that inserts an additional End Of Line.) Use redirected input < also causes findstr to hang. But this is expected; as explained in dbenham's post: "redirected input must end in LF".

我想报告一个关于在文件名中使用en - dash(-)或em - dash(-)时在第一个答案中搜索数据源的部分的错误。

更具体地说,如果您打算使用第一个选项—指定为参数的文件名,则无法找到该文件。只要使用选项2 -通过重定向的stdin或选项3 -来自管道的数据流,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

将打印:

带破折号的文件名:

作为参数 无法使用- dash.txt打开文件名 作为stdin通过重定向 我是带破折号的档案。 作为来自管道的数据流 我是带破折号的档案。

带em破折号的文件名:

作为参数 无法使用- dash.txt打开文件名 作为stdin通过重定向 我就是那个带破折号的文件。 作为来自管道的数据流 我就是那个带破折号的文件。

希望能有所帮助。

M.

/D对于多个目录的提示:将目录列表放在搜索字符串之前。这些都有效:

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

正如预期的那样,如果不以\开始目录,路径是相对于位置的。如果目录名中没有空格,则用"包围路径是可选的。结尾\是可选的。location的输出将包括您给它的任何路径。它可以在目录列表周围使用“”,也可以不使用。