Муравей: Как выполнить команду для каждого файла в каталоге?

97

Я хочу выполнить команду из файла сборки Ant для каждого файла в каталоге.
Я ищу платформо-независимое решение.

Как мне это сделать?

Конечно, я мог бы написать сценарий на каком-нибудь языке сценариев, но это добавило бы дополнительных зависимостей в проект.

ivan_ivanovich_ivanoff
источник

Ответы:

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}, в результате чего будет получен текущий файл.

blak3r
источник
Итак, я должен что-то включить? Или мне нужна внешняя ant lib? Я получаю сообщение «Проблема: не удалось создать задачу или ввести команду foreach» . Если я правильно понял, это означает, что foreach - неизвестное ключевое слово.
ivan_ivanovich_ivanoff
4
Оххх ... это задача Ant-Contrib. Итак, вам нужно что-то установить. См. Здесь: ant-contrib.sourceforge.net
blak3r
3
Этот пример взят из foreach до того, как он был включен в ant-contrib. На сайте ant.1045680.n5.nabble.com/Using-foreach-td1354624.html есть хороший пример. Я собираюсь обновить пример, чтобы он работал.
Шон
2
Вложенный элемент набора файлов устарел, используйте вместо него вложенный путь
чувак,
@dude Используйте <foreach> <path> <fileset> <include name = "*. jar" /> </fileset> </path> </foreach>
Шарат
88

Используйте задачу <apply> .

Он выполняет команду один раз для каждого файла. Укажите файлы с помощью наборов файлов или любого другого ресурса. <apply> встроен; не требуется дополнительных зависимостей; реализация нестандартной задачи не требуется.

Также можно запустить команду только один раз, добавив все файлы в качестве аргументов за один раз. Используйте атрибут parallel для переключения поведения.

Извините за опоздание на год.

Alex
источник
4
Я нашел это полезным только в 2011 году, так что в любом случае спасибо за это!
Майкл Делла Битта
7
Проблема с <apply> в том, что он выполняет только внешние системные команды. Он не будет делать ничего прямо сейчас. Неясно, был ли это первоначальный вопрос или нет. По сути, вам нужно использовать <apply> или <foreach> для двух разных ситуаций ... что совершенно глупо.
Арчи
27

Подход без ant-contrib предложен Тассило Хорном ( исходная цель здесь )

В основном, поскольку нет расширения <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> 
Jmini
источник
2
Это не очень зависит от платформы, хотя это явно требовалось в исходном вопросе ...
Чаки
18

Вот способ сделать это с помощью javascript и задачи ant scriptdef, вам не нужен ant-contrib для работы этого кода, поскольку scriptdef - это основная задача 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>
амс
источник
projectв приведенном выше примере имеется ссылка на переменную , но без предварительного определения. Было бы хорошо, если бы это было представлено или разъяснено. EDIT: n / m, обнаружено, что projectэто предопределенная переменная для доступа к текущему проекту. Я подозревал это, но не был уверен.
Джон Л.
2
Для тех, кто пробует это в JDK8 или более поздней версии, имейте в виду, что importClass работает, если ScriptEngine, загруженный JRE, является rhino (что было верно для большинства JDK 6 и 7), а в Nashorn (начиная с 8 и далее) вы можете использовать обратно совместимый "File = java.io.File" или более новый, но не обратно совместимый Java.type. Кажется, что Ant молча терпит неудачу при проблемах с запуском scriptdef, как я сегодня убедился.
Маттео Стекколини
16

ant-contrib - зло; написать специальную задачу для муравьев.

ant-contrib является злом, потому что он пытается преобразовать ant из декларативного стиля в императивный стиль. Но xml - это дерьмовый язык программирования.

Напротив, настраиваемая задача ant позволяет вам писать на реальном языке (Java) с реальной IDE, где вы можете писать модульные тесты, чтобы убедиться, что у вас есть желаемое поведение, а затем сделать чистое объявление в сценарии сборки о поведение, которое вы хотите.

Эта напыщенная речь имеет значение только в том случае, если вы заботитесь о написании поддерживаемых сценариев ant. Если вас не заботит ремонтопригодность, делайте все, что работает. :)

JTF

Джеффри Фредрик
источник
2
Вы правы, мне просто нужно написать кастомную задачу для муравьев на Java;)
ivan_ivanovich_ivanoff
4
ant-contrib действительно зло. Прямо сейчас я работаю над большим проектом сборки муравьев, который интенсивно использует if / then / else и antcalls, и это действительно ужасно. Все это выглядит как преобразованный сценарий пакета / оболочки, и все функции, связанные с зависимостями, которые выполняет ant, полностью отключены интенсивным использованием ant-contrib. Если вы хотите, чтобы ваша установка была чистой, создайте свою собственную задачу. : - /
cringe
2
@cringe, я не согласен. Как и все остальное, вы должны знать, когда в ваших интересах использовать ant-contrib. Держитесь подальше от таких вещей, как if и var, и используйте ant-contrib, чтобы не изобретать велосипед.
Нил
1
И должен был быть язык сценариев, настолько императивный, а не декларативный, с самого начала. Так это и кто зло ИМО
mvmn
Может быть, ant-contrib является априори злом, но его использование black3r кажется разумным и, так сказать, муравьиным.
ssimm
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>

Ключевые особенности, необходимые там, где:

Мне не удалось сделать разделитель variabel, но это не может быть серьезным недостатком.

даг
источник
К сожалению, это исчерпывает память и довольно быстро взрывается.
Дэвид Сен-Дени
Да, это может взорвать ваш стек, в зависимости от количества обрабатываемых файлов. Я сделал это довольно успешно с примерно 66 файлами (без увеличения параметров памяти). Однако это, конечно, вариант, только если у вас нет доступа к ant-contrib, который предлагает больше функций (например, параллельный запуск).
dag
Это было очень полезно.
DiamondDrake
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>
Hemant
источник
0

Сделайте то, что предложил blak3r, и определите свой целевой путь к классам следующим образом

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

где lib - это место, где вы храните свою банку

тарзанка
источник