在批处理Windows中使用正则表达式

3
我目前正在处理一个批处理任务(我也是新手),以获取Maven依赖项列表。 一直在尝试使用正则表达式来获取这些依赖项,但一直没有成功。我已经在此网站上测试了我的正则表达式,看起来它是可以工作的。
stackoverflow问题中查找了需要转义字符的FOR循环,但仍然无法解决问题,因此我不得不在这里寻求帮助。
请参见下面的代码片段,特别是正则表达式部分,外部循环是正确的。
for /f "tokens=*" %%i in ('findstr "+- \\-"  dependency-list.txt') do (
    for /f "tokens=*" %%j in ('findstr /i /r "^^(.+^)\[:\]^(.+^)^(:jar:^)^(.+^)^(:compile^)" %%i') do (
        echo %%j
    )
)

这是从我的外部循环中获取的示例数据:
[INFO] +- org.owasp.esapi:esapi:jar:2.0.1:compile
[INFO] |  +- commons-configuration:commons-configuration:jar:1.5:compile
[INFO] |  |  +- commons-lang:commons-lang:jar:2.3:compile

我想只使用正则表达式来获取这些部分:

commons-configuration:commons-configuration:jar:1.5:compile
org.owasp.esapi:esapi:jar:2.0.1:compile
commons-lang:commons-lang:jar:2.3:compile

以下是我在尝试执行批处理脚本时遇到的错误:

这些是我在尝试执行批处理脚本时遇到的错误。

FINDSTR: Cannot open org.owasp.esapi:esapi:jar:2.0.1:compile
'+-' is not recognized as an internal or external command,
operable program or batch file.
| was unexpected at this time.

任何帮助都会非常感激。
注意: 一旦我成功执行我的正则表达式,外部循环就可以被移除。

阅读FINDSTR命令的帮助文件。它具有非常有限的正则表达式支持。所见即所得。 - Squashman
你得到的错误应该很明显。第一个findstr的输出不是文件。即使你在%%i周围加上引号。 - Squashman
1个回答

1
FINDSTR的正则表达式功能非常有限,可以在打开命令提示符窗口并运行findstr /?获取输出帮助来了解。与Perl兼容的正则表达式Boost.Regex或其他使用Perl语法的正则表达式实现相比,FINDSTR的正则表达式功能远远不够。
没有选项可用于仅获取由正则表达式匹配的字符串的输出。有几个选项可以控制FINDSTR的输出,但没有一个选项可以仅获取找到的字符串的输出。
因此,需要使用不同的方法从包含+-\-的处理行中删除感兴趣的数据之前的所有内容,在文件dependency-list.txt中。Windows命令处理器仅提供两个用于字符串重新格式化任务的命令:FORSET
下面的批处理文件演示了它们两个,考虑到需要在访问同一命令块中定义/修改的环境变量值时使用延迟扩展。
@echo off
setlocal EnableExtensions DisableDelayedExpansion
cls
set "DeleteExample="
if exist dependency-list.txt goto GetData
(
    set "DeleteExample=1"
    echo First line with no data of interest.
    echo [INFO] +- org.owasp.esapi:esapi:jar:2.0.1:compile
    echo [INFO] ^|  +- commons-configuration:commons-configuration:jar:1.5:compile
    echo [INFO] ^|  ^|  +- commons-lang:commons-lang:jar:2.3:compile
    echo ;[INFO]^|  \- commons-lang:commons-lang:jar:4.5:compile
    echo Line trailing space at end +- 
    echo Line with no space at end  +-
    echo Line with just a hyphen character - somewhere on line.
    echo [INFO] ^|  ^|  +- !commons-configuration!:commons-configuration!:jar:5.2:compile
    echo Last line with no data of interest.
)>dependency-list.txt

:GetData
echo First solution
echo ==============
echo/
setlocal EnableDelayedExpansion
for /F "tokens=*" %%I in ('%SystemRoot%\System32\findstr.exe /R "+- \\-" dependency-list.txt 2^>nul') do (
    echo Line read: "%%I"
    set "Line=%%I"
    set "Line=!Line:*- =!"
    if defined Line echo Line work: "!Line!"
    rem More commands working with environment variable Line referenced with
    rem exclamation marks for delayed expansion on execution of the command line.
)
endlocal

echo/
echo Second solution
echo ===============
echo/
for /F "tokens=1* delims=-" %%I in ('%SystemRoot%\System32\findstr.exe /R "+- \\-" dependency-list.txt') do (
    if not "%%J" == "" (
        echo Line read: "%%J"
        set "Line=%%J"
        setlocal EnableDelayedExpansion
        call set "Line=%%Line:~1%%"
        if defined Line call echo Line work: "%%Line%%"
        rem More commands working with environment variable Line referenced
        rem with two percent signs on both side and with using command CALL.
        endlocal
    )
)

echo/
echo Third solution
echo ==============
echo/
for /F "tokens=1* delims=-" %%I in ('%SystemRoot%\System32\findstr.exe /R "+- \\-" dependency-list.txt 2^>nul') do (
    if not "%%J" == "" (
        echo Line read: "%%J"
        for /F "tokens=*" %%L in ("%%J") do if not "%%L" == "" (
            echo Line work: "%%L"
            rem More commands working with loop variable L.
        )
    )
)

echo/
echo Fourth solution
echo ===============
echo/
for /F delims^=^ eol^= %%I in ('%SystemRoot%\System32\findstr.exe /R "+- \\-" dependency-list.txt 2^>nul') do (
    echo Line read: "%%I"
    set "Line=%%I"
    setlocal EnableDelayedExpansion
    set "Line=!Line:*- =!"
    if defined Line echo Line work: "!Line!"
    rem More commands working with environment variable Line referenced with
    rem exclamation marks for delayed expansion on execution of the command line.
    endlocal
)

echo/
echo Fifth solution
echo ==============
echo/
for /F delims^=^ eol^= %%I in ('%SystemRoot%\System32\findstr.exe /L /C:+- /C:"\\-" dependency-list.txt') do (
    echo Line read: "%%I"
    set "Line=%%I"
    call :ProcessLine
)
goto EndDemo

:ProcessLine
set "Line=%Line:*- =%"
if defined Line echo Line work: "%Line%"
rem More commands working with environment variable Line referenced with
rem percent signs for expansion before execution of the command line.
goto :EOF

:EndDemo
if defined DeleteExample del dependency-list.txt
echo/
endlocal
pause

这个批处理文件的输出是:
First solution
==============

Line read: "[INFO] +- org.owasp.esapi:esapi:jar:2.0.1:compile"
Line work: "org.owasp.esapi:esapi:jar:2.0.1:compile"
Line read: "[INFO] |  +- commons-configuration:commons-configuration:jar:1.5:compile"
Line work: "commons-configuration:commons-configuration:jar:1.5:compile"
Line read: "[INFO] |  |  +- commons-lang:commons-lang:jar:2.3:compile"
Line work: "commons-lang:commons-lang:jar:2.3:compile"
Line read: "Line trailing space at end +- "
Line read: "Line with no space at end  +-"
Line work: "Line with no space at end  +-"
Line read: "[INFO] |  |  +- :commons-configuration:jar:5.2:compile"
Line work: ":commons-configuration:jar:5.2:compile"

Second solution
===============

Line read: " org.owasp.esapi:esapi:jar:2.0.1:compile"
Line work: "org.owasp.esapi:esapi:jar:2.0.1:compile"
Line read: " commons-configuration:commons-configuration:jar:1.5:compile"
Line work: "commons-configuration:commons-configuration:jar:1.5:compile"
Line read: " commons-lang:commons-lang:jar:2.3:compile"
Line work: "commons-lang:commons-lang:jar:2.3:compile"
Line read: " "
Line read: " !commons-configuration!:commons-configuration!:jar:5.2:compile"
Line work: "!commons-configuration!:commons-configuration!:jar:5.2:compile"

Third solution
==============

Line read: " org.owasp.esapi:esapi:jar:2.0.1:compile"
Line work: "org.owasp.esapi:esapi:jar:2.0.1:compile"
Line read: " commons-configuration:commons-configuration:jar:1.5:compile"
Line work: "commons-configuration:commons-configuration:jar:1.5:compile"
Line read: " commons-lang:commons-lang:jar:2.3:compile"
Line work: "commons-lang:commons-lang:jar:2.3:compile"
Line read: " "
Line read: " !commons-configuration!:commons-configuration!:jar:5.2:compile"
Line work: "!commons-configuration!:commons-configuration!:jar:5.2:compile"

Fourth solution
===============

Line read: "[INFO] +- org.owasp.esapi:esapi:jar:2.0.1:compile"
Line work: "org.owasp.esapi:esapi:jar:2.0.1:compile"
Line read: "[INFO] |  +- commons-configuration:commons-configuration:jar:1.5:compile"
Line work: "commons-configuration:commons-configuration:jar:1.5:compile"
Line read: "[INFO] |  |  +- commons-lang:commons-lang:jar:2.3:compile"
Line work: "commons-lang:commons-lang:jar:2.3:compile"
Line read: ";[INFO]|  \- commons-lang:commons-lang:jar:4.5:compile"
Line work: "commons-lang:commons-lang:jar:4.5:compile"
Line read: "Line trailing space at end +- "
Line read: "Line with no space at end  +-"
Line work: "Line with no space at end  +-"
Line read: "[INFO] |  |  +- !commons-configuration!:commons-configuration!:jar:5.2:compile"
Line work: "!commons-configuration!:commons-configuration!:jar:5.2:compile"

Fifth solution
==============

Line read: "[INFO] +- org.owasp.esapi:esapi:jar:2.0.1:compile"
Line work: "org.owasp.esapi:esapi:jar:2.0.1:compile"
Line read: "[INFO] |  +- commons-configuration:commons-configuration:jar:1.5:compile"
Line work: "commons-configuration:commons-configuration:jar:1.5:compile"
Line read: "[INFO] |  |  +- commons-lang:commons-lang:jar:2.3:compile"
Line work: "commons-lang:commons-lang:jar:2.3:compile"
Line read: ";[INFO]|  \- commons-lang:commons-lang:jar:4.5:compile"
Line work: "commons-lang:commons-lang:jar:4.5:compile"
Line read: "Line trailing space at end +- "
Line read: "Line with no space at end  +-"
Line work: "Line with no space at end  +-"
Line read: "[INFO] |  |  +- !commons-configuration!:commons-configuration!:jar:5.2:compile"
Line work: "!commons-configuration!:commons-configuration!:jar:5.2:compile"

前四个解决方案在单独的命令进程中运行,使用cmd.exe /C启动命令行并在后台运行:

C:\Windows\System32\findstr.exe /R "+- \\-" dependency-list.txt

这条命令行会在当前目录中搜索名为 dependency-list.txt 的文件,该目录可能与批处理文件所在的目录不同,并使用区分大小写的正则表达式查找包含 +-\- 的行。

第五种解决方案在单独的命令进程中运行命令行:

C:\Windows\System32\findstr.exe /L /C:+- /C:"\\-" dependency-list.txt

这个命令行使用区分大小写的文字搜索,查找包含+-\-的行。 FINDSTR不会向STDOUT输出任何内容,如果没有找到包含+-\-的行,也不会向STDERR输出任何内容。 FOR捕获单独命令进程的STDOUT输出,并逐行处理输出。
空行被FOR忽略,默认情况下以分号开头的行也会被忽略,因为eol=;是默认的行结束字符选项。 FOR默认将行拆分为子字符串(标记),使用普通空格和水平制表符作为字符串定界符,并将第一个空格/制表符分隔的字符串分配给指定的循环变量。

第一种解决方案

第一种解决方案通过使用选项tokens=*修改FOR的行处理行为,从而删除行中所有前导空格/制表符,如果还剩下什么,则将其余部分(包括空格/制表符)分配给指定的循环变量I。输出中以Line read:开头的行显示了由FOR处理后分配给循环变量I的字符串。
这个字符串被赋值给环境变量Line。然后,使用命令SET进行字符串替换,将行中第一个由连字符和空格组成的字符串之前的所有内容删除,并根据需要使用延迟的环境变量扩展来引用在以(开始并以匹配的)结束的命令块内定义/修改的环境变量的值。
如果在执行字符串替换后仍有剩余的内容,这部分行将带有Line work:输出,以查看字符串替换所做的工作。
这个解决方案很快,但是有缺点,以分号开头的行将被FOR忽略,并且永久启用延迟环境变量扩展会导致未正确处理包含一个或多个感叹号的行。然而,在文件dependency-list.txt中不包含以;开头且包含一个或多个!的感兴趣行时,这是最好的解决方案。

第二种解决方案

第二种解决方案使用命令FOR将每个非空行(不以分号开头)拆分为两个子字符串。第一个子字符串直到出现一个或多个-为止分配给指定的循环变量I,剩余的行分配给下一个循环变量J,根据ASCII表进行操作。

因此,对于示例行,J包含带前导空格的感兴趣数据,但不是空字符串。使用命令SET再次获取分配给环境变量Line的前导空格,最后只得到从第二个字符开始的字符串,即从字符索引1到字符串结尾的字符串。

但是,与其使用延迟扩展,不妨采用不同的技术,通过在命令块中引用刚刚设置的环境变量值,并在两侧使用两个百分号来使用CALL命令强制Windows命令处理器解析命令SETECHO执行前的两行。因此,在执行FOR命令之前解析整个命令块时,带有CALL的两行变为:

call set "Line=%Line:~1%"
if defined Line call echo Line work: %Line%

那两行代码在每次循环迭代中都会被解析两次,因为命令CALL导致在执行setecho之前分别用当前字符串替换%Line:~1%%Line%而完全不含第一个字符。

由于避免使用延迟扩展,该解决方案可以正确处理包含感叹号的有趣行。

第三个解决方案

第三个解决方案与第二个解决方案类似。它使用第二个FOR从外部FOR分配给循环变量J的字符串中删除所有前导空格/制表符,并将剩余的字符串分配给循环变量L,甚至可以是空字符串。

这个解决方案肯定比第二个解决方案更好,因为速度更快。

第四个解决方案

第四个解决方案旨在始终将非空行分配给捕获输出的循环变量I,独立于第一个字符是哪个字符。通过定义空分隔符列表和忽略以特定字符开头的行来禁用行拆分行为。

第一种方法中,删除连字符和空格左侧的字符串。与之不同的是,在将行分配给环境变量Line时禁用了延迟扩展,并且在处理行字符串时仅暂时启用了延迟扩展。
这种解决方案的缺点是循环内的命令setlocal EnableDelayedExpansionendlocal做了比仅启用和禁用延迟扩展更多的工作。有关命令SETLOCALENDLOCAL的详细信息,请参见this answer。因此,该解决方案绝对不是所提供解决方案中最快的解决方案之一。
第五种解决方案类似于第四种解决方案,不同之处在于使用从FOR循环内调用的子例程来避免在处理字符串值时使用延迟扩展的常见技术。
这种解决方案也非常慢,但具有更容易调试批处理文件的重大优势,可在批处理文件中顶部使用@echo ON来运行它。

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

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接