【发布时间】:2011-12-11 20:46:32
【问题描述】:
我想用另一个文件xy.xml中的字符串“我很好”替换文件test.xml中的字符串“我很好”。在ms build中使用正则表达式。
我必须从一个文件(xy.xml) 中读取字符串并将其替换为另一个文件test.xml。 所以请提供必要的步骤来通过示例解决这个问题
【问题讨论】:
标签: msbuild msbuild-task msbuild-propertygroup msbuild-buildengine
我想用另一个文件xy.xml中的字符串“我很好”替换文件test.xml中的字符串“我很好”。在ms build中使用正则表达式。
我必须从一个文件(xy.xml) 中读取字符串并将其替换为另一个文件test.xml。 所以请提供必要的步骤来通过示例解决这个问题
【问题讨论】:
标签: msbuild msbuild-task msbuild-propertygroup msbuild-buildengine
这不再需要...您现在可以将 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”。
【讨论】:
@csharptest.net 的答案很好,但在 DotNetCore 上不起作用。我会将此添加为评论,但我没有足够的声誉。
在 DotNetCore 上你必须更新:
其他一切都应该相同:
<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>
【讨论】:
有一种非常简单的方法来替换文件中的字符串:
<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] 已包含在列表中。
【讨论】:
Encoding="Unicode"!例如,app.config 的默认值为 Encoding="UTF-8",它不适用于 Unicode!
编辑:这个答案已经过时了。使用下面的解决方案...
使用 ReadLinesFromFile 任务从 xy.xml 文件中获取替换字符串。 Check this
然后使用 xy.xml 中的值作为 FileUpdate 任务的替换字符串。 Check this
把它们放在一起;)
【讨论】:
您可以使用 MSBuild 社区任务中的任务 FileUpdate,如文章中所述 http://geekswithblogs.net/mnf/archive/2009/07/03/msbuild-task-to-replace-content-in-text-files.aspx
【讨论】:
如果您不想使用第三方(社区)二进制文件,也不想将代码嵌入到您的 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'))" />
【讨论】:
我针对位于 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 - 我正在为 unix 构建使用 perl 替代命令执行 exec。
【讨论】:
James 的更新回答
<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>
【讨论】: