2
votes

I want to rename files using Ant maintaining their directory structure.

e.g. Assume following directory structure:

- copy
    - new
        - testthis.a

Using code below, I could rename files containing "this" word to "that.a" using copy task, but they all are getting pasted into "paste" directory loosing their directory structure.

<copy todir="paste" overwrite="true">
    <fileset dir="copy"/>
    <regexpmapper from="^(.*)this(.*)\.a$$" to="that.a"/>
</copy>

Output:

- paste
    - that.a

If I change regexmapper to (notice \1 before that.a):

    <regexpmapper from="^(.*)this(.*)\.a$$" to="\1that.a"/>

It's generating correct directory structure but always prepends word before "this" to "that.a"

Output:

- paste
    - new
        - testthat.a

Is there any way to rename files maintaining their directory structure without pre-pending or appending any word?

Is there any other mapper which can be used for the same?

Any help would be appreciated.

2

2 Answers

2
votes
<copy todir="paste" verbose="true">
    <fileset dir="copy" includes="**/*this*.a"/>
    <regexpmapper from="((?:[^/]+/)*)[^/]+$$" to="\1that.a" handledirsep="true"/>
</copy>

First, setting handledirsep="true" allows us use forward slashes to match backslashes. This makes the regular expression a bit cleaner.

Next, I'll explain the gnarly regex by breaking it into parts.

I explode ((?:[^/]+/)*) into...

(
    (?:
        [^/]+
        /
    )
    *
)

What the parts mean:

(  -- capture group 1 starts
    (?:  -- non-capturing group starts
        [^/]+  -- greedily match as many non-directory separators as possible
        /  -- match a single directory-separator character
    )  -- non-capturing group ends
    * -- repeat the non-capturing group zero-or-more times
)  -- capture group 1 ends

The above parts repeatedly match as many subdirectories as possible. The ( and ) put all of the matches into capture group 1. Later, capture group 1 can be used in the to attribute of <regexpmapper> with a \1 backreference.

If there are no / directory separators in a path, then the above parts won't match anything and capture group 1 will be an empty string.

Moving to the end of the regex, the $$ anchors the regex to the end of each path selected by the <fileset>.

In the double dollar-sign expression, $$, the first $ escapes the second $. This is necessary because Ant would treat a single $ as the start of a property reference.

The [^/]+ matches just the filename because it matches all characters at the end of the path that aren't directory separators (/).

Example

Given the following directory structure...

- copy (dir)
    - new (dir)
        - notthis.b
        - testthis.a
    - anythis.a

...Ant outputs...

[copy] Copying 2 files to C:\ant\paste
[copy] Copying C:\ant\copy\anythis.a to C:\ant\paste\that.a
[copy] Copying C:\ant\copy\new\testthis.a to C:\ant\paste\new\that.a
0
votes

Try this regexpmapper:

<regexpmapper from="^(.*)/([^/]*)this(.*)\.a$$" to="\1/that\3"/>

This cuts the path (\1) and filename prefix (\2), so you can preserve the directory structure.

Also, you can preserve the file extension if you use \3in the replacement string.