Ant:如何对目录中的每个文件执行命令?


97

我想从Ant构建文件为目录中的每个文件执行命令。
我正在寻找与平台无关的解决方案。

我该怎么做呢?

当然,我可以用某种脚本语言编写脚本,但这会给项目增加更多的依赖性。

Answers:


61

简短答案

<foreach>与嵌套一起使用<FileSet>

Foreach需要ant-contrib

更新了最近的ant-contrib示例:

<target name="foo">
  <foreach target="bar" param="theFile">
    <fileset dir="${server.src}" casesensitive="yes">
      <include name="**/*.java"/>
      <exclude name="**/*Test*"/>
    </fileset>
  </foreach>
</target>

<target name="bar">
  <echo message="${theFile}"/>
</target>

这将用$ {theFile}调用目标“栏”,生成当前文件。


所以,我必须包括一些东西吗?还是我需要一些外部ant lib?我收到“问题:无法创建任务或键入foreach”。如果我理解正确,这意味着foreach是未知关键字。
ivan_ivanovich_ivanoff 2009年

4
la脚...这是一项Ant-Contrib任务。因此,您必须安装一些东西。看到这里:ant-contrib.sourceforge.net
blak3r

3
该示例来自ant-contrib中的foreach。在ant.1045680.n5.nabble.com/Using-foreach-td1354624.html中有一个很好的示例,我将更新该示例以使其工作。
肖恩

2
嵌套文件集元素已被弃用,请改用嵌套路径
dude

@dude使用<foreach> <path> <fileset> <include name =“ *。jar” /> </ fileset> </ path> </ foreach>
Sharat

88

使用<apply>任务

它对每个文件执行一次命令。通过文件集或任何其他资源指定文件。<apply>是内置的;无需其他依赖项;无需自定义任务实现。

也可以只运行一次命令,一次性将所有文件作为参数附加。使用parallel属性来切换行为。

对不起,迟到了一年。


4
好吧,我只是在2011年发现它很有用,所以还是要多谢!
迈克尔·德拉·比塔

7
<apply>的问题在于它仅执行外部系统命令。它不会直接做任何事情。不清楚那是不是原始问题。基本上,对于两种不同的情况,必须使用<apply>或<foreach> ...这完全是愚蠢的。
Archie

27

塔西洛·霍恩Tassilo Horn)提出了一种无需蚂蚁控制的方法(最初的目标是

基本上,由于没有<java>扩展(还可以吗?),与<apply>扩展<exec>的方式相同,他建议使用<apply>(当然也可以在命令行中运行Java程序)

这里有一些例子:

  <apply executable="java"> 
    <arg value="-cp"/> 
    <arg pathref="classpath"/> 
    <arg value="-f"/> 
    <srcfile/> 
    <arg line="-o ${output.dir}"/> 

    <fileset dir="${input.dir}" includes="*.txt"/> 
  </apply> 

2
尽管在原始问题中显然需
要这样做,

18

这是使用javascript和ant scriptdef任务执行此操作的方法,由于scriptdef是一个核心的ant任务,因此您不需要ant-contrib即可使此代码正常工作。

<scriptdef name="bzip2-files" language="javascript">
<element name="fileset" type="fileset"/>
<![CDATA[
  importClass(java.io.File);
  filesets = elements.get("fileset");

  for (i = 0; i < filesets.size(); ++i) {
    fileset = filesets.get(i);
    scanner = fileset.getDirectoryScanner(project);
    scanner.scan();
    files = scanner.getIncludedFiles();
    for( j=0; j < files.length; j++) {

        var basedir  = fileset.getDir(project);
        var filename = files[j];
        var src = new File(basedir, filename);
        var dest= new File(basedir, filename + ".bz2");

        bzip2 = self.project.createTask("bzip2");        
        bzip2.setSrc( src);
        bzip2.setDestfile(dest ); 
        bzip2.execute();
    }
  }
]]>
</scriptdef>

<bzip2-files>
    <fileset id="test" dir="upstream/classpath/jars/development">
            <include name="**/*.jar" />
    </fileset>
</bzip2-files>

project在上面的示例中引用了一个变量,但没有提供任何先验定义。最好能有代表或澄清的内容。编辑:n / m,发现这project是访问当前项目的预定义变量。我对此表示怀疑,但不确定。
乔恩·L.

1
对于那些在JDK8或更高版本中尝试此操作的人,请记住,如果JRE加载的ScriptEngine是rhino(对于大多数JDK 6和7都是如此),则“ importClass”有效,而在Nashorn(从8开始)中,您可以使用向后兼容的“ File = java.io.File”或更新的但不向后兼容的Java.type。正如我今天所经历的那样,在运行scriptdef遇到问题时,Ant似乎无声地失败了。
Matteo Steccolini

15

ant-contrib是邪恶的;编写一个自定义的ant任务。

ant-contrib是邪恶的,因为它试图将ant从声明式样式转换为命令式样式。但是xml是一种废话编程语言。

相比之下,自定义ant任务允许您使用真实的IDE用真实的语言(Java)编写代码,您可以在其中编写单元测试以确保您具有所需的行为,然后在构建脚本中明确声明以下内容:您想要的行为。

仅当您关心编写可维护的ant脚本时,此问题才重要。如果您根本不关心可维护性,请执行任何工作。:)

t


2
没错,我应该用Java编写一个自定义ant任务;)
ivan_ivanovich_ivanoff 2009年

4
ant-contrib确实是邪恶的。现在,我处于一个大型蚂蚁构建项目的中间,该项目大量使用了if / then / else和antcalls,它的确令人恐惧。整个过程看起来像一个转换后的批处理/ shell脚本,而大量使用ant-contrib完全关闭了ant所做的所有依赖项。如果要保持设置清洁,请构建自己的任务。:-/
cringe 2010年

2
@cringe,我不同意。像其他任何东西一样,您必须知道何时使用ant-contrib最符合您的利益。远离if和var之类的东西,并使用ant-contrib来避免重新发明轮子。
尼尔2012年

1
首先,它应该是一种脚本语言,因此必须而不是声明性。所以是谁是邪恶的IMO
mvmn 2012年

也许ant-contrib是先天的邪恶,但是可以这么说,black3r对它的使用似乎是合理且蚂蚁般的。
ssimm '17

7

我知道这篇文章真的很老,但是现在经过一段时间和蚂蚁版本,有一种方法可以使用基本的蚂蚁功能,我想我应该分享一下。

这是通过调用嵌套任务的递归宏定义完成的(甚至可以调用其他宏)。唯一的约定是使用固定的变量名称(此处为元素)。

<project name="iteration-test" default="execute" xmlns="antlib:org.apache.tools.ant" xmlns:if="ant:if" xmlns:unless="ant:unless">

    <macrodef name="iterate">
        <attribute name="list" />
        <element name="call" implicit="yes" />
        <sequential>
            <local name="element" />
            <local name="tail" />
            <local name="hasMoreElements" />
            <!-- unless to not get a error on empty lists -->
            <loadresource property="element" unless:blank="@{list}" >
                <concat>@{list}</concat>
                <filterchain>
                    <replaceregex pattern="([^;]*).*" replace="\1" />
                </filterchain>
            </loadresource>
            <!-- call the tasks that handle the element -->
            <call />

            <!-- recursion -->
            <condition property="hasMoreElements">
                <contains string="@{list}" substring=";" />
            </condition>

            <loadresource property="tail" if:true="${hasMoreElements}">
                <concat>@{list}</concat>
                <filterchain>
                    <replaceregex pattern="[^;]*;(.*)" replace="\1" />
                </filterchain>
            </loadresource>

            <iterate list="${tail}" if:true="${hasMoreElements}">
                <call />
            </iterate>
        </sequential>
    </macrodef>

    <target name="execute">
        <fileset id="artifacts.fs" dir="build/lib">
            <include name="*.jar" />
            <include name="*.war" />
        </fileset>

        <pathconvert refid="artifacts.fs" property="artifacts.str" />

        <echo message="$${artifacts.str}: ${artifacts.str}" />
        <!-- unless is required for empty lists to not call the enclosed tasks -->
        <iterate list="${artifacts.str}" unless:blank="${artifacts.str}">
            <echo message="I see:" />
            <echo message="${element}" />
        </iterate>
        <!-- local variable is now empty -->
        <echo message="${element}" />
    </target>
</project>

需要的主要功能包括:

我没有设法使定界符可变,但是这可能不是主要的缺点。


不幸的是,这用尽了内存并且很快就崩溃了。
David St Denis 2014年

是的,它可能会耗尽您的堆栈,具体取决于您处理的文件数量。我用大约66个文件(没有增加的“内存”选项)非常成功。但是,如果您无权访问提供了更多功能(例如并行运行)的ant-contrib,这只是一个选择。
dag

这非常有帮助。
DiamondDrake '18

0

您可以使用ant-contrib任务“ for”在由任何分隔符分隔的文件列表上进行迭代,默认分隔符为“,”。

以下是显示此示例文件:

<project name="modify-files" default="main" basedir=".">
    <taskdef resource="net/sf/antcontrib/antlib.xml"/>
    <target name="main">
        <for list="FileA,FileB,FileC,FileD,FileE" param="file">
          <sequential>
            <echo>Updating file: @{file}</echo>
            <!-- Do something with file here -->
          </sequential>
        </for>                         
    </target>
</project>

0

做blak3r建议的操作并像这样定义目标类路径

<taskdef resource="net/sf/antcontrib/antlib.xml">
    <classpath>
        <fileset dir="lib">
          <include name="**/*.jar"/>
        </fileset>
    </classpath>        
</taskdef>

lib是您存储jar的位置

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.