Ant: Come eseguire un comando per ogni file nella directory?


97

Voglio eseguire un comando da un buildfile Ant, per ogni file in una directory.
Sto cercando una soluzione indipendente dalla piattaforma.

Come faccio a fare questo?

Certo, potrei scrivere uno script in un linguaggio di scripting, ma questo aggiungerebbe ulteriori dipendenze al progetto.

Risposte:


61

Risposta breve

Utilizzare <foreach>con un annidato<FileSet>

Foreach richiede ant-contrib .

Esempio aggiornato per il recente 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>

Questo chiamerà la "barra" di destinazione con $ {theFile} risultando nel file corrente.


Quindi, devo includere qualcosa? O ho bisogno di qualche ant lib esterno? Viene visualizzato il messaggio "Problema: impossibile creare l'attività o digitare foreach" . Se ho capito bene, significa che foreach è una parola chiave sconosciuta.
ivan_ivanovich_ivanoff

4
Ohhh zoppo ... è un compito Ant-Contrib. Quindi, devi installare qualcosa. Vedi qui: ant-contrib.sourceforge.net
blak3r

3
Quell'esempio viene da un foreach prima che fosse incluso in ant-contrib. C'è un buon esempio su ant.1045680.n5.nabble.com/Using-foreach-td1354624.html Aggiornerò l'esempio per funzionare.
Sean

2
L'elemento del set di file nidificato è deprecato, usa invece un percorso nidificato
amico

@dude Usa <foreach> <path> <fileset> <include name = "*. jar" /> </fileset> </path> </foreach>
Sharat

88

Usa l'attività <apply> .

Esegue un comando una volta per ogni file. Specificare i file tramite set di file o qualsiasi altra risorsa. <apply> è integrato; nessuna dipendenza aggiuntiva necessaria; non è necessaria l'implementazione di attività personalizzate.

È anche possibile eseguire il comando una sola volta, aggiungendo tutti i file come argomenti in una volta sola. Usa l'attributo parallelo per cambiare il comportamento.

Scusa per il ritardo di un anno.


4
Beh, l'ho trovato utile nel 2011, quindi grazie comunque!
Michael Della Bitta

7
Il problema con <apply> è che esegue solo comandi di sistema esterni. Non farà niente di strano direttamente. Non è chiaro se fosse questo l'argomento della domanda originale o meno. Fondamentalmente devi usare <apply> o <foreach> per le due diverse situazioni ... il che è totalmente stupido.
Archie

27

Un approccio senza ant-contrib è suggerito da Tassilo Horn (l' obiettivo originale è qui )

Fondamentalmente, poiché non esiste un'estensione di <java> (ancora?) Nello stesso modo in cui <apply> estende <exec> , suggerisce di usare <apply> (che ovviamente può anche eseguire un programma java in una riga di comando)

Alcuni esempi:

  <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
Questo non è molto indipendente dalla piattaforma sebbene fosse chiaramente richiesto nella domanda originale ...
Chucky

18

Ecco il modo per farlo usando javascript e il task ant scriptdef, non hai bisogno di ant-contrib per far funzionare questo codice poiché scriptdef è un compito fondamentale di ant.

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

si projectfa riferimento a una variabile nell'esempio precedente, ma non è disponibile una definizione precedente. Sarebbe bello che fosse rappresentato o chiarito. EDIT: n / m, trovato che projectè una variabile predefinita per accedere al progetto corrente. Lo sospettavo, ma non ne ero sicuro.
Jon L.

1
Per coloro che lo provano in JDK8 o successivo, tieni presente che "importClass" funziona se lo ScriptEngine caricato da JRE è rhino (il che era vero per la maggior parte dei JDK 6 e 7), mentre in Nashorn (da 8 in poi) puoi usare il "File = java.io.File" compatibile con le versioni precedenti o il Java.type più recente ma non compatibile con le versioni precedenti. Ant sembra fallire silenziosamente quando ha problemi con l'esecuzione di scriptdef, come ho avuto modo di sperimentare oggi.
Matteo Steccolini

15

ant-contrib è il male; scrivi un'attività formica personalizzata.

ant-contrib è malvagio perché cerca di convertire formica da uno stile dichiarativo a uno stile imperativo. Ma xml fa un linguaggio di programmazione schifoso.

Al contrario, un'attività ant personalizzata ti consente di scrivere in un linguaggio reale (Java), con un vero IDE, dove puoi scrivere test unitari per assicurarti di avere il comportamento che desideri, e quindi fare una dichiarazione pulita nel tuo script di compilazione su il comportamento che desideri.

Questo sproloquio è importante solo se ti interessa scrivere script di formiche mantenibili. Se non ti interessa la manutenibilità, fai tutto ciò che funziona. :)

Jtf


2
Hai ragione, dovrei semplicemente scrivere un'attività formica personalizzata in Java;)
ivan_ivanovich_ivanoff,

4
ant-contrib è davvero il male. In questo momento sono nel bel mezzo di un grande progetto di costruzione di formiche che fa un uso intenso di if / then / else e antcalls e suona davvero orribile. L'intera cosa sembra uno script batch / shell convertito e tutte le cose sulle dipendenze che fa ant sono completamente disattivate dall'uso massiccio di ant-contrib. Se vuoi mantenere pulita la tua configurazione, crea la tua attività. : - /
rabbrividire

2
@cringe, non sono d'accordo. Come ogni cosa, devi sapere quando è nel tuo interesse usare ant-contrib. Stai lontano da cose come if e var e usa ant-contrib per evitare di dover reinventare la ruota.
Neil

1
E avrebbe dovuto essere un linguaggio di scripting, così imperativo e non dichiarativo, tanto per cominciare. Quindi è E chi è il male IMO
mvmn

Forse ant-contrib è un male a priori, ma il suo uso da parte di black3r sembra ragionevole e antitetico, per così dire.
ssimm

7

So che questo post è davvero vecchio ma ora che sono passate alcune versioni di ant, c'è un modo per farlo con le funzionalità di base di ant e ho pensato di condividerlo.

È fatto tramite un macrodef ricorsivo che chiama attività annidate (possono essere chiamate anche altre macro). L'unica convenzione è usare un nome di variabile fisso (elemento qui).

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

Le caratteristiche chiave necessarie dove:

Non sono riuscito a creare il delimitatore variabile, ma questo potrebbe non essere un grave svantaggio.


Purtroppo questo esaurisce la memoria ed esplode piuttosto rapidamente.
David St Denis

Sì, potrebbe saltare in aria il tuo stack, a seconda del numero di file che elabori. L'ho fatto con successo con circa 66 file (senza maggiori opzioni di memoria). Tuttavia è solo un'opzione se non hai accesso ad ant-contrib che offre più funzionalità (es. Esecuzione in parallelo).
dag

Questo è stato molto utile.
DiamondDrake

0

È possibile utilizzare l'attività ant-contrib "for" per scorrere l'elenco di file separati da qualsiasi delimetro, il delimetro predefinito è ",".

Di seguito è riportato il file di esempio che mostra questo:

<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

Fai quello che blak3r ti ha suggerito e definisci il tuo classpath di destinazione in questo modo

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

dove lib è dove memorizzi il tuo jar

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.