2010-04-23 54 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 =」字符串開頭,並將它們打印爲不帶行號前綴。