Ant: Làm thế nào để thực hiện một lệnh cho mỗi tệp trong thư mục?


97

Tôi muốn thực thi một lệnh từ tệp xây dựng Ant, cho mỗi tệp trong một thư mục.
Tôi đang tìm kiếm một giải pháp độc lập với nền tảng.

Làm thế nào để tôi làm điều này?

Chắc chắn, tôi có thể viết một kịch bản bằng một số ngôn ngữ kịch bản, nhưng điều này sẽ thêm các phần phụ thuộc vào dự án.

Câu trả lời:


61

Câu trả lời ngắn

Sử dụng <foreach>với một<FileSet>

Foreach yêu cầu kiến đóng góp .

Ví dụ được cập nhật cho người đóng góp gần đây:

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

Thao tác này sẽ chống lại "thanh" đích bằng $ {theFile} dẫn đến tệp hiện tại.


Vì vậy, tôi phải bao gồm một cái gì đó? Hay tôi cần một ít kiến ​​bên ngoài? Tôi nhận được "Sự cố: không thể tạo tác vụ hoặc nhập foreach" . Nếu tôi hiểu đúng, điều này có nghĩa là, foreach là một từ khóa không xác định.
ivan_ivanovich_ivanoff 23/09/09

4
Ohhh khập khiễng ... đó là Nhiệm vụ Kiến tạo. Vì vậy, bạn phải cài đặt một cái gì đó. Xem tại đây: ant-contrib.sourceforge.net
blak3r 23/09/09

3
Ví dụ đó là từ foreach trước khi nó được đưa vào ant-Contrib. Có một ví dụ hay tại ant.1045680.n5.nabble.com/Using-foreach-td1354624.html Tôi sẽ cập nhật ví dụ này để hoạt động.
Sean

2
Phần tử tập tin lồng nhau được mô tả, hãy sử dụng đường dẫn lồng nhau để thay thế
dude

@dude Sử dụng <foreach> <path> <fileset> <include name = "*. jar" /> </fileset> </path> </foreach>
Sharat

88

Sử dụng tác vụ <apply> .

Nó thực hiện một lệnh một lần cho mỗi tệp. Chỉ định các tệp bằng bộ tệp hoặc bất kỳ tài nguyên nào khác. <apply> được tích hợp sẵn; không cần thêm phụ thuộc; không cần triển khai tác vụ tùy chỉnh.

Cũng có thể chỉ chạy lệnh một lần, nối tất cả các tệp dưới dạng đối số trong một lần. Sử dụng thuộc tính song song để chuyển đổi hành vi.

Xin lỗi vì đã đến muộn một năm.


4
Tôi chỉ thấy điều này hữu ích vào năm 2011, vì vậy dù sao cũng cảm ơn vì điều đó!
Michael Della Bitta

7
Vấn đề với <apply> là nó chỉ thực thi các lệnh hệ thống bên ngoài. Nó sẽ không thực hiện bất kỳ công cụ anty trực tiếp. Không rõ đó có phải là câu hỏi ban đầu nói về hay không. Về cơ bản, bạn phải sử dụng <apply> hoặc <foreach> cho hai trường hợp khác nhau ... điều này hoàn toàn ngớ ngẩn.
Archie

27

Tassilo Horn đề xuất một cách tiếp cận mà không có người đóng góp ( mục tiêu ban đầu là ở đây )

Về cơ bản, vì không có phần mở rộng nào của <java> (chưa?) Giống như cách mà <apply> mở rộng <exec> , anh ấy đề xuất sử dụng <apply> (tất nhiên cũng có thể chạy chương trình java trong một dòng lệnh)

Dưới đây là một số ví dụ:

  <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
Đây không phải là rất nền tảng độc lập mặc dù nó đã được rõ ràng yêu cầu trong câu hỏi ban đầu ...
Chucky

18

Đây là cách để thực hiện việc này bằng cách sử dụng javascript và tác vụ ant scriptdef, bạn không cần ant-Contribute để mã này hoạt động vì scriptdef là một tác vụ kiến ​​cốt lõi.

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

một biến projectđược tham chiếu trong ví dụ trên, nhưng không có sẵn định nghĩa trước. Sẽ rất tốt nếu có đại diện hoặc làm rõ. EDIT: n / m, được tìm thấy đó projectlà một var được xác định trước để truy cập vào dự án hiện tại. Tôi đã nghi ngờ điều đó, nhưng không chắc chắn.
Jon L.

1
Đối với những người đang thử điều này trong JDK8 hoặc mới hơn, hãy nhớ rằng "importClass" hoạt động nếu ScriptEngine được tải bởi JRE là tê giác (điều này đúng với hầu hết JDK 6 và 7), trong khi ở Nashorn (từ 8 trở đi), bạn có thể sử dụng tương thích ngược "File = java.io.File" hoặc Java.type mới hơn nhưng không tương thích ngược. Ant dường như không thành công một cách âm thầm khi gặp sự cố khi chạy scriptdef, như tôi đã trải nghiệm ngày hôm nay.
Matteo Steccolini

15

ant-góp là ác; viết một nhiệm vụ kiến ​​tùy chỉnh.

ant-Contrib là xấu vì nó cố gắng chuyển ant từ kiểu khai báo sang kiểu mệnh lệnh. Nhưng xml là một ngôn ngữ lập trình ngu ngốc.

Ngược lại, một tác vụ kiến ​​tùy chỉnh cho phép bạn viết bằng ngôn ngữ thực (Java), với IDE thực, nơi bạn có thể viết các bài kiểm tra đơn vị để đảm bảo rằng bạn có hành vi bạn muốn và sau đó khai báo rõ ràng trong tập lệnh xây dựng của bạn về hành vi bạn muốn.

Điều này chỉ quan trọng nếu bạn quan tâm đến việc viết các tập lệnh kiến ​​có thể bảo trì. Nếu bạn không quan tâm đến khả năng bảo trì bằng mọi cách, hãy làm bất cứ điều gì có hiệu quả. :)

Jtf


2
Đúng Your're, tôi chỉ nên viết một nhiệm vụ tùy chỉnh kiến trong Java;)
ivan_ivanovich_ivanoff

4
kiến đóng góp thực sự là ác. Ngay bây giờ tôi đang ở giữa một dự án xây dựng kiến ​​lớn sử dụng cường độ cao if / then / else và antcalls và nó thực sự đọc rất kinh khủng. Toàn bộ mọi thứ trông giống như một tập lệnh batch / shell đã được chuyển đổi và tất cả những thứ phụ thuộc mà kiến ​​thực hiện đều bị tắt hoàn toàn khi sử dụng nhiều ant-Contrib. Nếu bạn muốn giữ cho thiết lập của mình sạch sẽ, hãy xây dựng nhiệm vụ của riêng bạn. : - /
cringe

2
@cringe, tôi không đồng ý. Giống như bất cứ điều gì, bạn phải biết khi nào thì sử dụng ant-Contrib là có lợi nhất cho bạn. Tránh xa những thứ như if và var và sử dụng ant-góp để tránh phải phát minh lại bánh xe.
Neil

1
Và đáng lẽ ra phải là một ngôn ngữ kịch bản, bắt đầu nên bắt buộc và không mang tính khai báo. Vì vậy, nó Và là người ác IMO
mvmn

Có thể kiến ​​đóng góp là điều ác trước nhưng việc black3r sử dụng nó có vẻ hợp lý và hợp lý, có thể nói như vậy.
ssimm 16/02/17

7

Tôi biết bài đăng này thực sự cũ nhưng bây giờ một thời gian và các phiên bản kiến ​​đã trôi qua, có một cách để làm điều này với các tính năng kiến ​​cơ bản và tôi nghĩ tôi nên chia sẻ nó.

Nó được thực hiện thông qua một macrodef đệ quy gọi các tác vụ lồng nhau (thậm chí có thể gọi các macro khác). Quy ước duy nhất là sử dụng một tên biến cố định (phần tử ở đây).

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

Các tính năng chính cần thiết:

Tôi đã không quản lý để tạo ra biến thể dấu phân cách, nhưng đây có thể không phải là một nhược điểm lớn.


Thật không may, điều này hết bộ nhớ và nổ khá nhanh.
David St Denis

Có, nó có thể làm nổ ngăn xếp của bạn, tùy thuộc vào số lượng tệp bạn xử lý. Tôi đã làm điều đó khá thành công với khoảng 66 Tệp (không tăng tùy chọn Bộ nhớ). Tuy nhiên, nguyên nhân chỉ là một tùy chọn nếu bạn không có quyền truy cập vào chương trình ant-mít cung cấp nhiều chức năng hơn (ví dụ: chạy song song).
dag

Điều này rất hữu ích.
DiamondDrake

0

Bạn có thể sử dụng tác vụ ant-Contribute "for" để lặp lại trên danh sách các tệp được phân tách bởi bất kỳ tệp nào, máy mê sảng mặc định là ",".

Sau đây là tệp mẫu hiển thị điều này:

<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

Làm những gì blak3r đề xuất và xác định classpath mục tiêu của bạn như vậy

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

nơi lib là nơi bạn cất cái lọ của mình

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.