因此,对于当前困境的第二部分,我在c:\file_list.txt
中有一个文件夹列表。我需要能够根据行号提取它们(好吧,用一些mod回显它们),因为这个批处理脚本是由迭代宏进程调用的。我将行号作为参数传递。
@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)
我认为最简单的方法是修改此处的head.bat
代码:
Windows batch command(s) to read first line from text file
除了回应每一行 - 我只是回应找到的最后一行。并不像人们想象的那么简单。我注意到我的柜台由于某种原因停留在零;我想知道set /a counter+=1
是否正在做我认为它正在做的事情。
答案 0 :(得分:12)
我知道这是一个老问题,但对于有类似问题的人来说,这里有一些额外的信息......
Lee,你为什么“%% a”在for循环之外工作的原因是正确的。 %a-z和%A-Z变量(批处理文件中的%% a-z)是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)。到('findstr / n。*“c:\ file_list.txt”')
在for循环中,每一行现在都是这样的:
1:Some text on line 1
2:Blah blah blah
3:More text
接下来,我们使用“tokens = 1 * delims =:”来细分行号和内容。
现在,当我们循环浏览文件时, %% a 将返回当前行号,而 %% b 将返回该行的内容。
剩下的就是将%1 参数与 %% a (而不是计数器变量)进行比较并使用 %% b 存储当前行内容:如果“%% a”==“%1”设置行= %% b 。
额外的好处是不再需要' enabledelayedexpansion ',因为上面的代码消除了在for循环中间读取计数器变量。
修改 将' echo%line%'更改为' echo。%line%'。这将正确显示空行,而不是“ECHO关闭”。更改了'键入c:\ file_list.txt ^ | findstr / n。* '到' findstr / n。*“c:\ file_list.txt”',因为 findstr 命令已经可以直接读取文件
杰布,我想我已经解决了所有特殊问题。试一试: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
)
)
答案 1 :(得分:2)
呸,它吃了我的格式。
@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%
答案 2 :(得分:1)
您可以使用这样的批处理功能:
@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: () <> %! ^| "&
<强>输出强>
包含特殊shell字符的行:()&lt;&gt; %! ^ | “&安培;
无效的行号
上述功能还可以打印空行或包含特殊字符的行,这对大多数情况来说已经足够了。但是,为了处理提供给此函数的无效行号,请将错误检查代码添加到函数中,如下所示:
: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
特殊字符 - 已打印
空行 - 打印
不存在的行 - 显示错误消息
答案 3 :(得分:0)
有一个技巧可以提取没有行号前缀的行字符串(或者如果你想要的话),并且不需要在所有文件行上使用批量迭代(“for / F”加上计数)。
要这样做,必须使用findstr.exe始终在管道和反向过滤行中使用/ N标志,通过/B /C:"<N1>:" /C:"<N2>:" ... /C:"<NX>:"
参数在管道中使用第二个findstr.exe。
这里是我用来解析文本和二进制文件的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
<强>优点:强>
已知问题:
<强>示例:强>
调用print_file_string.bat -n。 example.txt 1:20:10:30 / R / C:“。*”
打印按行号排序的example.txt文件的1,10,20,30行 并使用行号前缀打印它们:
调用print_file_string.bat。 example.txt 100 / R / C:“。*”
打印第100个example.txt文件的字符串,并打印没有行号 前缀。
调用print_file_string.bat -pe c:\ Application res.dll“”/ B / C:“VERSION =”
从c:\ Application \ res.dll二进制文件中打印所有字符串,其中 字符串以“VERSION =”字符串开头,并打印出没有行号的字符串 前缀。
call print_file_string.bat -pe c:\ Application res.dll 1:20:10:30 / R / C:“。*”
从中打印1,10,20,30行字符串资源 c:\ Application \ res.dll二进制文件,其中字符串以 “VERSION =”字符串并打印它们没有行号前缀。