44

我想用另一个文件 xy.xml 中的字符串“我很好”替换文件 test.xml 中的字符串“我很好”。在 ms 构建中使用正则表达式。

即我必须从一个文件(xy.xml) 中读取字符串并将其替换为另一个文件test.xml。所以请提供必要的步骤来通过示例解决这个问题

4

8 回答 8

105

这不再需要...您现在可以将 C# 注入项目/构建文件...

定义自定义任务和参数,如下所示:

<UsingTask TaskName="ReplaceFileText" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
  <ParameterGroup>
    <InputFilename ParameterType="System.String" Required="true" />
    <OutputFilename ParameterType="System.String" Required="true" />
    <MatchExpression ParameterType="System.String" Required="true" />
    <ReplacementText ParameterType="System.String" Required="true" />
  </ParameterGroup>
  <Task>
    <Reference Include="System.Core" />
    <Using Namespace="System" />
    <Using Namespace="System.IO" />
    <Using Namespace="System.Text.RegularExpressions" />
    <Code Type="Fragment" Language="cs">
      <![CDATA[
            File.WriteAllText(
                OutputFilename,
                Regex.Replace(File.ReadAllText(InputFilename), MatchExpression, ReplacementText)
                );
          ]]>
    </Code>
  </Task>
</UsingTask>

然后像任何其他 MSBuild 任务一样简单地调用它

<Target Name="AfterBuild">
  <ReplaceFileText 
    InputFilename="$(OutputPath)File.exe.config" 
    OutputFilename="$(OutputPath)File.exe.config" 
    MatchExpression="\$version\$" 
    ReplacementText="1.0.0.2" />
</Target>

上面的示例将位于输出目录中的“File.exe.config”中的“$version$”替换为“1.0.0.2”。

于 2014-03-22T00:01:14.453 回答
10

有一种非常简单的方法来替换文件中的字符串:

  <Target Name="Replace" AfterTargets="CoreCompile">
      <PropertyGroup>
          <InputFile>c:\input.txt</InputFile>
          <OutputFile>c:\output.txt</OutputFile>
      </PropertyGroup>
      <WriteLinesToFile
          File="$(OutputFile)"
          Lines="$([System.IO.File]::ReadAllText($(InputFile)).Replace('from','to'))"
          Overwrite="true"
          Encoding="Unicode"/>
  </Target>

请参阅https://docs.microsoft.com/en-us/visualstudio/msbuild/property-functions?view=vs-2019 以探索可内联的 C# 代码。[System.Text.RegularExpressions.Regex]列入名单。

于 2020-06-29T08:09:13.187 回答
8

@csharptest.net的答案很好,但它不适用于 DotNetCore。我会将此添加为评论,但我没有足够的声誉。

在 DotNetCore 上,您必须更新:

  • 任务工厂到“RoslynCodeTaskFactory”
  • 任务程序集到“$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll”
  • 删除对“System.Core”的引用
  • 消费目标必须将“AfterTargets”属性指定为“Build”

其他一切都应该相同:

<Project Sdk="Microsoft.NET.Sdk.Web">
  ...

  <UsingTask
    TaskName="ReplaceFileText"
    TaskFactory="RoslynCodeTaskFactory"
    AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
    <ParameterGroup>
      <InputFilename ParameterType="System.String" Required="true" />
      <OutputFilename ParameterType="System.String" Required="true" />
      <MatchExpression ParameterType="System.String" Required="true" />
      <ReplacementText ParameterType="System.String" Required="true" />
    </ParameterGroup>
    <Task>
      <Using Namespace="System"/>
      <Using Namespace="System.IO"/>
      <Using Namespace="System.Text.RegularExpressions" />
      <Code Type="Fragment" Language="cs">
        <![CDATA[  
          File.WriteAllText(
            OutputFilename,
            Regex.Replace(File.ReadAllText(InputFilename), MatchExpression, ReplacementText)
            );
        ]]>
      </Code>
    </Task>
  </UsingTask>

  <Target Name="AfterBuildStep" AfterTargets="Build">
    <ReplaceFileText
       InputFilename="$(OutputPath)File.exe.config" 
       OutputFilename="$(OutputPath)File.exe.config" 
       MatchExpression="\$version\$" 
       ReplacementText="1.0.0.2" />
  </Target>
</Project>
于 2019-11-30T20:31:35.120 回答
4

编辑:这个答案已经过时了。使用下面的解决方案...

使用 ReadLinesFromFile 任务从 xy.xml 文件中获取替换字符串。检查这个

然后使用 xy.xml 中的值作为 FileUpdate 任务的替换字符串。检查这个

把它们放在一起;)

于 2011-10-25T11:36:16.883 回答
4

您可以使用 MSBuild 社区任务中的任务 FileUpdate,如文章 http://geekswithblogs.net/mnf/archive/2009/07/03/msbuild-task-to-replace-content-in-text-files.aspx中所述

于 2014-10-02T08:20:12.607 回答
2

如果您不想使用第三方(社区)二进制文件,也不想将代码嵌入到您的 msbuild 项目中,我建议您创建一个简单的任务库,该库可以实现File.WriteAllText并在以后托管其他任务:

using System.IO;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;

public class FileWriteAllText : Task
{
    [Required]
    public string Path { get; set; }
    [Required]
    public string Contents { get; set; }

    public override bool Execute()
    {
        File.WriteAllText(Path, Contents);
        return true;
    }
}

然后您可以在 msbuild 中替换、追加等:

<UsingTask TaskName="FileWriteAllText" AssemblyFile="MyTasks.dll" />
<FileWriteAllText Path="test.xml"
     Contents="$([System.Text.RegularExpressions.Regex]::Replace(
         $([System.IO.File]::ReadAllText('test.xml')), 'how r u', 'i am fine'))" />
于 2020-06-12T17:03:49.690 回答
0

我针对位于 Unix 驱动器上的同一个文件运行了这两个替换,并使用了它的 unc 路径 \server\path...:

<ReplaceFileText
  InputFilename="$(fileToUpdate)"
  OutputFilename="$(fileToUpdate)"
  MatchExpression="15.0.0"
  ReplacementText="15.3.1"/>


<FileUpdate Files="$(fileToUpdate2)"
            Regex="15.0.0"
            ReplacementText="15.3.1" />

并且上面的cs自定义动作不添加bom;但是 FileUpdate 做到了:

%head -2 branding.h branding2.h
==> branding.h <==
#/* branding.h
#** This file captures common branding strings in a format usable by both sed and C-preprocessor.

==> branding2.h <==
#/* branding.h
#** This file captures common branding strings in a format usable by both sed and C-preprocessor.

感谢 csharptest.net - 我正在使用 perl 替代命令执行 exec,用于 unix 构建。

于 2015-03-20T16:34:10.263 回答
0

詹姆斯的更新回答

  <UsingTask TaskName="ReplaceTextInFiles" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.$(VsBuildTaskBinarySuffix).dll">
<ParameterGroup>
  <MatchExpression ParameterType="System.String" Required="true" />
  <ReplacementExpression ParameterType="System.String" Required="true" />
  <InputFile ParameterType="Microsoft.Build.Framework.ITaskItem" Required="true" />      
  <IsTextReplaced ParameterType="System.Boolean"  Output="True"/>
</ParameterGroup>
<Task>
  <Reference Include="System.Core" />
  <Using Namespace="System" />
  <Using Namespace="System.IO" />
  <Using Namespace="System.Text.RegularExpressions" />
  <Code Type="Fragment" Language="cs">
    <![CDATA[
      bool isMatchFound = false;
      string filecontent = "";
      string path = InputFile.ItemSpec;

      Log.LogMessage(MessageImportance.High, "[ReplaceTextInFiles]: Match= " + MatchExpression);
      Log.LogMessage(MessageImportance.High, "[ReplaceTextInFiles]: Replace= " + ReplacementExpression);

      IsTextReplaced = false;
      using(StreamReader rdr = new StreamReader(path))
      {
        filecontent = rdr.ReadToEnd();
        if (Regex.Match(filecontent, MatchExpression).Success)
        {
          filecontent = Regex.Replace(filecontent, MatchExpression, ReplacementExpression);
          isMatchFound = true;            
        }
      }

      if(isMatchFound){
        using(StreamWriter wrtr = new StreamWriter(path))
        {
          wrtr.Write(filecontent);
          IsTextReplaced = true;
          Log.LogMessage(MessageImportance.Normal, "[ReplaceTextInFiles]: Replaced text in file:" + path);
        }
      }       
    ]]>
  </Code>
</Task>

于 2020-05-27T18:44:49.387 回答