Ashar
Ashar

Reputation: 3045

Ansible find module does not exclude folder in the search

Here is my playbook where i try to find test-juli.jar under /app directory however, i wish to exclude /app/Patchbackup folder from the search.

Below is my playbook for the same:

  tasks:

    - name: Find test home directories under /app
      find:
        paths: /app
        file_type: any
        recurse: yes
        depth: 4
        patterns: 'test-juli.jar'
        excludes: 'log,tmp,.installation,Patchbackup'
      tags: always
      register: tomjarfound

    - debug:
        msg: "ALL LISTED REFINED JARS: {{ item.path }}"
      loop: "{{ tomjarfound.files }}"

When i run the above, i was expecting the find not to find under /app/Patchbackup but the output shows that it does despite being excluded.

Here is the output:

TASK [debug] ***********************************************************************************************************************************************************
ok: [10.0.0.11] => (item=/app/apache-test-9.0.10/bin/test-juli.jar) => {
    "msg": "ALL LISTED REFINED JARS: /app/apache-test-9.0.10/bin/test-juli.jar"
}
ok: [10.0.0.11] => (item=/app/Patchbackup/app/apache-test-9.0.10/bin/test-juli.jar) => {
    "msg": "ALL LISTED REFINED JARS: /app/Patchbackup/app/apache-test-9.0.10/bin/test-juli.jar"
}

Can you please suggest how can I exclude the folder /app/Patchbackup from ansible's find ?

Upvotes: 6

Views: 9014

Answers (4)

ilias-sp
ilias-sp

Reputation: 6685

As you realized, the find module doesn't provide you with an option to start with the root dir of /app but exclude one of its directories. You have a few options:

  1. if the dirs under /app are not many, you can use the clause paths to provide a list of them (excluding the undesired Patchbackup)
  2. retrieve all results under /app and then filter out the ones from /app/Patchbackup that you don't want. Here is an example of how to filter them out:

Code:

  - set_fact:
      my_results_final: "{{ my_results_final | default([]) + [item] }}"
    when: item is not regex('^/app/Patchbackup(.+)')
    with_items:
    - "{{ tomjarfound.files }}"


  - debug:
      var: my_results_final

Upvotes: 2

Alxsey
Alxsey

Reputation: 1

My solution to this omission is to go with json_query filter:

- find:
    paths: /app
    recurse: true
    patterns: 'test-juli.jar'
  register: results

- set_fact:
    final_result: "{{ results | to_json | from_json | json_query(_query) }}"
  vars:
    _query: "files[?!contains(path, 'Patchbackup')]"

you will need to have jmespath python library installed. (if I remember it correctly that | to_json | from_json | part is needed to deal with another types issue.

Upvotes: 0

Jaap Joris Vens
Jaap Joris Vens

Reputation: 3550

Ansible's find module's excludes parameter operates on basenames of files. Per the documentation:

excludes: One or more (shell or regex) patterns, which type is controlled by use_regex option. Items whose basenames match an excludes pattern are culled from patterns matches. Multiple patterns can be specified using a list.

In your case, it is correctly ignoring /app/Patchbackup/ because its basename is Patchbackup, but also correctly including /app/Patchbackup/app/apache-test-9.0.10/bin/test-juli.jar because its basename is test-juli.jar.

Upvotes: 3

larsks
larsks

Reputation: 311750

The find module is arguably broken. The excludes parameter applies only to items in the final result set, and not to intermediate directories.

That is, if you have a directory structure like this:

toplevel/
  foo/
    testfile1.txt
  bar/
    testfile2.txt

And from the parent of the toplevel directory you run a task like this:

- find:
    paths: toplevel
    recurse: true
    excludes: foo
  register: results

Your result set will look like:

  • toplevel/bar/testfile2.txt
  • toplevel/foo/testfile1.txt

Compare that to setting file_type: any, like this:

- find:
    paths: toplevel
    file_type: any
    recurse: true
    excludes: foo
  register: results

In this case, the result set will look like:

- `toplevel/bar`
- `toplevel/bar/testfile2.txt`
- `toplevel/foo/testfile1.txt`

Note that toplevel/bar is included in the results, but toplevel/foo is excluded. That's because:

  • We set file_type: any, which means we want to find directories as well as files.
  • This makes toplevel/foo part of the result set.
  • We are excluding from the results anything with the basename foo

You will get more flexible behavior if you simply use the find command:

- name: exclude an intermediate directory with find command
  command: >-
    find toplevel -name foo -prune -o -type f -print
  register: result

Which will return the following items:

  • toplevel/bar/testfile2.txt

I've put a runnable version of the above on github.

Upvotes: 6

Related Questions