批处理文件 - 搜索一个词并提取变量中的下一个词

Batch File - Search for a word and extract the next word in a variable

我有一个日志文件,其中有很多没有特定格式的文本。它有一个特定的变量 'SId',随着文件的进行,它被分配了很多值。 例如:

For the first line le=24 we have SId = 23 and then,
on second it's SId = 56, following the be = 45 which......

我正在尝试创建一个批处理文件,它将读取整个文件并找到最后分配给变量 'SId' 的值。可能吗?

在没有看到实际日志文件内容的情况下,很难提供可靠的示例,但通常如果 SId 是每行的第一个字符串,则以下两个 FindStr 示例之一可能就足够了:

@For /F "Tokens=1,3" %%A In ('FindStr/BRC:"SId[ ]=[ ][0-9]*" "file.log"') Do @Set "%%A=%%B"
@Echo %SId%
@Pause

@For /F "Tokens=1,3" %%A In ('FindStr/RC:"^SId\ =\ [0-9]*" "file.log"') Do @Set "%%A=%%B"
@Echo %SId%
@Pause

您的最后一个值将分配给名为 %SId% 的变量。我使用 file.log 作为日志文件的名称,请根据需要进行调整。要查找 FindStr 的 options/switches,请在命令提示符处输入 FindStr/?

批处理并不是真正为此类事情而制作的。以下取决于一些事情:
- 在 SId= 之间有一个 space,在 = 之后有一个 space(可以用 find 字符串更改)
- 值后有一个有效的分隔符(space、制表符、逗号、=)或该值是该行的最后一个内容。

@echo off 
SETLOCAL EnableDelayedExpansion
for /f "delims=" %%a in ('type t.txt^|find "SId = "') do (
  set "line=%%a"
  set "line=!line:*SId =!
  set /a "last=!line:~1!" 2>nul
)
echo %last%

诀窍是删除从开始到(包括)搜索字符串的所有内容(遗憾的是 = 无法用此删除)然后使用其余部分(减去第一个字符,即=) 与 set /a 将数字分配给变量。
如果数字后面的字符串部分被重定向到 NUL,则会出现错误消息 "missing operand"。

首先,问题的答案:是的,有可能。

但我认为,阅读问题 如何 获得最后一个 SId 号码的每个人都会更加有趣。好吧,仅使用有限的 Windows 命令解释器功能从数据格式未知的文本文件中获取此数字确实很困难。出于这个原因,这个纯粹而糟糕的任务描述对我来说很有趣,我接受了这个编码挑战。

我在 C:\Temp 中创建了一个包含以下三行的文件 Test File.log

For the first line we have SId = 23 and then,
the second line contains nothing interesting despite SId=x8434
;on third it's "! SId  = 56, following SId=8434which ... SId34234 ... !" SId 

最后一行以分号开头,这是使用 FOR 解析的行的问题,因为 eol=; 是默认设置,导致默认忽略此类行。

最后一行还包含双引号,这需要在处理该行时延迟环境变量扩展,以及两个感叹号,这很容易导致替换两个 ! 以及处理该行之间的所有内容启用延迟扩展。

最后一行也包含4次SId。前两个 SId 在等号周围有不同数量的 space,第三个 SId 没有等号,因此可以忽略,最后一个 SId 之后有只有一个 space.

下面的批处理代码已为感兴趣的读者注释,但用于确定最后一次有效 SId 出现次数的代码块除外。这部分代码对于批处理编程的初学者来说真的很难解释。通过从批处理文件顶部删除 @echo off,从命令提示符 window 中删除批处理文件 运行 并查看输出命令行,肯定更容易理解此代码部分的工作原理.

带有 FOR 循环和子例程 GetNumberProcessLine 块用示例 Test File.log 执行四次,其值:

  = 56, following SId=8434which ... SId34234 ... !" SId 
=8434which ... SId34234 ... !" SId 
34234 ... !" SId 
 

注意:每个Value以尾随space结尾。

这个描述非常笼统的任务的批处理代码:

@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "LogFile=C:\Temp\Test File.log"
if not exist "%LogFile%" goto EndBatch

rem Use command FINDSTR to search for all lines containing case-sensitive
rem the string "SId = " without or with spaces around the equal sign and
rem at least one digit. Output by FINDSTR are the lines matching this regular
rem expression with line number and a colon at beginning because of option /N.
rem The option /N is used to make sure that no line with SId starts with a
rem semicolon as those lines would be ignored by default by FOR. The last
rem line with a string matching this simple regular expression is assigned
rem to variable LastLine. Otherwise this environment variable deleted
rem before the loop still does not exist after the loop finished.

set "LastLine="
for /F "delims=" %%I in ('%SystemRoot%\System32\findstr.exe /N /R /C:"SId *= *[0123456789]" "%LogFile%"') do set "LastLine=%%I"
if not defined LastLine goto EndBatch

rem The last line contains perhaps multiple times an equal sign and perhaps
rem even multiple "SId" (secure identifier) strings. So it is necessary to
rem process this last line really the hard way. And it is better to process
rem the line perhaps containing also double quotes or other characters with
rem a special meaning for the Windows command interpreter using delayed
rem environment variable expansion.

setlocal EnableDelayedExpansion
set "Identifier="

:ProcessLine
set "Value=!LastLine:*SID=!"
if not defined Value goto LineProcessed
if "!Value!" == "!LastLine!" goto LineProcessed

for /F "tokens=1,2" %%A in ("!Value!") do (
    set "LastID="
    if "%%A" == "=" (
        set "Number=%%B"
        call :GetNumber
    ) else (
        set "Number=%%A"
        if "!Number:~0,1!" == "=" (
            set "Number=!Number:~1!"
            call :GetNumber
        )
    )
    if defined LastID set "Identifier=!LastID!"
    set "LastLine=!Value!"
    goto ProcessLine
)
set "LastLine=!Value!"
goto ProcessLine

:GetNumber
if not defined Number goto :EOF
set "IsDigit=1"
for /F "delims=0123456789" %%I in ("!Number:~0,1!") do set "IsDigit=0"
if %IsDigit% == 0 goto :EOF
set "LastID=%LastID%%Number:~0,1%"
set "Number=!Number:~1!"
goto GetNumber

rem Pass the last found identifier from current environment with delayed
rem expansion to previous environment on restoring previous environment.

:LineProcessed
endlocal & set "Identifier=%Identifier%"
if not defined Identifier goto EndBatch

echo Last SId found: %Identifier%

rem Other command lines which process the found identifier.

:EndBatch
endlocal

例如 Test File.log 这个批处理文件的输出是:

Last SId found: 8434

要了解使用的命令及其工作原理,请打开命令提示符 window,在其中执行以下命令,并仔细阅读为每个命令显示的所有帮助页面。

  • call /?
  • echo /?
  • endlocal /?
  • findstr /?
  • for /?
  • goto /?
  • if /?
  • rem /?
  • set /?
  • setlocal /?

Single line with multiple commands using Windows batch file 解释了在一个命令行中使用的 & 运算符。