Как заменить строку в файле с помощью msbuild?
Я хочу заменить строку, например "how ru" в файле test.xml, строкой "у меня все хорошо" в другом файле xy.xml.using регулярного выражения в сборке ms.
т.е. я должен прочитать строку из одного файла (xy.xml) и заменить ее в другом файле test.xml. поэтому, пожалуйста, предоставьте необходимые шаги для решения этой проблемы на примере
4 ответа
РЕДАКТИРОВАТЬ: Этот ответ устарел. Используйте решение ниже...
Используйте задачу ReadLinesFromFile, чтобы получить строку замены из файла xy.xml. Проверь это
Затем используйте значение из xy.xml в качестве строки замены для задачи FileUpdate. Проверь это
И собрать все это вместе;)
Это больше не требуется... теперь вы можете внедрить 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>
Приведенный выше пример заменяет "$version$" на "1.0.0.2" в "File.exe.config", расположенном в выходном каталоге.
Есть очень простой способ просто заменить строку в файле:
<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]
включен в список.
Ответ от @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>
Вы можете использовать задачу FileUpdate из Задач сообщества MSBuild, как описано в статье 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'))" />
Обновленный ответ от Джеймса
<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>
Я запустил обе замены для одного и того же файла, который находится на диске 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 не добавляет бомбу; однако 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 - я делал exec с командами perl subtitute для сборок unix.