使用 DOS/Batch 以随机长度拆分字符串

Split String with Random Length using DOS/Batch

我有一个日志文件需要处理并从中提取数据。每行包含一个事件日志输出字符串。不幸的是,字符串的各个部分没有统一格式。以下是一些示例行:

"Some random length string.  0x8dda46 0x1 0x384 C:\Program Files (x86)\some\path\foo0.exe  "
"Some random leeeength string.  0xa95ac2 0x8cc C:\Program Files (x86)\some\path\foo1.exe %%1936 0xcc0  "
"Some random leength string.  0xbcd668 0x330 C:\Program Files (x86)\some\path\foo2.exe %%1936 0xf38  "
"Some random leeeeeeeength string.  0xbcd668 0x1 0x330 C:\Program Files (x86)\some\path\foo2.exe  "
"Some random leeength string.  0x352c44 0xfc0 C:\Program Files (x86)\some\path\foo3.exe %%1936 0x92c  "
"Some random leeeeength string.  0xa95ac2 0x0 0x8cc C:\Program Files (x86)\some\path\foo1.exe  "
"Some random leength string.  0x352c44 0x0 0xfc0 C:\Program Files (x86)\some\path\foo3.exe  "

我需要提取没有完整路径的 "foo.exe" 文件名和 "C:\Progra..." 之前的十六进制值(它是进程 ID)

所以我希望输出为:

0x384 foo0.exe
0x8cc foo1.exe
0x330 foo2.exe
0x330 foo2.exe
0xfc0 foo3.exe
0x8cc foo1.exe
0xfc0 foo3.exe

我正在尝试用尽可能少的 "hard coded" search/replace 来实现目标,因为字符串的许多部分不会具有相同的内容或相同的长度。我尝试使用 FOR /F 来拆分字符串,但我无法找到这两列,因为它们总是在变化。唯一不变的是 "C:\Program Files (x86)" 部分。 (加上 FOR 有 52 个变量限制)

我写了一些棘手的批处理文件,但我开始觉得我对 DOS 的要求太高了;-)

在此先感谢您的帮助!

这确实是一项需要正则表达式的任务,对于 windows 命令行中的正则表达式,您需要 powershell。幸运的是,您可以从批处理文件或 DOS 命令提示符 运行 powershell:

powershell -Command "(Get-Content 'c:\full_path_here\input.log') -replace '.+?(0x[0-9a-f]{3}) .+?\([^\]+\.exe).*', ' '"

这有几个部分

  1. powershell -Command 运行 整个表达式用引号括起来,就好像它是来自 powershell 命令行的 运行
  2. Get-Content 类似于 linux cat 命令 - 它读取整个文件内容
  3. -replace使用正则表达式将文件每一行的内容替换为括号中匹配的两个表达式
@ECHO OFF
SETLOCAL
FOR /f "tokens=1*delims=." %%a IN (q28333414.txt) DO (
 FOR /f "tokens=1*delims=:" %%c IN ("%%~b") DO CALL :process %%c&CALL :report "%%d

)
GOTO :EOF

:process
SET hexval=%~3
IF DEFINED hexval shift&GOTO process
SET "hexval=%~1"
SET "drive=%~2:"

GOTO :eof

:report
SET "line=%drive%%~1"
SET "line="%line:.exe=.exe"%"
FOR %%r IN (%line%) DO ECHO %hexval% %%~nxr&GOTO :eof

我使用了一个名为 q28333414.txt 的文件,其中包含您的数据用于我的测试。

第一个过程简单地丢弃 .: 之间的每个(space 分隔的)参数,直到正好剩下两个 - 所需的 hexval 和盘符。

report 进程重新附加驱动器号并将其和 .exe 名称括在引号中。 for %%r 选择第一个字符串,去掉引号,吐出结果,一切都完成了。


编辑:修复报告以仅根据需要显示文件名和扩展名以及 dbenham 注释


突发新闻:(字面意思!)

@ECHO OFF
SETLOCAL enabledelayedexpansion
FOR /f "delims=" %%a IN (q28333414.txt) DO SET "line=%%~a"&CALL :process "!line::=" "!"
)
GOTO :EOF

:process
SET "hexval=%~3"
IF DEFINED hexval shift&GOTO process
CALL :lastbar1 %%~1
SET "filename=%~2"
SET filename="c:%filename:.exe =.exe" %
FOR %%r IN (%filename%) DO ECHO %hexval% %%~nxr&GOTO :eof
GOTO :eof

:lastbar1
SET "hexval=%~3"
IF DEFINED hexval shift&GOTO lastbar1
SET "hexval=%~1"
GOTO :eof

好的 - 那我们试试吧。

对于每一行,用 " " 替换所有邪恶的冒号,并将生成的带引号的字符串序列传递给子例程。

移动参数直到只有 2 个,这将是最后一个倒计时前后的字符串 - 呃,冒号。

对第一个参数重复该过程。倒数第二个值是所需的 hexval。

用第二个参数,在任何.exe之前加上"c:,在任何.exe之后加上",所以结果是一个带引号的全文件名和渣滓;吐出 hexval 和文件名并完成...

在“&”注释相当暗淡的光线下进行的小修改 - 著名的 set "var=whatever" 公式失败,在这种情况下包含 &(如子目录 "Documents & Settings") 因此可以删除封闭的引号,因为尾随 space 不相关。知道触发问题的测试数据是什么会很有用 - 减少猜测。

这是一个混合批处理 + JScript 脚本(但仍然是一个 .bat 文件),它将执行类似于 NextInLine 的 PowerShell 解决方案的正则表达式替换。

@if (@CodeSection == @Batch) @then

@echo off
setlocal

set "logfile=test.log"

rem // Ask JScript to parse log.  On each line, %%I = hex.  %%J = exe.
for /f "tokens=1*" %%I in ('cscript /nologo /e:JScript "%~f0" "%logfile%"') do (
    echo %%I %%J
)

rem // End main runtime.
goto :EOF

@end
// JScript chimera portion
var fso = WSH.CreateObject('Scripting.FileSystemObject'),
    log = fso.OpenTextFile(WSH.Arguments(0), 1);

while (!log.AtEndOfStream) {
    var line = log.ReadLine();
    WSH.Echo(line.replace(/^.+(0x[0-9a-f]+) \w:\.+?\(\w+\.exe).+$/i, " "));
}

log.Close();

当然,如果我在你的船上,我可能会使用 GnuWin32 sed

sed -r -e "s/^.*(0x[a-f0-9]+) \w:.+\(.+\.exe).*$/ /i" test.log

只是为了开玩笑,我 运行 针对上面 O.P. 的测试日志文件对每个完全工作的解决方案进行了一些时间测试,运行 每次测试几次并得到模式持续时间(最常出现的结果)。

  • Aacini 的解决方案:0.013 秒(优秀,但取决于窄匹配)
  • sed:0.015s(最简单)
  • Magoo 的解决方案:0.034s(聪明!)
  • 我的 JScript 混合体:0.034 秒(当然是最好的)
  • dbenham的jrepl.bat:0.051s(强大的瑞士军刀方案)
  • NextInLine 的 PowerShell:挂起我的计时器脚本,但在 PowerShell 的最初痛苦启动后感觉大约半秒

您可以使用的任何好的正则表达式实用程序都应该能够解决您的问题。我喜欢用我的 JREPL.BAT hybrid JScript/batch utility。它是纯脚本,可​​以在任何 Windows XP 以上的机器上本地运行。

假设你的文件是test.log,那么我会使用:

jrepl ".* (0x[0-9A-F]+) C:\Program Files \(x86\)\(?:.*\)?([^\]+\.exe) .*" " " /i /f test.log

在每一行中,它会查找以 "C:\Program Files (x86)\" 开头并以“.exe”结尾的文件路径之前夹有空格的十六进制字符串的最后一次出现。我让搜索忽略大小写。

此解决方案假定随机字符串中没有反斜杠。

@echo off
setlocal EnableDelayedExpansion

for /F "tokens=1-5 delims=\" %%a in (logFile.txt) do (
   rem Extract the HEX value
   for %%A in (%%~a) do (
      set "value=!lastButOne!"
      set "lastButOne=%%A"
   )
   rem Extract the file name
   for /F %%A in ("%%e") do set "name=%%A"
   echo !value! !name!
)