Ant: Bagaimana cara menjalankan perintah untuk setiap file di direktori?


97

Saya ingin menjalankan perintah dari buildfile Ant, untuk setiap file dalam direktori.
Saya mencari solusi yang tidak bergantung platform.

Bagaimana saya melakukan ini?

Tentu, saya bisa menulis skrip dalam beberapa bahasa skrip, tetapi ini akan menambah ketergantungan lebih lanjut ke proyek.

Jawaban:


61

Jawaban singkat

Gunakan <foreach>dengan file bersarang<FileSet>

Foreach membutuhkan ant-kontrib .

Contoh yang Diperbarui untuk ant-contrib baru-baru ini:

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

Ini akan memanggil "bar" target dengan $ {theFile} yang menghasilkan file saat ini.


Jadi, saya harus memasukkan sesuatu? Atau apakah saya memerlukan ant lib eksternal? Saya mendapatkan "Masalah: gagal membuat tugas atau ketik foreach" . Jika saya mengerti dengan benar, ini berarti, foreach adalah kata kunci yang tidak diketahui.
ivan_ivanovich_ivanoff

4
Ohhh payah ... itu Tugas Ant-Kontrib. Jadi, Anda harus menginstal sesuatu. Lihat di sini: ant-contrib.sourceforge.net
blak3r

3
Contoh itu dari foreach sebelum dimasukkan dalam ant-contrib. Ada contoh bagus di ant.1045680.n5.nabble.com/Using-foreach-td1354624.html Saya akan memperbarui contoh agar berfungsi.
Sean

2
Elemen kumpulan file bertingkat telah dikoreksi, sebagai gantinya gunakan jalur bersarang
Bung

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

88

Gunakan tugas <apply> .

Ini mengeksekusi perintah sekali untuk setiap file. Tentukan file dengan menggunakan kumpulan file atau sumber daya lainnya. <apply> sudah ada di dalamnya; tidak perlu ketergantungan tambahan; tidak diperlukan implementasi tugas kustom.

Ini juga memungkinkan untuk menjalankan perintah hanya sekali, menambahkan semua file sebagai argumen sekaligus. Gunakan atribut paralel untuk mengalihkan perilaku.

Maaf sudah terlambat setahun.


4
Saya baru saja menemukan ini berguna pada tahun 2011, jadi terima kasih untuk itu!
Michael Della Bitta

7
Masalah dengan <apply> adalah ia hanya menjalankan perintah sistem eksternal. Itu tidak akan melakukan hal anty secara langsung. Tidak jelas apakah itu pertanyaan aslinya atau tidak. Pada dasarnya Anda harus menggunakan <apply> atau <foreach> untuk dua situasi berbeda ... yang benar-benar bodoh.
Archie

27

Pendekatan tanpa ant-kontrib disarankan oleh Tassilo Horn ( target aslinya ada di sini )

Pada dasarnya, karena tidak ada ekstensi <java> (yet?) Dengan cara yang sama seperti <apply> memperluas <exec> , dia menyarankan untuk menggunakan <apply> (yang tentunya juga dapat menjalankan program java dalam baris perintah)

Berikut beberapa contohnya:

  <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
Ini bukan platform independen meskipun jelas diperlukan dalam pertanyaan asli ...
Chucky

18

Berikut adalah cara untuk melakukannya menggunakan javascript dan tugas ant scriptdef, Anda tidak perlu ant-contrib agar kode ini berfungsi karena scriptdef adalah tugas inti semut.

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

sebuah variabel projectdireferensikan dalam contoh di atas, tetapi definisi sebelumnya tidak tersedia. Akan lebih baik jika hal itu diwakili atau diklarifikasi. EDIT: n / m, ditemukan itu projectadalah var yang telah ditentukan untuk mengakses proyek saat ini. Aku sudah curiga, tapi tidak yakin.
Jon L.

1
Bagi mereka yang mencoba ini di JDK8 atau lebih baru, perlu diingat bahwa "importClass" berfungsi jika ScriptEngine yang dimuat oleh JRE adalah badak (yang berlaku untuk sebagian besar JDK 6 dan 7), sedangkan di Nashorn (dari 8 dan seterusnya) Anda dapat menggunakan "File = java.io.File" yang kompatibel dengan mundur atau yang lebih baru tetapi tidak kompatibel dengan versi Java.type. Ant tampaknya gagal diam-diam saat mengalami masalah saat menjalankan scriptdef, seperti yang saya alami hari ini.
Matteo Steccolini

15

ant-kontrib itu jahat; tulis tugas semut khusus.

ant-contrib jahat karena mencoba mengubah semut dari gaya deklaratif menjadi gaya imperatif. Tapi xml membuat bahasa pemrograman omong kosong.

Sebaliknya, tugas semut khusus memungkinkan Anda menulis dalam bahasa nyata (Java), dengan IDE nyata, tempat Anda dapat menulis pengujian unit untuk memastikan Anda memiliki perilaku yang Anda inginkan, dan kemudian membuat deklarasi bersih dalam skrip build Anda tentang perilaku yang Anda inginkan.

Kata-kata kasar ini hanya penting jika Anda peduli tentang menulis skrip semut yang dapat dipelihara. Jika Anda tidak peduli tentang pemeliharaan dengan segala cara, lakukan apa pun yang berhasil. :)

Jtf


2
Anda benar, saya sebaiknya menulis tugas semut khusus di Java;)
ivan_ivanovich_ivanoff

4
ant-contrib memang jahat. Saat ini saya sedang mengerjakan proyek pembangunan semut besar yang menggunakan if / then / else dan antcalls secara intens dan hasilnya sangat mengerikan. Semuanya tampak seperti skrip batch / shell yang dikonversi dan semua hal dependensi yang dilakukan sem benar-benar dimatikan oleh penggunaan ant-contrib yang berat. Jika Anda ingin menjaga pengaturan Anda tetap bersih, buat tugas Anda sendiri. : - /
ngeri

2
@ Cringe, saya tidak setuju. Seperti apa pun, Anda harus tahu kapan sebaiknya Anda menggunakan ant-contrib. Jauhi hal-hal seperti if dan var dan gunakan ant-contrib untuk menghindari keharusan menemukan kembali roda.
Neil

1
Dan seharusnya menjadi bahasa scripting, sangat penting dan bukan deklaratif, untuk memulai. Jadi Dan siapa yang IMO jahat
mvmn

Mungkin ant-contrib adalah apriori jahat tetapi penggunaan black3r itu tampaknya masuk akal dan ant-ish, jadi untuk berbicara.
ssimm

7

Saya tahu posting ini benar-benar tua tetapi sekarang setelah beberapa waktu dan versi semut berlalu, ada cara untuk melakukan ini dengan fitur dasar semut dan saya pikir saya harus membagikannya.

Ini dilakukan melalui macrodef rekursif yang memanggil tugas bertingkat (bahkan makro lain dapat dipanggil). Konvensi satu-satunya adalah menggunakan nama variabel tetap (elemen di sini).

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

Fitur utama dibutuhkan di mana:

Saya tidak berhasil membuat variabel pembatas, tetapi ini mungkin bukan kerugian besar.


Sayangnya ini kehabisan memori dan meledak agak cepat.
David St Denis

Ya, itu mungkin meledakkan tumpukan Anda, tergantung pada jumlah file yang Anda proses. Saya melakukannya dengan cukup sukses dengan sekitar 66 File (tanpa opsi Memori yang ditingkatkan). Namun itu hanya menjadi pilihan jika Anda tidak memiliki akses ke ant-contrib yang menawarkan lebih banyak fungsionalitas (mis. Menjalankan paralel).
dag

Ini sangat membantu.
DiamondDrake

0

Anda dapat menggunakan tugas ant-contrib "untuk" untuk mengulangi daftar file yang dipisahkan oleh pembatas apa pun, pembatas default adalah ",".

Berikut adalah file contoh yang menunjukkan ini:

<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

Lakukan apa yang disarankan oleh blak3r dan tentukan classpath target Anda seperti itu

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

di mana lib adalah tempat Anda menyimpan toples Anda

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.