CorayThan
CorayThan

Reputation: 17825

Jinja2 filter list using string contains test

I'm trying to filter a list in ansible in Jinja2 when the elements contain a string, but the Jinja documentation doesn't seem clear enough for me to figure it out.

This is what I have so far:

- name: run script
  command: /usr/tmp/run_script.py
  register: script_results

- name: display run info
  debug:
    var: "{{script_results.stdout_lines | select(\"'running script' in script_results.stdout_lines\") }}"

But all I get is the error:

"<generator object _select_or_reject at 0x13851e0>": "VARIABLE IS NOT DEFINED!"

So for example, if stdout_lines contains ["apples","running script one","oranges","running script two"], I want to print

running script one
running script two

They have documentation for select and documentation for built-in-tests, but they don't display the "in" test, and I don't know how they work in the context of this ansible variable.

I tried solving it like this:

- name: display run info
  debug:
    var: item
  with_items: "{{script_results.stdout_lines}}"
  when: "'running script' in item"

But that displays "skipping" for every line that doesn't pass the test ... kinda defeating the purpose!

Upvotes: 17

Views: 55512

Answers (5)

Bruce
Bruce

Reputation: 21

I know this is an old thread but I too was looking for an answer to this and I was able to use the if in method. I think the other issue you were having was how to display the resulting list. You can do a jinja loop right in the ansible modules, debug for example. Of course since Ansible is horrible with presenting data using debug, you could also utilize the blockinfile module with the pipe (|). Hopefully this helps others and gives yet another option.

- debug:
    msg: |
      {% for item in (script_results.stdout_lines) %}
        {% if 'running script' in item %}
        {{ item }}
        {% endif %}
      {% endfor %}
  tags: debug

or to add the filtered data to a file:

- name: Update the scriptStatus file.
  delegate_to: localhost
  run_once: TRUE
  blockinfile:
    path: '/tmp/scriptStatus.txt'
    block: |
     {% for item in (script_results.stdout_lines) %}
        {% if 'running script' in item %}
        {{ item }}
        {% endif %}
      {% endfor %}
  tags: chkScripts

Upvotes: 2

Akif
Akif

Reputation: 6776

You can build a new list with set_fact and print the elements of a new list.

- hosts: localhost
  gather_facts: false
  vars:
    script_stdout_lines:
      - apples
      - running script one
      - oranges
      - running script two
  tasks:
    - set_fact:
        new_list: "{{ new_list | default([]) + [item] }}"
      with_items: "{{ script_stdout_lines }}"
      when: '"running script" in item'
    - debug: var=new_list

Result:

TASK [set_fact] *********************************************************************************************************************
skipping: [localhost] => (item=apples) 
ok: [localhost] => (item=running script one)
skipping: [localhost] => (item=oranges) 
ok: [localhost] => (item=running script two)

TASK [debug] ************************************************************************************************************************
ok: [localhost] => {
    "new_list": [
        "running script one",
        "running script two"
    ]
}

It prints skipping during set_fact operation but at the end it provides a new list with the only matching items.

Upvotes: 1

udondan
udondan

Reputation: 59979

The select filter would take another filter. Like in the docs odd, which will return only the odd elements of the list. The filter you would like to combine select with is equalto.

Now here's the thing. Ansible bundles a very old version of Jinja2, which simply does not contain the equalto filter. Yes, that renders it useless unless you want to filter odd elements. (Which nobody ever in history wanted to...)

Furthermore I was yet unable to make custom filter plugins work in Ansible 2. So you're pretty much forced to hack something ugly together.

helloV already showed one option. Here is another idea:

- name: run script
  shell: /usr/tmp/run_script.py | grep "running script"
  register: script_results

Update:

I recently discovered you can use match (not a standard Jinja2 filter but added by Ansible) together with select. Thats a good replacement for the eualto filter plus you can use regular expressions. This should work:

{{ script_results.stdout_lines | select("match", ".*running script.*") }}

Upvotes: 24

CorayThan
CorayThan

Reputation: 17825

I ended up writing a python script to do it, because I couldn't get ansible or ancient-jinja2 to make the cut.

Ansible tasks:

- name: gather run info
  command: "{{role_path}}/files/print_results.py {{script_results.stdout_lines}}"
  register: script_print_results
  delegate_to: 127.0.0.1
  run_once: true

- name: display run info
  debug:
    var: script_print_results.stdout_lines
  delegate_to: 127.0.0.1
  run_once: true

Python script:

for result_line in sys.argv[1:]:
    if "running script:" in result_line:
        print result_line[1:-1]

Upvotes: 0

helloV
helloV

Reputation: 52375

I understand there may be more than one way to do this. Will this work for you?

  - debug: var={{item}}
    when: item.find('running script') > -1
    with_items: script_results.stdout_lines

Upvotes: 4

Related Questions