2010-04-23 81 views
6

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

@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(结果:1)

我想通容易的事会修改head.bat代码在这里找到: Windows batch command(s) to read first line from text file
除了不呼应每一行 - 我只是呼应了最后线找到。并不像人们想象的那么简单。我注意到我的柜台由于某种原因停留在零。我想知道set /a counter+=1是否在做我认为正在做的事情。

+1

请注意'for/f'会跳过空行。在某些情况下,这可能也可能会使您的计数失效。 – Joey 2010-04-24 10:02:52

回答

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% 
+0

啊,明白了。 初学者 set/a counter = 1 然后设置一个等于%% a的环境变量并回显 - 因为我猜测%% a不存在于for语句之外。 for/f“usebackq delims =”%% a in(c:\ file_list.txt)do(if“!counter!”==“%1”set line = %% a&goto:printme set/a计数器+ = 1) :printme echo%line% – Lee 2010-04-23 21:06:18

12

我知道这是一个老问题,但在这里是有类似问题的人一些额外的信息...

李,你为什么“%%一个”不能正常工作之外的推理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 “')

  • 'FINDSTR/N。* “路径\文件名”' 读取文件,并添加了行号( '/N ')的每一个线(' 。*' 是一个正则表达式匹配任何字符的“0或更多”)。由于每行现在都有一个行号(即使是空行),for循环也不会跳过任何行。

每行现在看起来像这里面的for循环:

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

接下来,我们使用“令牌= 1 * delims =:”打破了行号和内容。

  • 令牌= 1 *”设定第一令牌定界符之前(存储在%%一个)的一切,和第二令牌(存储在%% b)至所有后。
  • 'delims =:'设置为“”作为用于分隔字符串的分隔符。现在

,我们遍历文件,%%一个将返回当前行号和%% b将返回该行的内容。

所有剩下的就是比较%1参数%%一个(而不是一个计数器变量),并使用%% b存储当前行内容:如果“%%一个” ==“%1”set line = %% b

另外一个好处是,“enabledelayedexpansion”不再是必要的,由于上述代码消除读取的中间为循环计数器变量。

编辑:改变 '回波%线路%' 到 '回波%线路%。'。这将正确显示空白行,而不是“ECHO关闭”。更改'键入c:\ file_list.txt^|因为findstr命令已经可以直接读取文件了,所以可以使用“filestr/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 
) 
) 
+0

我测试了它,但它与我的文件失败。首先我用'::::Column'(所有的冒号被删除)试过,然后用'\ .. \ .. \ .. \ windows \ system32 \ calc.exe'测试它,但是它没有打印此:-) – jeb 2012-10-26 05:37:49

+0

啊......不知道为什么calc.exe一个没有工作(它似乎适用于我),但你肯定让我的冒号都崩溃了。我将不得不重新考虑这种方法。我会用'find/n/v“”'代替'findstr',但是最后我会遇到同样的问题:']]]] Bracket'和'[[[[Bracket]都会显示为'Bracket'。似乎没有一种简单的方法来避免这些分隔符崩溃。 – 2012-10-26 07:41:02

+0

我开始认为使用“more + LineNumber”的方法可能是唯一可靠的方法([link](http://stackoverflow.com/questions/6409869/echo-the-nth-line -from-A-文本文件,其中正是-A-命令行参数的/ 13080820#13080820))。 我现在可能不得不编辑几个帖子:/ – 2012-10-26 07:47:19

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字符线:()<>%!^| “&

无效行号

上述功能还可以打印空行或包含特殊字符线,这是足以满足大多数情况下。然而,为了处理提供给该函数无效行号,请添加错误检查代码,这样的功能:

: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

  • 特殊字符 - 印刷

  • 空行 - 印刷

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

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 [email protected]"%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 

优点:

  • 不是通过在所有行 “为/ F” 迭代更快文件。
  • 使用特殊字符如& | %“`”?甚至!字符(在真正的dll资源上测试)
  • 处理来自PE文件(如dll和exe)的资源字符串(从https://technet.microsoft.com/en-us/sysinternals/strings.aspx下载字符串文件并将其放在脚本附近) 例如,您可以从EXE/DLL文件内置的字符串中提取版本字符串

已知问题:

  • 如果线(S)过滤器已经使用或-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. call print_file_string.bat。 example.txt 100/R /C:".*“

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

  3. 呼叫print_file_string.bat -pe C:\应用res.dll “”/ B/C: “VERSION =”

    打印从c中的所有字符串:\应用程序\ res.dll二进制文件,其中 以“VERSION =”字符串开头的字符串,并将其打印为不带行号 前缀的字符串。

  4. 呼叫print_file_string.bat -pe C:\应用res.dll 1:20:10:3​​0/R /C:".*”

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

相关问题