Ansuman Bebarta
Ansuman Bebarta

Reputation: 7256

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

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

Upvotes: 83

Views: 142022

Answers (9)

lonix
lonix

Reputation: 20591

The question shows grep with -Fx which means a whole line fixed string. But many people will land here looking for a way to do it with a regex.

Option 1 using shell (simpler):

- ansible.builtin.shell: grep '.* bar baz' /some/file
  changed_when: false

Option 2 using ansible itself (for the purists, and Windows users):

- ansible.builtin.lineinfile:
    path: /some/file
    regex: ".* bar baz"
    state: absent            # here's the trick
  changed_when: false
  check_mode: true
  register: result
  failed_when: result.found != 1

Upvotes: 0

Donn Lee
Donn Lee

Reputation: 3149

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
  ansible.builtin.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

Upvotes: 52

oneindelijk
oneindelijk

Reputation: 636

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

Upvotes: 2

Robert Colbert
Robert Colbert

Reputation: 126

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')"

Upvotes: 2

LHSnow
LHSnow

Reputation: 1266

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)

Upvotes: 107

Antonis Christofides
Antonis Christofides

Reputation: 6939

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

Upvotes: 58

Je.Go
Je.Go

Reputation: 81

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.

Upvotes: 5

mixja
mixja

Reputation: 7467

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

Upvotes: 14

VSK
VSK

Reputation: 469

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

Upvotes: 6

Related Questions