Introduction
Commands like sed and awk allow us to harness the power of regular expressions to search for text-based patterns in files and manipulate the matched text as per our requirement. The thing about regular expressions is that they are confusing. To the new Linux user, regular expressions may as well be another language. Although sed and awk are very powerful tools, mastering them is a tedious task for a system administrator.
While working on the command line, we frequently interact with various system configuration files and sometimes we need to modify these files to suit our needs. This requirement may span across a fleet of systems as well. Using awk or sed to modify multiline strings in files over ssh could prove to be a handful. Luckily, we can use the replace module in ansible to search for and replace multiple lines between two patterns.
In this post, we will share a sample Ansible playbook that replaces multiple lines of text between two patterns. We can view documentation for the replace module using ansible-doc. Given below is a snippet from the output.
root@linuxnix ~]# ansible-doc replace > REPLACE (/usr/lib/python3.6/site-packages/ansible/modules/files/replace.py) This module will replace all instances of a pattern within a file. It is up to the user to maintain idempotence by ensuring that the same pattern would never match any replacements made. * This module is maintained by The Ansible Community OPTIONS (= is mandatory): - after If specified, only content after this match will be replaced/removed. Can be used in combination with `before'. Uses Python regular expressions; see http://docs.python.org/2/library/re.html. Uses DOTALL, which means the `.' special character `can match newlines'. [Default: (null)] type: str version_added: 2.4 - attributes The attributes the resulting file or directory should have. To get supported flags look at the man page for `chattr' on the target system. This string should contain the attributes in the same order as the one displayed by `lsattr'. The `=' operator is assumed as default, otherwise `+' or `-' operators need to be included in the string. (Aliases: attr)[Default: (null)] type: str version_added: 2.3 - backup Create a backup file including the timestamp information so you can get the original file back if you somehow clobbered it incorrectly. [Default: False] type: bool - before If specified, only content before this match will be replaced/removed. Can be used in combination with `after'. Uses Python regular expressions; see http://docs.python.org/2/library/re.html. Uses DOTALL, which means the `.' special character `can match newlines'. [Default: (null)] type: str version_added: 2.4 - encoding The character encoding for reading and writing the file. [Default: utf-8] type: str version_added: 2.4 ...skipping... = regexp The regular expression to look for in the contents of the file. Uses Python regular expressions; see http://docs.python.org/2/library/re.html. Uses MULTILINE mode, which means `^' and `
For our playbook, we will be using the regexp and replace options to edit the /var/www/html/index.html on a couple of Ubuntu systems. Our goal is to replace the entire content between the body tags with the lines “This is a sample index.html file. The file has been updated using Ansible.” Given below is the current content residing within the body tags.
<body> <div> <div> <img src="/icons/openlogo-75.png" alt="Debian Logo"/> <span> Apache2 Debian Default Page </span> </div> <!-- <div> <div> TABLE OF CONTENTS </div> <div> <a href="#about">About</a> </div> ----------------------------------------------------output trunacted for brevity </div> </div> <div> </div> </body>
Here is our playbook.
[root@linuxnix ansible-stuff]# cat rep.yaml --- - name: Demonstrate the usage of the replace module hosts: all gather_facts: no tasks: - name: replace text within body tags replace: path: /var/www/html/index.html regexp: '(<body>[sS]*)</body>' replace: |- <body> This is a sample index.html file. The file has been updated using Ansible. </body>
Taking a look at the replace module section, we observe that under regexp we are matching for (<body>[sS]*)</body>.
This matches for any content including whitespace within the pattern <body> and </body>. In the next line, we provide some lines of text that we would like to use as replacements for the text within the patterns. The |- symbol is used when we want to use a multiline string. Let’s run the playbook.
[root@linuxnix ansible-stuff]# ansible-playbook rep.yaml PLAY [Demonstrate the usage of the replace module] ********************************************************************************************************************** TASK [replace text within body tags] ************************************************************************************************************************************ [WARNING]: Platform linux on host test_c1 is using the discovered Python interpreter at /usr/bin/python, but future installation of another Python interpreter could change this. See https://docs.ansible.com/ansible/2.9/reference_appendices/interpreter_discovery.html for more information. changed: [test_c1] [WARNING]: Platform linux on host test_c4 is using the discovered Python interpreter at /usr/bin/python, but future installation of another Python interpreter could change this. See https://docs.ansible.com/ansible/2.9/reference_appendices/interpreter_discovery.html for more information. changed: [test_c4] [WARNING]: Platform linux on host test_c5 is using the discovered Python interpreter at /usr/bin/python, but future installation of another Python interpreter could change this. See https://docs.ansible.com/ansible/2.9/reference_appendices/interpreter_discovery.html for more information. changed: [test_c5] [WARNING]: Platform linux on host test_c2 is using the discovered Python interpreter at /usr/bin/python, but future installation of another Python interpreter could change this. See https://docs.ansible.com/ansible/2.9/reference_appendices/interpreter_discovery.html for more information. changed: [test_c2] [WARNING]: Platform linux on host test_c3 is using the discovered Python interpreter at /usr/bin/python, but future installation of another Python interpreter could change this. See https://docs.ansible.com/ansible/2.9/reference_appendices/interpreter_discovery.html for more information. changed: [test_c3] PLAY RECAP ************************************************************************************************************************************************************** test_c1 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 test_c2 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 test_c3 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 test_c4 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 test_c5 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
To verify the result, we’ll now check out the content of the file on one of the nodes.
[root@linuxnix ansible-stuff]# ansible all -m shell -a "cat /var/www/html/index.html" --limit test_c1 [WARNING]: Platform linux on host test_c1 is using the discovered Python interpreter at /usr/bin/python, but future installation of another Python interpreter could change this. See https://docs.ansible.com/ansible/2.9/reference_appendices/interpreter_discovery.html for more information. test_c1 | CHANGED | rc=0 >> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>Apache2 Debian Default Page: It works</title> <style type="text/css" media="screen"> * { ----------------------------------------output truncated for brevity </style> </head> <body> This is a sample index.html file. The file has been updated using Ansible. </body> </html> Conclusion This concludes our example on how we could use an ansible playbook to replace multiple lines in a file. We hope that you found this article to be useful and we encourage you to try out different regex patterns in your playbooks to explore this approach further.
Sahil Suri
Latest posts by Sahil Suri (see all)
- Google Cloud basics: Activate Cloud Shell - May 19, 2021
- Create persistent swap partition on Azure Linux VM - May 18, 2021
- DNF, YUM and RPM package manager comparison - May 17, 2021
- Introduction to the aptitude package manager for Ubuntu - March 26, 2021
- zypper package management tool examples for managing packages on SUSE Linux - March 26, 2021