前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >在 MSBuild 编译过程中操作文件和文件夹(检查存在/创建文件夹/读写文件/移动文件/复制文件/删除文件夹)

在 MSBuild 编译过程中操作文件和文件夹(检查存在/创建文件夹/读写文件/移动文件/复制文件/删除文件夹)

作者头像
walterlv
发布于 2023-10-22 02:48:37
发布于 2023-10-22 02:48:37
6130
举报

本文整理 MSBuild 在编译过程中对文件和文件夹处理的各种自带的编译任务(Task)。


Exists 检查文件存在

使用 Exists 可以判断一个文件或者文件夹是否存在。注意无论是文件还是文件夹,只要给定的路径存在就返回 true。可以作为 MSBuild 属性、项和编译任务的执行条件。

1 2 3

<PropertyGroup Condition=" Exists( '$(MSBuildThisFileDirectory)..\build\build.xml' ) "> <_WalterlvPackingDirectory>$(MSBuildThisFileDirectory)..\bin\$(Configuration)\</_WalterlvPackingDirectory> </PropertyGroup>

MakeDir 创建文件夹

下面的例子演示创建一个文件夹:

1 2 3

<Target Name="_WalterlvCreateDirectoryForPacking"> <MakeDir Directories="$(MSBuildThisFileDirectory)..\bin\$(Configuration)\" /> </Target>

下面是使用到 MakeDir 全部属性的例子,将已经成功创建的文件夹提取出来。

1 2 3 4 5

<Target Name="_WalterlvCreateDirectoryForPacking"> <MakeDir Directories="$(MSBuildThisFileDirectory)..\bin\$(Configuration)\"> <Output TaskParameter="DirectoriesCreated" PropertyName="CreatedPackingDirectory" /> </MakeDir> </Target>

Move 移动文件

下面的例子是将输出文件移动到一个专门的目录中,移动后,所有的文件将平级地在输出文件夹中(即所有的子文件夹中的文件也都被移动到同一层目录中了)。

1 2 3 4 5 6 7 8 9 10 11 12

<PropertyGroup> <_WalterlvPackingDirectory>$(MSBuildThisFileDirectory)..\bin\$(Configuration)\</_WalterlvPackingDirectory> </PropertyGroup> <Target Name="_WalterlvMoveFilesForPacking"> <ItemGroup> <_WalterlvToMoveFile Include="$(OutputPath)**" /> </ItemGroup> <Move SourceFiles="@(_WalterlvToMoveFile)" DestinationFolder="$(_WalterlvPackingDirectory)" SkipUnchangedFiles="True" /> </Target>

你可以通过下面的例子了解到 Move 的其他大多数属性及其用法:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

<PropertyGroup> <_WalterlvPackingDirectory>$(MSBuildThisFileDirectory)..\bin\$(Configuration)\</_WalterlvPackingDirectory> </PropertyGroup> <Target Name="_WalterlvMoveFilesForPacking"> <ItemGroup> <_WalterlvToMoveFile Include="$(OutputPath)**" /> <_WalterlvTargetFile Include="$(_WalterlvPackingDirectory)\%(_WalterlvToMoveFile.RecursiveDir)" /> </ItemGroup> <Move SourceFiles="@(_WalterlvToMoveFile)" DestinationFiles="$(_WalterlvTargetFile)" OverwriteReadOnlyFiles="True"> <Output TaskParameter="MovedFiles" PropertyName="MovedOutputFiles" /> </Copy> </Target>

这段代码除了没有使用 DestinationFolder 之外,使用到了所有 Move 能用的属性:

  • 将所有的 _WalterlvToCopyFile 一对一地复制到 _WalterlvTargetFile 指定的路径上。
  • 即便目标文件是只读的,也会覆盖。

Copy 复制文件

下面的例子是将输出文件拷贝到一个专门的目录中,保留原来所有文件之间的目录结构,并且如果文件没有改变则跳过。

1 2 3 4 5 6 7 8 9 10 11 12

<PropertyGroup> <_WalterlvPackingDirectory>$(MSBuildThisFileDirectory)..\bin\$(Configuration)\</_WalterlvPackingDirectory> </PropertyGroup> <Target Name="_WalterlvCopyFilesForPacking"> <ItemGroup> <_WalterlvToCopyFile Include="$(OutputPath)**" /> </ItemGroup> <Copy SourceFiles="@(_WalterlvToCopyFile)" DestinationFolder="$(_WalterlvPackingDirectory)\%(RecursiveDir)" SkipUnchangedFiles="True" /> </Target>

如果你希望复制后所有的文件都在同一级文件夹中,不再有子文件夹,那么去掉 \%(RecursiveDir)

你可以通过下面的例子了解到 Copy 的其他大多数属性及其用法:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

<PropertyGroup> <_WalterlvPackingDirectory>$(MSBuildThisFileDirectory)..\bin\$(Configuration)\</_WalterlvPackingDirectory> </PropertyGroup> <Target Name="_WalterlvCopyFilesForPacking"> <ItemGroup> <_WalterlvToCopyFile Include="$(OutputPath)**" /> <_WalterlvTargetFile Include="$(_WalterlvPackingDirectory)\%(_WalterlvToCopyFile.RecursiveDir)" /> </ItemGroup> <Copy SourceFiles="@(_WalterlvToCopyFile)" DestinationFiles="@(_WalterlvTargetFile)" OverwriteReadOnlyFiles="True" Retries="10" RetryDelayMilliseconds="10" SkipUnchangedFiles="True" UseHardlinksIfPossible="True"> <Output TaskParameter="CopiedFiles" PropertyName="CopiedOutputFiles" /> </Copy> </Target>

这段代码除了没有使用 DestinationFolder 之外,使用到了所有 Copy 能用的属性:

  • 将所有的 _WalterlvToCopyFile 一对一地复制到 _WalterlvTargetFile 指定的路径上。
  • 即便目标文件是只读的,也会覆盖。
  • 如果复制失败,则重试 10 次,每次等待 10 毫秒
  • 如果文件没有改变,则跳过复制
  • 如果目标文件系统支持硬连接,则使用硬连接来提升性能

Delete 删除文件

下面这个例子是删除输出目录下的所有的 pdb 文件(适合 release 下发布软件)。

1 2 3

<Target Name="_WalterlvDeleteFiles"> <Delete Files="$(OutputPath)*.pdb" /> </Target>

也可以把此操作已经删除的文件列表拿出来。使用全部属性的 Delete 的例子:

1 2 3 4 5 6

<Target Name="_WalterlvDeleteFiles"> <Delete Files="$(OutputPath)*.pdb" TreatErrorsAsWarnings="True"> <Output TaskParameter="DeletedFiles" PropertyName="DeletedPdbFiles" /> </Delete> </Target>

ReadLinesFromFile 读取文件

在编译期间,可以从文件中读出文件的每一行:

1 2 3 4 5 6 7 8 9

<PropertyGroup> <_WalterlvToWriteFile>$(OutputPath)walterlv.md</_WalterlvToWriteFile> </PropertyGroup> <Target Name="_WalterlvReadFilesToLines"> <ReadLinesFromFile File="$(_WalterlvToWriteFile)"> <Output TaskParameter="Lines" PropertyName="TheLinesThatRead" /> </ReadLinesFromFile> </Target>

WriteLinesToFile 写入文件

可以在编译期间,将一些信息写到文件中以便后续编译的时候使用,甚至将代码写到文件中以便动态生成代码。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

<PropertyGroup> <_WalterlvBlogSite>https://blog.walterlv.com</_WalterlvBlogSite> <_WalterlvToWriteFile>$(OutputPath)walterlv.md</_WalterlvToWriteFile> </PropertyGroup> <ItemGroup> <_WalterlvToWriteLine Include="This is the first line" /> <_WalterlvToWriteLine Include="This is the second line" /> <_WalterlvToWriteLine Include="My blog site is: $(_WalterlvBlogSite)" /> </ItemGroup> <Target Name="_WalterlvWriteFilesForPacking"> <WriteLinesToFile File="$(_WalterlvToWriteFile)" Lines="@(_WalterlvToWriteLine)" /> </Target>

▲ 注意,默认写入文件是不会覆盖的,会将内容补充到原来文件的后面。

1 2 3 4 5 6 7

<Target Name="_WalterlvWriteFilesForPacking"> <WriteLinesToFile File="$(_WalterlvToWriteFile)" Lines="@(_WalterlvToWriteLine)" Overwrite="True" Encoding="Unicode" WriteOnlyWhenDifferent="True" /> </Target>

RemoveDir 删除文件夹

在编写编译命令的时候,可能会涉及到清理资源。或者为了避免无关文件的影响,在编译之前删除我们的工作目录。

1 2 3

<Target Name="_WalterlvRemoveDirectoryForPacking"> <RemoveDir Directories="$(MSBuildThisFileDirectory)..\bin\$(Configuration)\" /> </Target>

下面是使用到 MakeDir 全部属性的例子,将已经成功创建的文件夹提取出来。

1 2 3 4 5

<Target Name="_WalterlvRemoveDirectoryForPacking"> <RemoveDir Directories="$(MSBuildThisFileDirectory)..\bin\$(Configuration)\"> <Output TaskParameter="RemovedDirectories" PropertyName="RemovedPackingDirectory" /> </RemoveDir> </Target>

本文会经常更新,请阅读原文: https://blog.walterlv.com/post/msbuild-file-and-directory-operations.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名 吕毅 (包含链接: https://blog.walterlv.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系 ([email protected])

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019-12-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
如何最快速地将旧的 NuGet 包 (2.x, packages.config) 升级成新的 NuGet 包 (4.x, PackageReference)
发布于 2018-05-13 09:07 更新于 2018-06-29 08:52
walterlv
2018/09/18
7720
将 .NET Core 项目打一个最简单的 NuGet 源码包,安装此包就像直接把源码放进项目一样
2018-06-20 01:22
walterlv
2018/09/18
1.1K0
将 .NET Core 项目打一个最简单的 NuGet 源码包,安装此包就像直接把源码放进项目一样
Roslyn 如何使用 MSBuild MakeDir 创建文件夹
在 MSBuild 的 Task 内置任务里面,可以使用 MakeDir task 进行创建文件夹,简单的使用方法如下
林德熙
2020/11/19
1.1K0
Roslyn 打包 NuGet 包 BuildTransitive 文件夹用于穿透依赖传递拷贝文件
默认的 PackageReference 可以实现传递依赖,传递依赖的含义是是假定 B 项目安装了 A 库,而 C 项目依赖 B 项目,那么 C 项目将会自然拿到 A 库的 DLL 引用。但默认的 NuGet 包的构建指导文件 targets 命令是不会在传递执行的,也就是如上的 C 项目将不会执行 B 项目安装的 A 库里面的 target 内容 有一些项目需要拷贝自定义文件,例如拷贝图片或者一些 Native 的 DLL 等资源。如 WPF 框架需要拷贝 PenIME 等资源。如果只是在最底层的项目安装了库,那为了让可执行文件项目也输出库的资源,就需要在可执行项目上也安装库。以上的方法的不足在于安装复杂,也许会忘记安装 本文告诉大家一个解决方法是通过在制作库的时候,加上 BuildTransitive 文件夹,在此文件夹内添加构建指导文件,此时这个构建指导文件 targets 文件里面的命令将会在传递中执行,也就是说只需要在底层的项目安装即可,不需要在可执行项目上也安装库
林德熙
2021/12/24
7920
MSBuild入门
MSBuild是什么? MSBuild全称(Microsoft Build Engine),是用于构建应用程序的平台。您可能不知道它,但是如果您在使用VS做开发,那么一定时时刻刻在使用它。因为是它在背后为你管理生成你的项目文件。当新建一个项目时,注意下项目文件夹中的*.*proj文件就是为MSBuild提供的,这是个文本文件,基于XML格式,里面包含有项目所包含的文件,生成配置,输出配置等信息。当把一个文件或者图片等添加到项目中,就会在这里添加一个描述,反之则删除一个描述信息;在项目属性页所做的配置也会在这
blackheart
2018/01/19
1.7K0
MSBuild入门
Roslyn 让 VisualStudio 急速调试底层库方法
我有一个很大的项目,这个项目里面包含了很多小的底层库。有一天我发现了某个底层库可能有小伙伴挖了一个坑,我期望调试这个底层库,但是我一点都不想编译整个大项目,因为底层库被太多的项目应用,一点点修改都需要编译很久。本文提供一个简单的方法让大家可以通过修改项目文件,让 VisualStudio 可以急速调试底层库,每次更改底层库只需要重新编译底层库就可以
林德熙
2019/06/15
4980
无需安装 VS2019,在 Visual Studio 2022 中编译 .NET Framework 4.5/4/3.5 这样的古老框架
Visual Studio 2022 已正式发布!着急升级的小伙伴兴致勃勃地升级并卸载了原来的 Visual Studio 2019 后,发现自己的几个库项目竟然无法编译通过了。究其原因,是因为我的一些库依旧在支持古老的 .NET Framework 4.5 框架,而 Visual Studio 2022 不再附带如此古老的目标包了。
walterlv
2023/10/23
2.1K0
无需安装 VS2019,在 Visual Studio 2022 中编译 .NET Framework 4.5/4/3.5 这样的古老框架
使用 MSBuild Target 复制文件的时候如何保持文件夹结构不变
使用 MSBuild 中的 Copy 这个编译目标可以在 .NET 项目编译期间复制一些文件。不过使用默认的参数复制的时候文件夹结构会丢失,所有的文件会保留在同一级文件夹下。
walterlv
2023/10/22
4810
使用 MSBuild Target 复制文件的时候如何保持文件夹结构不变
项目文件中的已知属性(知道了这些,就不会随便在 csproj 中写死常量啦)
发布于 2018-04-12 13:03 更新于 2018-08-29 01:36
walterlv
2018/09/18
1.7K0
MSBuild的简单介绍与使用
MSBuild 是 Microsoft 和 Visual Studio的生成系统。它不仅仅是一个构造工具,应该称之为拥有相当强大扩展能力的自动化平台。MSBuild平台的主要涉及到三部分:执行引擎、构造工程、任务。其中最核心的就是执行引擎,它包括定义构造工程的规范,解释构造工程,执行“构造动作”;构造工程是用来描述构造任务的,大多数情况下我们使用MSBuild就是遵循规范,编写一个构造工程;MSBuild引擎执行的每一个“构造动作”就是通过任务实现的,任务就是MSBuild的扩展机制,通过编写新的任务就能够
张善友
2018/01/29
1.4K0
msbuild 修改 VisualStudio 文件复制到输出目录的路径
在默认的 VisualStudio 可以右击任意的文件,让这个文件在编译时复制到输出目录,但是这个选项将会在复制到输出目录时带上这个文件所在 VisualStudio 的文件夹结构。本文告诉大家几个方法让 VisualStudio 的文件可以在编译时输出到自定义的任意路径
林德熙
2020/02/17
3.8K1
从零开始构建MSBuild C#项目文件
首先打开MSBuild命令提示符,然后切换到你想要创建项目的文件夹,例如我的文档或者桌面。然后,输入md HelloWorld创建一个名为HelloWorld的文件夹。然后输入cd HelloWorld切换到这个文件夹。为简便起见,下面所说的命令提示符,都是指这里的MSBuild命令提示符。
乐百川
2022/05/05
1.3K0
.NET / MSBuild 扩展编译时什么时候用 BeforeTargets / AfterTargets 什么时候用 DependsOnTargets?
在为 .NET 项目扩展 MSBuild 编译而编写编译目标(Target)时,我们会遇到用于扩展编译目标用的属性 BeforeTargets AfterTargets 和 DependsOnTargets。
walterlv
2023/10/22
5340
从零开始制作 NuGet 源代码包(全面支持 .NET Core / .NET Framework / WPF 项目)
默认情况下,我们打包 NuGet 包时,目标项目安装我们的 NuGet 包会引用我们生成的库文件(dll)。除此之外,我们也可以专门做 NuGet 工具包,还可以做 NuGet 源代码包。然而做源代码包可能是其中最困难的一种了,目标项目安装完后,这些源码将直接随目标项目一起编译。
walterlv
2023/10/23
1.3K0
从零开始制作 NuGet 源代码包(全面支持 .NET Core / .NET Framework / WPF 项目)
MSBuild 中的特殊字符($ @ % 等):含义、用法以及转义
在 MSBuild 中有一些特殊字符,如 $ @ % ' 等,本文介绍他们的含义,如何使用他们,以及你真的需要这些字符的时候如何编写他们。
walterlv
2023/10/22
6920
Roslyn 使用 WriteLinesToFile 解决参数过长无法传入
在写 Roslyn 的时候,经常需要辅助编译的工具,而这些工具需要传入一些参数,在项目很大的时候,会发现自己传入的参数比微软限制控制台可以传入的参数大很多,这时就无法传入了参数。 本文告诉大家如何使用 WriteLinesToFile 先把参数写入文件,通过文件的方式传输参数
林德熙
2018/09/19
6820
Roslyn 使用 WriteLinesToFile 解决参数过长无法传入
Roslyn 开发 NuGet 包的 Task 编译可能遇到的问题
在写 msbuild 脚本的时候,或修改项目文件的时候,将会使用到很多的微软提供的 Task 命令。在需要复杂的编译的时候,可以通过自己定义一个任务用来定义编译
林德熙
2022/08/04
5810
理解 C# 项目 csproj 文件格式的本质和编译流程
发布于 2018-05-10 00:13 更新于 2018-08-12 08:11
walterlv
2018/09/18
2.8K0
理解 C# 项目 csproj 文件格式的本质和编译流程
如何创建一个基于 MSBuild Task 的跨平台的 NuGet 工具包
发布于 2018-05-11 16:04 更新于 2018-09-01 00:07
walterlv
2018/09/18
1.5K0
如何创建一个基于 MSBuild Task 的跨平台的 NuGet 工具包
Roslyn 打包自定义的文件到 NuGet 包
在使用 sdk 格式的项目文件支持快速进行打包,但使用这个方式打包的时候将默认只带程序集输出文件,而没有带依赖的文件。本文告诉大家如何在打包的时候加上需要放在包里面的文件
林德熙
2020/04/08
9740
推荐阅读
相关推荐
如何最快速地将旧的 NuGet 包 (2.x, packages.config) 升级成新的 NuGet 包 (4.x, PackageReference)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档