Only check whether a line present in a file (ansible)

Ansible

Ansible Problem Overview


In ansible, I need to check whether a particular line present in a file or not. Basically, I need to convert the following command to an ansible task. My goal is to only check.

grep -Fxq "127.0.0.1" /tmp/my.conf

Ansible Solutions


Solution 1 - Ansible

Use check_mode, register and failed_when in concert. This fails the task if the lineinfile module would make any changes to the file being checked. Check_mode ensures nothing will change even if it otherwise would.

- name: "Ensure /tmp/my.conf contains '127.0.0.1'"
  lineinfile:
    name: /tmp/my.conf
    line: "127.0.0.1"
    state: present
  check_mode: yes
  register: conf
  failed_when: (conf is changed) or (conf is failed)

Solution 2 - Ansible

- name: Check whether /tmp/my.conf contains "127.0.0.1"
  command: grep -Fxq "127.0.0.1" /tmp/my.conf
  register: checkmyconf
  check_mode: no
  ignore_errors: yes
  changed_when: no

- name: Greet the world if /tmp/my.conf contains "127.0.0.1"
  debug: msg="Hello, world!"
  when: checkmyconf.rc == 0

Update 2017-08-28: Older Ansible versions need to use always_run: yes instead of check_mode: no.

Solution 3 - Ansible

User robo's regexp & absent method is quite clean, so I've fleshed it out here for easy use and added improvements from comments by @assylias and @Olivier:

- name: Ensure /tmp/my.conf contains 127.0.0.1
  lineinfile:
    path: /tmp/my.conf
    regexp: '^127\.0\.0\.1.*whatever'
    state: absent
  check_mode: yes
  changed_when: false
  register: out

- debug:
    msg: "Yes, line exists."
  when: out.found

- debug:
    msg: "Line does NOT exist."
  when: not out.found

Solution 4 - Ansible

With the accepted solution, even though you ignore errors, you will still get ugly red error output on the first task if there is no match:

TASK: [Check whether /tmp/my.conf contains "127.0.0.1"] ***********************
failed: [localhost] => {"changed": false, "cmd": "grep -Fxq "127.0.0.1" /tmp/my.conf", "delta": "0:00:00.018709", "end": "2015-09-27 17:46:18.252024", "rc": 1, "start": "2015-09-27 17:46:18.233315", "stdout_lines": [], "warnings": []}
...ignoring

If you want less verbose output, you can use awk instead of grep. awk won't return an error on a non-match, which means the first check task below won't error regardless of a match or non-match:

- name: Check whether /tmp/my.conf contains "127.0.0.1"
  command: awk /^127.0.0.1$/ /tmp/my.conf
  register: checkmyconf
  changed_when: False

- name: Greet the world if /tmp/my.conf contains "127.0.0.1"
  debug: msg="Hello, world!"
  when: checkmyconf.stdout | match("127.0.0.1")

Notice that my second task uses the match filter as awk returns the matched string if it finds a match.

The alternative above will produce the following output regardless of whether the check task has a match or not:

TASK: [Check whether /tmp/my.conf contains "127.0.0.1"] ***********************
ok: [localhost]

IMHO this is a better approach as you won't ignore other errors in your first task (e.g. if the specified file did not exist).

Solution 5 - Ansible

Use ansible lineinfile command, but this command will update the file with the line if it does not exists.

- lineinfile: dest=/tmp/my.conf line='127.0.0.1' state=present

Solution 6 - Ansible

Another way is to use the "replace module" then "lineinfile module".

The algo is closed to the one used when you want to change the values of two variables.

  • First, use "replace module" to detect if the line you are looking for is here and change it with the something else. (Like same line + something at the end).
  • Then if "replace" is true, It means your line is here then replace the new line with a particularity, with the new line looking.
  • Else the line you are looking for is not here.

Example:

# Vars
- name: Set parameters
  set_fact:
    newline      : "hello, i love ansible"
    lineSearched : "hello"
    lineModified : "hello you"

# Tasks
- name: Try to replace the line
  replace:
    dest    : /dir/file
    replace : '{{ lineModified }} '
    regexp  : '{{ lineSearched }}$'
    backup  : yes
  register  : checkIfLineIsHere

- name: Line is here, change it
  lineinfile:
    state   : present
    dest    : /dir/file
    line    : '{{ newline }}'
    regexp  : '{{ lineModified }}$'
  when: checkIfLineIsHere.changed
  • If the file contains "hello", it will become "hello you" then "hello, i love ansible" at the end.
  • If the file content doesn't contain "hello", the file is not modified.

With the same idea, you can do something if the lineSearched is here:

# Vars
- name: Set parameters
  set_fact:
    newline      : "hello, i love ansible"
    lineSearched : "hello"
    lineModified : "hello you"

# Tasks
- name: Try to replace the line
  replace:
    dest    : /dir/file
    replace : '{{ lineModified }} '
    regexp  : '{{ lineSearched }}$'
    backup  : yes
  register  : checkIfLineIsHere

# If the line is here, I want to add something.
- name: If line is here, do something
  lineinfile:
    state   : present
    dest    : /dir/file
    line    : '{{ newline }}'
    regexp  : ''
    insertafter: EOF
  when: checkIfLineIsHere.changed

# But I still want this line in the file, Then restore it
- name: Restore the searched line.
  lineinfile:
    state   : present
    dest    : /dir/file
    line    : '{{ lineSearched }}'
    regexp  : '{{ lineModified }}$'
  when: checkIfLineIsHere.changed
  • If the file contains "hello", the line will still contain "hello" and "hello, i love ansible" at the end.
  • If the file content doesn't contain "hello", the file is not modified.

Solution 7 - Ansible

You can use the file plugin for this scenario.

To set a fact you can use in other tasks ... this works.

- name: Check whether /tmp/my.conf contains "127.0.0.1"
  set_fact:
    myconf: "{{ lookup('file', '/tmp/my.conf') }}"  
  ignore_errors: yes

- name: Greet the world if /tmp/my.conf contains "127.0.0.1"
  debug: msg="Hello, world!"
  when: "'127.0.0.1' in myconf"

To check the file content as a condition of a task ... this should work.

- name: Greet the world if /tmp/my.conf contains "127.0.0.1"
  debug: msg="Hello, world!"
  when: "'127.0.0.1' in lookup('file', '/tmp/my.conf')"

Solution 8 - Ansible

Another solution, also useful for other purposes is to loop over the contents of the file, line by line

- name: get the file
  slurp:
    src: /etc/locale.gen
  register: slurped_file

- name: initialize the matches list
  set_fact:
    MATCHES: []

- name: collect matches in a list
  set_fact:
    MATCHES: "{{ MATCHES + [line2match] }}"
  loop: "{{ file_lines }}"
  loop_control:
    loop_var: line2match
  vars:
    - decode_content: "{{ slurped_file.content | b64decode }}"
    - file_lines: "{{ decode_content.split('\n') }}"
  when: '"BE" in line2match'
  
- name: report matches if any
  debug:
    msg: "Found {{ MATCHES | length }} matches\n{{ MATCHES }}"
  when: 'listlen | int > 0'
  vars:
    listlen: "{{ MATCHES | length }}"

This mechanism can be use to get a specific value from a matched line if you use it for example with the jinja regex_replace filter

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAnsuman BebartaView Question on Stackoverflow
Solution 1 - AnsibleLHSnowView Answer on Stackoverflow
Solution 2 - AnsibleAntonis ChristofidesView Answer on Stackoverflow
Solution 3 - AnsibleDonn LeeView Answer on Stackoverflow
Solution 4 - AnsiblemixjaView Answer on Stackoverflow
Solution 5 - AnsibleVSKView Answer on Stackoverflow
Solution 6 - AnsibleJe.GoView Answer on Stackoverflow
Solution 7 - AnsibleRobert ColbertView Answer on Stackoverflow
Solution 8 - AnsibleoneindelijkView Answer on Stackoverflow