Ant: How to execute a command for each file in directory?

File IoAntBuild ProcessBuild Automation

File Io Problem Overview


I want to execute a command from an Ant buildfile, for each file in a directory.
I am looking for a platform-independent solution.

How do I do this?

Sure, I could write a script in some scripting language, but this would add further dependencies to the project.

File Io Solutions


Solution 1 - File Io

Use the <apply> task.

It executes a command once for each file. Specify the files by means of filesets or any other resource. <apply> is built-in; no additional dependency needed; no custom task implementation needed.

It's also possible to run the command only once, appending all files as arguments in one go. Use the parallel attribute to switch the behaviour.

Sorry for being late a year.

Solution 2 - File Io

Short Answer

Use <foreach> with a nested <FileSet>

Foreach requires ant-contrib.

Updated Example for recent 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>

This will antcall the target "bar" with the ${theFile} resulting in the current file.

Solution 3 - File Io

An approach without ant-contrib is suggested by Tassilo Horn (the original target is here)

Basicly, as there is no extension of <java> (yet?) in the same way that <apply> extends <exec>, he suggests to use <apply> (which can of course also run a java programm in a command line)

Here some examples:

  <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> 

Solution 4 - File Io

Here is way to do this using javascript and the ant scriptdef task, you don't need ant-contrib for this code to work since scriptdef is a core ant task.

<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>

Solution 5 - File Io

ant-contrib is evil; write a custom ant task.

ant-contrib is evil because it tries to convert ant from a declarative style to an imperative style. But xml makes a crap programming language.

By contrast a custom ant task allows you to write in a real language (Java), with a real IDE, where you can write unit tests to make sure you have the behavior you want, and then make a clean declaration in your build script about the behavior you want.

This rant only matters if you care about writing maintainable ant scripts. If you don't care about maintainability by all means do whatever works. :)

Jtf

Solution 6 - File Io

I know this post is realy old but now that some time and ant versions passed there is a way to do this with basic ant features and i thought i should share it.

It's done via a recursive macrodef that calls nested tasks (even other macros may be called). The only convention is to use a fixed variable name (element here).

<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>

The key features needed where:

I didnt manage to make the delimiter variabel, but this may not be a major downside.

Solution 7 - File Io

You can use the ant-contrib task "for" to iterate on the list of files separate by any delimeter, default delimeter is ",".

Following is the sample file which shows this:

<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>
	

Solution 8 - File Io

Do what blak3r suggested and define your targets classpath like so

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

where lib is where you store your jar's

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionivan_ivanovich_ivanoffView Question on Stackoverflow
Solution 1 - File IoAlexView Answer on Stackoverflow
Solution 2 - File Ioblak3rView Answer on Stackoverflow
Solution 3 - File IoJminiView Answer on Stackoverflow
Solution 4 - File IoamsView Answer on Stackoverflow
Solution 5 - File IoJeffrey FredrickView Answer on Stackoverflow
Solution 6 - File IodagView Answer on Stackoverflow
Solution 7 - File IoHemantView Answer on Stackoverflow
Solution 8 - File IobungeeView Answer on Stackoverflow