使用DOS / Batch分割随机长度的字符串

时间:2015-02-04 23:15:07

标签: regex windows batch-file string-split

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

"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 ...”之前的完整路径和HEX值(它是进程ID)

所以我希望输出为:

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

我试图通过尽可能少的“硬编码”搜索/替换来实现目标,因为字符串的许多部分不会是相同的内容或相同的长度。我试图使用FOR / F来分割字符串,但我无法找到两列,因为它们总是在变化。唯一不变的是“C:\ Program Files(x86)”部分。 (加上FOR有52个变量限制)

我写了一些棘手的批处理文件,但我开始认为我要求DOS太多了; - )

提前感谢您的帮助!

5 个答案:

答案 0 :(得分:3)

@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的文件,其中包含我的测试数据。

第一个过程只是抛弃.:之间的每个(空格分隔)参数,直到剩下两个 - 所需的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,这将是最后倒计时之前和之后的字符串 - 呃冒号。

对第一个参数重复此过程。倒数第二个值是所需的十六进制值。

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

在“set "var=whatever"”评论相当暗淡的小修订版中 - 着名的&公式在此案例中包含{{1}}失败(如子文档“Documents& Settings”所以封闭的引号可以删除,因为尾随空格不相关。虽然知道触发问题的测试数据是什么会有用 - 减少猜测。

答案 1 :(得分:2)

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

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

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

在每一行上,它查找最后一个十六进制字符串,该字符串夹在以" C:\ Program Files(x86)\"开头的文件路径之前的空格中。并以" .exe"结束。我做了搜索忽略的情况。

答案 2 :(得分:2)

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

@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!
)

答案 3 :(得分:1)

这是一个混合批处理+ 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, "$1 $2"));
}

log.Close();

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

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

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

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

答案 4 :(得分:0)

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

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

这有几个部分

  1. powershell -Command在引号中运行整个表达式,就好像它是从powershell命令行运行一样
  2. Get-Content就像linux cat命令一样 - 它读取整个文件内容
  3. -replace使用正则表达式将文件每一行的内容替换为括号中的两个匹配表达式