创建和删除目录
最基本的文件系统操作之一就是创建目录或文件夹。做这项工作的任务名为 mkdir,毫不奇怪,它非常类似于具有相同名称的 Windows 和 UNIX/Linux 命令。
<mkdir dir="archive/metals/zinc"/>
首先要注意 / 被用作目录分隔符,这是 UNIX 和 Linux 的惯例。您可能认为这不是很平台无关的,但是 Ant 知道如何处理它,并针对它运行所在的平台做恰当的事情,这与我们在前面定义基于位置的属性时所看到的方式相同。我们能够同样容易地使用 \,而不管平台是什么 —— Ant 能够处理任一种形式,甚至能够处理两种形式的混合。
mkdir 任务的另一个有用特性是它的如下能力:在父目录还不存在时创建它们。考虑一下上面的清单,设想 archive 目录存在,但是 metals 目录不存在。如果使用底层平台的 mkdir 命令,您需要首先显式地创建 metals 目录,然后第二次调用 mkdir 命令来创建 zinc 目录。但是 Ant 任务比这更加智能,它能够一次性创建这两个目录。类似地,如果目标目录已经存在,mkdir 任务不会发出错误消息,而只是假设它的工作已经完成,从而什么也不做。
删除目录同样也很容易:
<delete dir="archive/metals/zinc"/>
这将删除指定的目录连同它包含的所有文件以及子目录。使用 file 属性而不是 dir 属性可以指定要删除的单个文件。
复制和移动文件及目录
在 Ant 中制作文件的一份拷贝很简单。例如:
<copy file="src/Test.java" tofile="src/TestCopy.java"/>
您还可以使用 move 来执行重命名操作而不是拷贝文件:
<move file="src/Test.java" tofile="src/TestCopy.java"/>
另一个常用的文件系统操作是将文件复制或移动到另一个目录。做这项工作的 Ant 语法同样也很简单:
<copy file="src/Test.java" todir="archive"/>
<move file="src/Test.java" todir="archive"/>
默认情况下,Ant 仅输出它执行的移动和复制操作的摘要,包括诸如已移动或复制的文件的数量等信息。如果想看到更详细的信息,包括涉及的文件名称等,您可以将 verbose 属性设置为true。
创建和解压缩 zip 及 tar 文件
在前一节中,我们看到了如何创建 JAR 文件。创建其他归档文件的过程几乎完全相同。下面是创建zip 文件的 Ant 任务:
<zip destfile="output.zip" basedir="output"/>
相同的语法也可用于创建 tar 文件。 还可以使用 GZip 和 BZip 任务来压缩文件。例如:
<gzip src="output.tar" zipfile="output.tar.gz"/>
解压缩和提取文件同样也很简单:
<unzip src="output.tar.gz" dest="extractDir"/>
还可以包括 overwrite 属性来控制覆盖行为。默认设置是覆盖与正在被提取的归档文件中的条目相匹配的所有现有文件。相关的任务名称是 untar、unjar、gunzip 和 bunzip2。
替换文件中的标记
我们将在本节考察的最后一个文件系统操作是 replace 任务,它执行文件中的查找和替换操作。
token 属性指定要查找的字符串,value 属性指定一个新的字符串,查找到的标记字符串的所有实例都被替换为这个新的字符串。例如:
<replace file="input.txt" token="old" value="new"/>
替换操作将在文件本身之内的适当位置进行。为了提供更详细的输出,可把 summary 属性设置为true。这将导致该任务输出找到和替换的标记字符串实例的数目。
模式匹配
在前面考察文件系统任务时,我们仅使用了单独地命名的文件和目录。然而,一次对一组文件执行那些操作经常是有用的 —— 例如对给定目录中以 .java 结尾的所有文件执行操作。正如等价的DOS 和 UNIX 命令提供了这样的功能一样,Ant 也提供了这样的功能。这是使用通配符字符来完成的:*,它匹配零个或多个字符;以及 ?,它仅匹配一个字符。因而匹配以 .java 结尾的所有文件的模式不过就是 *.java。
也可以对目录执行模式匹配。例如,模式 src*/*.java 将匹配带 src 前缀的任何目录中的所有Java 文件。 还有另一种模式结构:**,它匹配任意数量的目录。例如,模式 **/*.java 将匹配当前目录结构下的所有 Java 文件。
您能够以相当一致的方式对文件系统任务使用模式,比如嵌套的 fileset 元素。先前,我们使用这个任务来复制单个文件:
<copy file="src/Test.java" todir="archive"/>
如果我们想要使用一个模式,可以将 file 属性替换为一个 fileset 元素,如下所示:
<copy todir="archive">
<fileset dir="src">
<include name="*.java"/>
</fileset>
</copy>
fileset 默认情况下包含指定 src 目录下的所有文件,因此为了仅选择 Java 文件,我们对模式使用一个 include 元素。类似地,我们可以对另一个模式添加一个 exclude 元素,从而潜在地排除include 指定的匹配项。甚至可以指定多个include 和 exclude 元素;这样将得到一组文件和目录,它们包含 include 模式的所有匹配项的并集,但排除了 exclude 模式的所有匹配项。
注意还有一个通常很有用的文件集特性,但是对于没有意识到它的人来说,这个特性偶尔会产生混淆。这个特性称为 默认排除:即自动从文件集内容中排除的内置模式列表。该列表包括与名为 CVS的目录相匹配的条目,以及以 ~ 字符结尾的文件,它们可能是备份文件。您通常不想在文件系统操作中包括这类文件和目录,因此排除这些文件是默认行为。然而,如果确实想无例外地选择 所有文件和目录,可以将文件集的 defaultexcludes 属性设置为 no。
使用选择器
正如我们已经看到的,文件集用于指定一组文件,并且这个组的内容可以使用 include 和 exclude模式来指定。也可以结合称为 选择器 的特殊元素使用include 和 exclude 来选择文件。下面是对 Ant 可用的核心选择器的列表:
· size:这个选择器用于根据文件的字节大小选择文件(除非使用 units 属性来指定了不同的单位)。when 属性用于设置比较的性质(less、more 或者 equal),value 属性定义每个文件将与之作比较的目标大小。
· contains:只有包含给定文本字符串(由text 属性指定)的文件才匹配这个选择器。默认情况下,查找操作是大小写敏感的;添加casesensitive="no" 可以改变默认设置。
· filename:name 属性指定文件名要与之匹配的模式。它本质上与 include 元素相同,以及与指定了negate="yes" 时的 exclude 元素相同。
· present:从当前目录结构中选择如下文件:它们与指定的 targetdir 目录中的文件具有相同的名称和相对目录结构。
· depend:这个选择器与 present 选择器具有相同的效果,只不过匹配的文件被限制到相对于 targetdir 位置中的对应文件来说,最近已修改过的那些文件。
· date:这个选择器基于其最后修改日期选择文件。when 属性指定作比较的性质是 before、after 还是 equal,datetime 属性指定与之作比较的日期和时间,这个日期和时间具有给定的固定格式 MM/DD/YYYY HH:MM AM_or_PM。注意 Windows 平台上有一个内置的 2 秒偏移,以允许底层文件系统的不精确性 —— 这可能导致匹配的文件数量超过预期。允许的回旋时间量可以使用 granularity 属性来更改(以毫秒为单位来指定)。
· depth:这个选择器检查每个文件的目录结构层次数目。min 和/或 max 属性用于选择具有想要的目录层次数目的的文件。
还可以通过在一个选择器 容器 内嵌套一个或多个选择器来组合选择器。 最常用的选择器容器 and仅选择它包含的所有选择器都选择了的文件。其他选择其容器包括or、not、none 和 majority。
下面是一个文件集的例子,它仅选择那些大于 512 字节并且包含字符串“hello”的文件。
<fileset dir="dir">
<and>
<contains text="hello"/>
<size value="512" when="more"/>
</and>
</fileset>
将生成文件链接起来
有两种生成大型项目的不同方法。一种是让一个单一的生成文件做所有事情;另一种是让高级别的生成文件调用其它生成文件以执行特定任务,从而将生成过程划分为许多较小的部分。
使用 ant 任务来从一个 Ant 生成中调用另一个 Ant 生成是很容易的。在简单的情况下,您可以使用 antfile 属性,仅指定那些要使用的生成文件,Ant 将生成该生成文件中的默认目标。例如:
<ant antfile="sub-build.xml"/>
在父生成文件中定义的任何属性默认将传递给子生成文件,虽然这可以通过指定
inheritAll="false"来避免。通过使用 property 元素来传入显式的属性也是可以做到的 —— 即使将 inheritAll 设置为 false,这些属性也仍然适用于子生成文件。这个功能很适合用于给子生成文件传入参数。
让我们来考虑一个例子。下面是我们想要调用的一个生成文件:
<?xml version="1.0"?>
<project default="showMessage">
<target name="showMessage">
<echo message="Message=${message}"/>
</target>
</project>
(我们在前面还没有遇到过 echo 任务 —— 它简单地输出给定的消息。)
下面是调用第一个生成文件的第二生成文件,它还给第一个生成文件传入 message 属性:
<?xml version="1.0"?>
<project default="callSub">
<target name="callSub">
<ant antfile="sub.xml" target="showMessage" inheritAll="false">
<property name="message" value="Hello from parent build"/>
</ant>
</target>
</project>
运行第二个生成文件所得到的输出如下:
Buildfile: build.xml
callSub:
showMessage:
[echo] Message=Hello from parent build
BUILD SUCCESSFUL
Total time: 0 seconds