脚本智能字符串匹配并在批处理文件

时间:2016-05-12 18:44:37

标签: windows batch-file command-line

我有多个文本(.cs)文件都有类似于这样的行:

public partial class ApiIThis :  IEquatable<ApiIThis>

public partial class ApiIThat :  IEquatable<ApiIThat>

我需要将它们转换为

public partial class ApiIThis :  IEquatable<ApiIThis>, IThis
public partial class ApiIThat :  IEquatable<ApiIThat>, IThat

这将在需要在.bat环境中运行的Windows command line文件中运行

更新:回答@aschipfl

  

到目前为止你尝试了什么,你有什么问题?请   通过在问题中添加信息来分享您的努力!

到目前为止,我所做的是在谷歌搜索中找到一些我正在尝试做的例子。我发现SO帖子(How to replace substrings in windows batch file)看起来有点相关,但这还不足以让我离开。

  

无论如何,.cs文件只有你所展示的行,甚至一行   仅?

不,这些不是文件中的唯一行。这些文件将包含C#工具自动生成的完整Swagger类。

  

你是否需要在其他行之间找到它们,如果是,那么通过什么   标准是什么?

是的,他们将在其他行之间。但是,它将是文件中唯一这样的行。该文件的定义如下:

using Something;
using SomethingElse;

public partial class ApiIThis :  IEquatable<ApiIThis>
{
    public ApiIThis() 
    { 
        /* Other code here */ 
    }

    /* Other code here */ 
}
  

课后和中间的单词总是匹配吗?

是的,它们将始终匹配。

  

单词可能包含空格吗?

不,它们永远不会包含空格。

  

是否要在单个输出文件中收集转换后的行,   或者你想为每个输入文件单独输出文件?或者你甚至   想要覆盖/替换原始文件?

我希望更新原始文件。

最后,我想补充说,它们都将以Api开头,实际上ApiI

3 个答案:

答案 0 :(得分:2)

@echo off
setlocal EnableDelayedExpansion

rem Process all *.cs files
for %%a in (*.cs) do (
   echo Processing "%%a"

   rem Get the number of the target line
   for /F "delims=:" %%b in ('findstr /N /C:"public partial class" "%%a"') do set /A "skip=%%b-1"

   rem Read from original file
   < "%%a" (

      rem Copy previous lines
      for /L %%i in (1,1,!skip!) do (
         set "line="
         set /P "line="
         echo/!line!
      )

      rem Process target line
      set /P "line="
      for /F "tokens=4,6" %%b in ("!line!") do (
         set "Api=%%b"
         echo public partial class %%b :  %%c, !Api:~3!
      )

      rem Copy rest of lines
      findstr "^"

   rem Write to new file
   ) > "%%~Na.new"

   move /Y "%%~Na.new" "%%a"
)

编辑:我使用OP提供的文件测试了此解决方案。令人惊讶的是,这些文件 NOT 以CR + LF对结束,但只是在LF(Linux风格)中,因此必须将它们转换为CR + LF标准才能由批处理文件处理(使用Windows标准)。

我使用more命令以这种方式转换文件:

for %a in (*.cs) do more %a > %~Na.new
del *.cs
ren *.new *.cs

之后转换是正确的,除了可以直接插入相应echo命令的行开头的4个空格。

C:\> test.bat
Processing "ApiIClaim.cs"
Processing "ApiIClaimType.cs"
Processing "ApiIMonetaryType.cs"


C:\> for %a in (*.cs) do @fc %a %~Na.new
Comparando archivos ApiIClaim.cs y APIICLAIM.NEW
***** ApiIClaim.cs
    [DataContract]
    public partial class ApiIClaim :  IEquatable<ApiIClaim>
    {
***** APIICLAIM.NEW
    [DataContract]
public partial class ApiIClaim :  IEquatable<ApiIClaim>, IClaim
    {
*****

Comparando archivos ApiIClaimType.cs y APIICLAIMTYPE.NEW
***** ApiIClaimType.cs
    [DataContract]
    public partial class ApiIClaimType :  IEquatable<ApiIClaimType>
    {
***** APIICLAIMTYPE.NEW
    [DataContract]
public partial class ApiIClaimType :  IEquatable<ApiIClaimType>, IClaimType
    {
*****

Comparando archivos ApiIMonetaryType.cs y APIIMONETARYTYPE.NEW
***** ApiIMonetaryType.cs
    [DataContract]
    public partial class ApiIMonetaryType :  IEquatable<ApiIMonetaryType>
    {
***** APIIMONETARYTYPE.NEW
    [DataContract]
public partial class ApiIMonetaryType :  IEquatable<ApiIMonetaryType>, IMonetaryType
    {
*****

答案 1 :(得分:1)

以下脚本 - 让我们称之为append-keyword.bat - 做你想做的事。要使用它,请提供要作为命令行参数处理的所有文件;还允许使用通配符?*

@echo off
setlocal EnableExtensions DisableDelayedExpansion

set "TMPF=%TEMP%\%~n0_%RANDOM%.tmp"
:LOOP
set "ARGF=%~1"
if defined ARGF (
    setlocal EnableDelayedExpansion
    for %%F in ("!ARGF!") do (
        endlocal
        if /I not "%%~fF"=="%~f0" (
            set "FILE=%%~fF"
            setlocal EnableDelayedExpansion
            if exist "!FILE!" if not exist "!FILE!\" (
                rem /* (it's a file but not a dir. due to "\") */
                > "!TMPF!" call :PROCESS MOV "!FILE!"
                if defined MOV (
                    > nul move /Y "!TMPF!" "!FILE!"
                ) else (
                    > nul del "!TMPF!"
                )
            )
        ) else (
            setlocal EnableDelayedExpansion
        )
    )
    endlocal
    shift /1
    goto :LOOP
)

endlocal
exit /B


:PROCESS  return  item
setlocal DisableDelayedExpansion
set "ITEM=%~2"
set "RTN="
setlocal EnableDelayedExpansion
for /F delims^=^ eol^= %%L in ('findstr /N /R "^^" "!ITEM!"') do (
    endlocal
    set "LINE=%%L"
    set "SKIP="
    rem // Default delimiters TAB and SPACE:
    setlocal EnableDelayedExpansion
    for /F "eol=/ tokens=1-3,*" %%A in ("!LINE:*:=!") do (
        endlocal
        if "%%A %%B %%C"=="public partial class" (
            rem // Delimiters `:`, TAB and SPACE:
            for /F "eol=/ tokens=1,* delims=:    " %%E in ("%%D") do (
                rem // Delimiters `<`:
                for /F "eol=/ tokens=1,* delims=<" %%G in ("%%F") do (
                    if "%%G"=="IEquatable" (
                        rem // Delimiters `>`, `,`, TAB and SPACE:
                        for /F "eol=/ tokens=1,* delims=>,   " %%I in ("%%H") do (
                            if "%%J"=="" (
                                if "%%E"=="%%I" (
                                    set "STR=%%E"
                                    setlocal EnableDelayedExpansion
                                    if "!STR:~,3!"=="Api" (
                                        echo(!LINE:*:=!, !STR:~3!
                                        endlocal
                                        set "SKIP=#"
                                    ) else (
                                        endlocal
                                    )
                                )
                            )
                        )
                    )
                )
            )
        )
        setlocal EnableDelayedExpansion
    )
    if not defined SKIP (
        echo(!LINE:*:=!
    ) else (
        endlocal
        set "RTN=#"
        setlocal EnableDelayedExpansion
    )
)
endlocal
endlocal & set "%~1=%RTN%"
exit /B

主程序完成以下任务:

  • 处理命令行参数,循环遍历它们;
  • 通过for循环解析通配符;
  • 排除要处理的脚本本身;
  • 检查每个文件是否存在;
  • 将子例程的返回数据写入临时文件;
  • 在发生更改时将临时文件移动到已处理的文件上;

子程序执行以下操作:

  • 逐行读取给定文件(即使是空行);
  • 按此顺序检查单词publicpartialclass,而不是检查其间的空白数量; (请注意,不检查单词是否后跟冒号!)
  • 检查单词IEquatable,然后检查<,更多文字和>;
  • 检查上面的文字是否后跟其他内容(如果是,行/文件已经处理过,请跳过它);
  • 检查class之后和<>之间的字词是否匹配; (认为​​后一个字符串不能单独包含<>,!)
  • 检查该单词是否以Api开头,将其剪切并将剩余的字符串附加到该行,由, SPACE 分隔;
  • 返回(echo)修改后的行或原始行,具体取决于上述所有检查是否成功;
  • 返回一个标志,指示是否有任何行已被更改;
  • 以区分大小写的方式进行所有字符串比较;

答案 2 :(得分:-1)

过滤(使用PowerShell)

这是一个使用PowerShell的脚本(自Vista以来所有Windows,通过更新安装在XP上)。

Param(
    [String]
    $input_file
)

Function Filter-Data
{
    Param(
        [Parameter(ValueFromPipeline=$True)]
        [String]
        $line
    )
    Process {
        if ($line -match "public\s+partial\s+class\s+ApiI([A-Za-z0-9_]+)\s*:\s*IEquatable\s*<\s*ApiI[A-Za-z0-9_]+\s*>") {
            Return $matches[0] + ", " + $matches[1]
        } Else {
            Return $line
        }
    }
}

Get-Content $input_file | Filter-Data | Write-Host

你给它输入文件名。它吐出过滤后的数据。您可能希望使过滤更加智能化(允许空格等),但是这里的功能可以解决问题。

调用可能如下所示:

PowerShell -NoProfile -ExecutionPolicy Bypass -File filter.ps1 source_file.cs <NUL
笔记
  • -NoProfile会告诉PoSH不要加载用户的个人资料(启动速度更快)
  • -ExecutionPolicy必须允许文件,如果您尚未在计算机上配置文件,则需要指定ByPass以便PoSH运行脚本。
  • <NUL PowerShell经常在完成执行之前等待您按ENTER键。在脚本完成后,将任何内容重定向都不会将控制权返回给调用者。

编辑根据添加到问题的详细信息和关于琐事的评论,我更新了过滤代码,以便更一般地匹配和替换。它还允许C#令牌之间的空格,即使这不是官方要求(我不完全信任代码生成器)。