Windowsbatch file以回显特定的行号

所以对于我目前困境的第二部分,我有一个在c:\file_list.txt中的文件夹列表。 我需要能够基于行号来提取它们(呃,用一些mod来回显它们),因为这个批处理脚本被一个迭代的macros过程调用。 我通过行号作为参数。

 @echo off setlocal enabledelayedexpansion set /a counter=0 set /a %%a = "" for /f "usebackq delims=" %%a in (c:\file_list.txt) do ( if "!counter!"=="%1" goto :printme & set /a counter+=1 ) :printme echo %%a 

这给了我%a的输出。 卫生署! 所以,我试着回应!a! (结果: ECHO is off. )。 我试过回显%a (结果:a)

我觉得简单的做法是修改这里find的head.bat代码: Windows批处理命令(s)从文本文件中读取第一行
除了不是回应每一行 – 我只是回应find的最后一行。 不像人们想象的那么简单。 我注意到我的柜台由于某种原因停留在零。 我想知道,如果set /a counter+=1正在做我认为正在做的事情。

我知道这是一个古老的问题,但这里有一些额外的信息给任何人有类似的问题…

李,你的理由是为什么“%% a”不在for循环之外工作是正确的。 %az和%AZ变量(批处理文件中的%% az)是for循环的结构,不存在于其外部。

我想建议这个问题的替代解决方案,匹配正确的行号(没有空行跳过),并不需要延迟扩展,计数器或goto语句。 看看下面的代码:

 @echo off for /f "tokens=1* delims=:" %%a in ('findstr /n .* "c:\file_list.txt"') do if "%%a"=="%1" set line=%%b echo.%line% 

这是什么导致了我的上述变化。 假设您有以下文件内容:

 Some text on line 1 Blah blah blah More text 

我做的第一件事是改变(c:\ file_list.txt) .to ('findstr / n。*“c:\ file_list.txt”')

  • ' findstr / n。*“PATH \ FILENAME”读取文件并在每行添加行号(' / n ')(' 。* '是匹配任何字符的“0或更多”的正则表达式)。 由于每一行现在都有一个行号(甚至是空行),for循环将不会跳过任何行。

现在,每行在for循环中将如下所示:

 1:Some text on line 1 2:Blah blah blah 3:More text 

接下来,我们使用“tokens = 1 * delims =:”来拆分行号和内容。

  • ' tokens = 1 * '将第一个标记(存储在%% a中 )设置为分隔符之前的所有内容,第二个标记(存储在%% b中 )设置为之后的所有内容。
  • ' delims =: 'sets“ ”作为分隔符用来分隔字符串。

现在,当我们遍历文件时, %% a将返回当前行号, %% b将返回该行的内容。

剩下的就是将%1参数与%% a (而不是计数器变量)进行比较,并使用%% b存储当前行的内容: if“%% a”==“%1”set line = %% b

另外一个好处是' enabledelayedexpansion '不再是必需的,因为上面的代码在for循环的中间没有读取一个计数器变量。

编辑:将“ echo%line% ”更改为“ echo。%line% ”。 这将正确显示空白行,而不是“ECHO关闭”。 更改' 键入c:\ file_list.txt ^ | 因为findstr命令已经可以直接读取文件,所以找到 findstr / n。* ”到“ findstr / n。*”c:\ file_list.txt“ ”。

杰布,我想我已经解决了所有的特殊问题。 给这个镜头:

 for /f "tokens=*" %%a in ('findstr /n .* "c:\file_list.txt"') do ( set "FullLine=%%a" for /f "tokens=1* delims=:" %%b in ("%%a") do ( setlocal enabledelayedexpansion set "LineData=!FullLine:*:=!" if "%%b" equ "%1" echo(!LineData! endlocal ) ) 

嗯,它吃了我的格式。

 @echo off setlocal enabledelayedexpansion set /a counter=0 set %%a = "" for /f "usebackq delims=" %%a in (c:\file_list.txt) do (if "!counter!"=="%1" goto :printme & set /a counter+=1) :printme echo %%a% 

你可以使用这样的批处理功能:

 @ECHO OFF CALL :ReadNthLine "%~nx0" 10 PAUSE >NUL GOTO :EOF :ReadNthLine File nLine FOR /F "tokens=1* delims=]" %%A IN ('^<"%~1" FIND /N /V "" ^| FINDSTR /B /C:"[%2]"') DO ECHO.%%B GOTO :EOF A line containing special shell characters: () <> %! ^| "& 

OUTPUT

包含特殊外壳字符的行:()<>%! ^ | “&

无效的行号

上面的函数也可以打印空行或者包含特殊字符的行,这在大多数情况下是足够的。 但是,为了处理提供给此函数的无效行号,请将错误检查代码添加到此功能中:

 :ReadNthLine File nLine FOR /F %%A IN ('^<"%~1" FIND /C /V ""') DO IF %2 GTR %%A (ECHO Error: No such line %2. 1>&2 & EXIT /b 1) FOR /F "tokens=1* delims=]" %%A IN ('^<"%~1" FIND /N /V "" ^| FINDSTR /B /C:"[%2]"') DO ECHO.%%B EXIT /b 

ReadNthLine2

  • 特殊字符 – 印刷

  • 空行 – 印

  • 不存在的行 – 显示错误消息

有一个技巧来提取没有行号前缀的行字符串(或者如果你想要的话),而不需要在所有文件行上使用批量迭代(“for / F”加上计数)。

要做到这一点,你必须使用findstr.exe总是在流水线中使用/ N标志,并通过管道中的第二个findstr.exe通过/B /C:"<N1>:" /C:"<N2>:" ... /C:"<NX>:"参数。

这里是我用来解析文本和二进制文件的print_file_string.bat脚本:

 @echo off rem Description: rem Script for string lines extraction from a text/binary file by findstr rem utility pattern and/or line number. rem Command arguments: rem %1 - Optional flags: rem -n - prints line number prefix "<N>:" for each found string from file. rem By default, the line number prefix does not print. rem -f1 - filter by line numbers for strings after %4..%N filter pattern. rem By default, filters by line numbers from the file. rem -pe - treats input file as a Portable Executable file rem (the strings.exe must exist). rem By default, the file treated as a text file. rem %1 - Path to a directory with a file to extract. rem %2 - Relative path to a text/binary file with strings. rem %3 - Set of line numbers separated by : character to print strings of. rem These line numbers by default are line numbers of strings from the rem file, not from filtered output. If you want to point line numbers rem after %4..%N filter pattern, then you must use -f1 flag. rem If empty, then treated as "all strings". rem %4..%N - Arguments for findstr command line in first filter. rem If empty, then treated as /R /C:".*", which means "any string". rem CAUTION: rem DO NOT use /N flag in %4..%N arguments, instead use script -n flag to rem print strings w/ line number prefix. rem Examples: rem 1. call print_file_string.bat -n . example.txt 1:20:10:30 /R /C:".*" rem Prints 1, 10, 20, 30 lines of the example.txt file sorted by line number rem and prints them w/ line number prefix: rem rem 2. call print_file_string.bat . example.txt 100 /R /C:".*" rem Prints 100'th string of example.txt file and prints it w/o line number rem prefix. rem rem 3. call print_file_string.bat -pe c:\Application res.dll "" /B /C:"VERSION=" rem Prints all strings from the c:\Application\res.dll binary file, where rem strings beginning by the "VERSION=" string and prints them w/o line number rem prefix. rem rem 4. call print_file_string.bat -pe c:\Application res.dll 1:20:10:30 /R /C:".*" rem Prints 1, 10, 20, 30 lines of string resources from the rem c:\Application\res.dll binary file, where strings beginning by the rem "VERSION=" string and prints them w/o line number prefix. setlocal EnableDelayedExpansion set "?~dp0=%~dp0" set "?~nx0=%~nx0" rem script flags set FLAG_PRINT_LINE_NUMBER_PREFIX=0 set FLAG_F1_LINE_NUMBER_FILTER=0 set FLAG_FILE_FORMAT_PE=0 rem flags set "FLAGS=" :FLAGS_LOOP rem flags always at first set "FLAG=%~1" if not "%FLAG%" == "" ^ if not "%FLAG:~0,1%" == "-" set "FLAG=" if not "%FLAG%" == "" ( if "%FLAG%" == "-n" set FLAG_PRINT_LINE_NUMBER_PREFIX=1 if "%FLAG%" == "-f1" set FLAG_F1_LINE_NUMBER_FILTER=1 if "%FLAG%" == "-pe" set FLAG_FILE_FORMAT_PE=1 shift rem read until no flags goto FLAGS_LOOP ) set "DIR_PATH=%~dpf1" set "FILE_PATH=%~2" set "FILE_PATH_PREFIX=" if not "%DIR_PATH%" == "" set "FILE_PATH_PREFIX=%DIR_PATH%\" if not "%FILE_PATH_PREFIX%" == "" ^ if not exist "%FILE_PATH_PREFIX%" ( echo.%?~nx0%: error: Directory path does not exist: "%FILE_PATH_PREFIX%" exit /b 1 ) >&2 if "%FILE_PATH%" == "" ( echo.%?~nx0%: error: File path does not set. exit /b 2 ) >&2 if not exist "%FILE_PATH_PREFIX%%FILE_PATH%" ( echo.%?~nx0%: error: File path does not exist: "%FILE_PATH_PREFIX%%FILE_PATH%" exit /b 3 ) >&2 set "LINE_NUMBERS=%~3" set "FINDSTR_LINES_FILTER_CMD_LINE=" if "%LINE_NUMBERS%" == "" goto FINDSTR_LINES_FILTER_END set LINE_NUMBER_INDEX=1 :FINDSTR_LINES_FILTER_LOOP set "LINE_NUMBER=" for /F "tokens=%LINE_NUMBER_INDEX% delims=:" %%i in ("%LINE_NUMBERS%") do set "LINE_NUMBER=%%i" if "%LINE_NUMBER%" == "" goto FINDSTR_LINES_FILTER_END set FINDSTR_LINES_FILTER_CMD_LINE=!FINDSTR_LINES_FILTER_CMD_LINE! /C:"!LINE_NUMBER!:" set /A LINE_NUMBER_INDEX+=1 goto FINDSTR_LINES_FILTER_LOOP :FINDSTR_LINES_FILTER_END shift shift shift set "FINDSTR_FIRST_FILTER_CMD_LINE=" :FINDSTR_FIRST_FILTER_LOOP set ARG=%1 if not "!ARG!" == "" ( set FINDSTR_FIRST_FILTER_CMD_LINE=!FINDSTR_FIRST_FILTER_CMD_LINE! !ARG! shift goto FINDSTR_FIRST_FILTER_LOOP ) if "!FINDSTR_FIRST_FILTER_CMD_LINE!" == "" set FINDSTR_FIRST_FILTER_CMD_LINE=/R /C:".*" set OUTPUT_HAS_NUMBER_PREFIX=0 rem in case if /N at the end set "FINDSTR_FIRST_FILTER_CMD_LINE=!FINDSTR_FIRST_FILTER_CMD_LINE! " rem 1. add /N parameter to first filter if must print line prefixes and -f1 flag is not set. rem 2. flags prefixed output if must print line prefixes. if %FLAG_PRINT_LINE_NUMBER_PREFIX% NEQ 0 ( if %FLAG_F1_LINE_NUMBER_FILTER% EQU 0 ( if "!FINDSTR_FIRST_FILTER_CMD_LINE:/N =!" == "!FINDSTR_FIRST_FILTER_CMD_LINE!" ( set "FINDSTR_FIRST_FILTER_CMD_LINE=/N !FINDSTR_FIRST_FILTER_CMD_LINE!" ) ) set OUTPUT_HAS_NUMBER_PREFIX=1 ) rem 1. add /N parameter to first filter and flags prefixed output if lines filter is not empty and -f1 flag is not set. rem 2. add /B parameter to lines filter if lines filter is not empty if not "!FINDSTR_LINES_FILTER_CMD_LINE!" == "" ( if %FLAG_F1_LINE_NUMBER_FILTER% EQU 0 ( if "!FINDSTR_FIRST_FILTER_CMD_LINE:/N =!" == "!FINDSTR_FIRST_FILTER_CMD_LINE!" ( set "FINDSTR_FIRST_FILTER_CMD_LINE=/N !FINDSTR_FIRST_FILTER_CMD_LINE!" set OUTPUT_HAS_NUMBER_PREFIX=1 ) ) if "!FINDSTR_LINES_FILTER_CMD_LINE:/B =!" == "!FINDSTR_LINES_FILTER_CMD_LINE!" ( set "FINDSTR_LINES_FILTER_CMD_LINE=/B !FINDSTR_LINES_FILTER_CMD_LINE!" ) ) rem 1. remove /N parameter from first filter if -f1 flag is set. rem 2. flags prefixed output if -f1 flag is set. if %FLAG_F1_LINE_NUMBER_FILTER% NEQ 0 ( if not "!FINDSTR_FIRST_FILTER_CMD_LINE:/N =!" == "!FINDSTR_FIRST_FILTER_CMD_LINE!" ( set "FINDSTR_FIRST_FILTER_CMD_LINE=!FINDSTR_FIRST_FILTER_CMD_LINE:/N =!" ) set OUTPUT_HAS_NUMBER_PREFIX=1 ) if "%TOOLS_PATH%" == "" set "TOOLS_PATH=%?~dp0%" rem set "TOOLS_PATH=%TOOLS_PATH:\=/%" if "%TOOLS_PATH:~-1%" == "\" set "TOOLS_PATH=%TOOLS_PATH:~0,-1%" if %FLAG_FILE_FORMAT_PE% EQU 0 ( set CMD_LINE=type "%FILE_PATH_PREFIX%%FILE_PATH%" ^| findstr !FINDSTR_FIRST_FILTER_CMD_LINE! ) else ( rem add EULA acception into registry to avoid EULA acception GUI dialog reg add HKCU\Software\Sysinternals\Strings /v EulaAccepted /t REG_DWORD /d 0x00000001 /f >nul 2>nul rem @ for bug case workaround set CMD_LINE=@"%TOOLS_PATH%\strings.exe" -q "%FILE_PATH_PREFIX%%FILE_PATH%" ^| findstr !FINDSTR_FIRST_FILTER_CMD_LINE! ) if %FLAG_F1_LINE_NUMBER_FILTER% NEQ 0 set CMD_LINE=!CMD_LINE! ^| findstr /N /R /C:".*" if not "!FINDSTR_LINES_FILTER_CMD_LINE!" == "" set CMD_LINE=!CMD_LINE! ^| findstr !FINDSTR_LINES_FILTER_CMD_LINE! rem echo !CMD_LINE! >&2 ( endlocal rem to avoid ! character truncation setlocal DisableDelayedExpansion if %OUTPUT_HAS_NUMBER_PREFIX% NEQ 0 ( if %FLAG_PRINT_LINE_NUMBER_PREFIX% NEQ 0 ( %CMD_LINE% 2>nul ) else ( for /F "usebackq eol= tokens=1,* delims=:" %%i in (`^(%CMD_LINE: | findstr = ^| findstr %^) 2^>nul`) do echo.%%j ) ) else ( %CMD_LINE% 2>nul ) ) exit /b 0 

优点:

已知的问题:

  • 如果按行使用了过滤器,或者设置了-f1标志,则:字符(重复)将从字符串的开头进行修剪。
  • findstr对内部字符串缓冲区有限制 – 8191个字符(包括行返回字符终止符)。 在大多数情况下,大于此数字的所有字符串将被截断为零。

例子:

  1. 调用print_file_string.bat -n。 example.txt 1:20:10:3​​0 / R /C:".*“

    打印按行号排序的example.txt文件的第1,第10,第20,第30行,并将其打印为行号前缀:

  2. 调用print_file_string.bat。 example.txt 100 / R /C:".*“

    打印example.txt文件的第100个字符串,并打印它没有行号前缀。

  3. 调用print_file_string.bat -pe c:\ Application res.dll“”/ B / C:“VERSION =”

    打印c:\ Application \ res.dll二进制文件中的所有字符串,其中字符串以“VERSION =”字符串开头,并以不包含行号前缀的方式打印。

  4. 调用print_file_string.bat -pe c:\ Application res.dll 1:20:10:3​​0 / R /C:".*“

    从c:\ Application \ res.dll二进制文件中打印1,10,20,30行字符串资源,其中字符串以“VERSION =”字符串开头,并将其打印为不带行号前缀的字符串资源。