In the lineinfile module, it replaces the full line.
If the line is long I have to repeat the whole line again.
Let us suppose I want to replace the single word in the file:
#abc.conf
This is my horse
this is the playbook:
- lineinfile: dest=abc.conf
state=present
regexp='horse'
line='This is my dog'
backup=yes
is there any way to achieve someting like sed 's/horse/dog/g' ?
New module replace available since 1.6 version:
- replace:
dest=abc.conf
regexp='horse'
replace='dog'
backup=yes
You can use backreferences to retrieve other parts(that should not be changed) of the line:
- lineinfile: dest=abc.conf
state=present
regexp='^(.*)horse(.*)$'
line='\1dog\2'
backup=yes
backrefs=yes
If you need to do more replace operations in one block and you have the file locally, you might want to consider using template, which substitutes variables in the template file and copies the file to the remote:
- template: src=/mytemplates/foo.j2 dest=/etc/file.conf
In the local file you can write a variable with ansible sintax like
{{variable}}
and it will be substituted if it is in the scope of the script. Here the docs.
Related
My script has many lines starting with slo. How can I replace all the strings that are starting with slo to fwd using bash commands? Any help would be appreciated.
Here is a snippet of my script
template_version: 2018-03-02
resources:
instance01:
type: ../../../templates/nf.yaml
properties:
vm_name: 'slol2lvdl1'
vm_flavour: 'dns_19te'
image_name: 'pdns_dnsd_slo_211214121207'
vm_az: 'az-1'
vm_disk_root_size: '50'
vm_disk_data_size: '50'
network_mgmt_refs: 'int:dns_ox_slo_507:c3dns_slo_live_nc_vnns_pcg'
My requirement is to replace all slo to fwd in the above code. I have 5 files like this in the same directory.
sed is the go-to for file content replacements with regular expressions. If every slo you want to replace is between _ characters it's fairly easy with a command like this (in GNU sed which ships with just about all linuxes):
sed -i -e 's/_slo_/_fwd_/g' files to replace
-i replaces the text inline, replacing existing file contents with updated contents.
If not all slo are within _ characters you need to worry about unintentional matches.
Be sure to make a backup of these files or if they're in a git repo work from a clean state in case you don't like the change. Using git to track the changes might make sense even if you don't currently have the files in a git repo as this will make it trivial to compare before and after.
sed -i 's/slo/fwd/' worked! Also found many alternatives but sed was straight forward!
I have a precommit file which checks for commented line format in .cfg files. The current comment style is a semicolon, so this will fail if a pound sign is used for file comments. Can RegEx somehow be used to match multiple comment patterns? For example:
- --comment-style
- ';'|'#'|'some other comment'
files: |
(?x)(
\.cfg
)$
This is the source of the precommit test:
https://github.com/Lucas-C/pre-commit-hooks/blob/master/tests/insert_license_test.py
Thanks.
it cannot
https://github.com/Lucas-C/pre-commit-hooks/blob/556aa509296132b8e2477f0a3e33c9916232e748/pre_commit_hooks/insert_license.py#L32-L35
parser.add_argument('--comment-style', default='#',
help='Can be a single prefix or a triplet: '
'<comment-start>|<comment-prefix>|<comment-end>'
'E.g.: /*| *| */')
there is only one comment type that is allowed by that hook
I want to uncomment a line in file sshd_config by using Ansible and I have the following working configuration:
- name: Uncomment line from /etc/ssh/sshd_config
lineinfile:
dest: /etc/ssh/sshd_config
regexp: '^#AuthorizedKeysFile'
line: 'AuthorizedKeysFile .ssh/authorized_keys'
However this config only works if the line starts by #AuthorizedKeysFile, but it won't work if the line starts by # AuthorizedKeysFile or # AuthorizedKeysFile (spaces between # and the words).
How can I configure the regexp so it won't take into account any number of spaces after '#'?
I've tried to add another lineinfile option with a space after '#', but this is not a good solution:
- name: Uncomment line from /etc/ssh/sshd_config
lineinfile:
dest: /etc/ssh/sshd_config
regexp: '# AuthorizedKeysFile'
line: 'AuthorizedKeysFile .ssh/authorized_keys'
If you need zero or more white spaces after the '#' character, the following should suffice:
- name: Uncomment line from /etc/ssh/sshd_config
lineinfile:
dest: /etc/ssh/sshd_config
regexp: '^#\s*AuthorizedKeysFile.*$'
line: 'AuthorizedKeysFile .ssh/authorized_keys'
The modification to your original code is the addition of the \s* and the .*$ in the regex.
Explanation:
\s - matches whitespace (spaces, tabs, line breaks and form feeds)
* - specifies that the expression to it's left (\s) can have zero or more instances in a match
.* - matches zero or more of any character
$ - matches the end of the line
Firstly, you are using the wrong language. With Ansible, you don't tell it what to do, but define the desired state. So it shouldn't be Uncomment line form /etc/ssh/sshd_config, but Ensure AuthorizedKeysFile is set to .ssh/authorized_keys.
Secondly, it doesn't matter what the initial state is (if the line is commented, or not). You must specify a single, unique string that identifies the line.
With sshd_config this is possible as the AuthorizedKeysFile directive occurs only once in the file. With other configuration files this might be more difficult.
- name: Ensure AuthorizedKeysFile is set to .ssh/authorized_keys
lineinfile:
dest: /etc/ssh/sshd_config
regexp: AuthorizedKeysFile
line: 'AuthorizedKeysFile .ssh/authorized_keys'
It will match any line containing AuthorizedKeysFile string (no matter if it's commented or not, or how many spaces are there) and ensure the full line is:
AuthorizedKeysFile .ssh/authorized_keys
If the line were different, Ansible will report "changed" state.
On the second run, Ansible will find the AuthorizedKeysFile again and discover the line is already in the desired state, so it will end the task with "ok" state.
One caveat with the above task is that if any of the lines contains a comment such as a real, intentional comment (for example an explanation in English containing the string AuthorizedKeysFile), Ansible will replace that line with the value specified in line.
I should caveat this with #techraf's point that 99% of the time a full template of a configuration file is almost always better.
Times I have done lineinfile include weird and wonderful configuration files that are managed by some other process, or laziness for config I don't fully understand yet and may vary by distro/version and I don't want to maintain all the variants... yet.
Go forth and learn more Ansible... it is great because you can keep iterating on it from raw bash shell commands right up to best practice.
lineinfile module
Still good to see how best to configuration manage one or two settings just a little better with this:
tasks:
- name: Apply sshd_config settings
lineinfile:
path: /etc/ssh/sshd_config
# might be commented out, whitespace between key and value
regexp: '^#?\s*{{ item.key }}\s'
line: "{{ item.key }} {{ item.value }}"
validate: '/usr/sbin/sshd -T -f %s'
with_items:
- key: MaxSessions
value: 30
- key: AuthorizedKeysFile
value: .ssh/authorized_keys
notify: restart sshd
handlers:
- name: restart sshd
service:
name: sshd
state: restarted
validate don't make the change if the change is invalid
notify/handlers the correct way to restart once only at the end
with_items (soon to become loop) if you have multiple settings
^#? the setting might be commented out - see the other answer
\s*{{ item.key }}\s will not match other settings (i.e. SettingA cannot match NotSettingA or SettingAThisIsNot)
Still might clobber a comment like # AuthorizedKeysFile - is a setting which we have to live with because there could be a setting like AuthorizedKeysFile /some/path # is a setting... re-read the caveat.
template module
- name: Configure sshd
template:
src: sshd_config.j2
dest: /etc/ssh/sshd_config
owner: root
group: root
mode: "0644"
validate: '/usr/sbin/sshd -T -f %s'
notify: restart sshd
handlers:
- name: restart sshd
service:
name: sshd
state: restarted
multiple distro support
And if you are not being lazy about supporting all your distros see this tip
- name: configure ssh
template: src={{ item }} dest={{ SSH_CONFIG }} backup=yes
with_first_found:
- "{{ ansible_distribution }}-{{ ansible_distribution_major_version }}.sshd_config.j2"
- "{{ ansible_distribution }}.sshd_config.j2"
https://ansible-tips-and-tricks.readthedocs.io/en/latest/modifying-files/modifying-files/
(needs to be updated to a loop using the first_found lookup)
Is it possible to achieve the same goal with replace module.
https://docs.ansible.com/ansible/latest/modules/replace_module.html
- name: Uncomment line from /etc/ssh/sshd_config
replace:
path: /etc/ssh/sshd_config
regexp: '^\s*#+AuthorizedKeysFile.*$'
replace: 'AuthorizedKeysFile .ssh/authorized_keys'
If you want to simply uncomment a line without setting the value, you can use replace with backreferences, eg (with a handy loop):
- name: Enable sshd AuthorizedKeysFile
replace:
path: /etc/ssh/sshd_config
# Remove comment and first space from matching lines
regexp: '^#\s?(\s*){{ item }}(.+)$'
replace: '\1{{ item }}\2'
loop:
- 'AuthorizedKeysFile'
This will only remove the first space after the #, and so retain any original indenting. It will also retain anything after the key (eg the default setting, and any following comments)
Thanks to the other helpful answers that provided a solid starting point.
I tried this in my task, but doesn't seem to work
- name: Fix line endings from CRLF to LF
local_action: replace dest={{my_dir}}/conf/{{item}} regexp='\r\n' replace='\n'
I usually do this using sed as follows and it works
sed -i 's/\r//g' file
I want to avoid using shell module to do this replacement as it throws a warning in ansible
You can remove the CRLF line endings with the -replace command. Your playbook might look like:
---
- hosts: all
tasks:
- local_action: replace dest={{my_dir}}/conf/{{item}} regexp="\r"
By not specifying the replace parameter in the - replace command, it will just remove all carriage returns. See http://docs.ansible.com/ansible/replace_module.html.
I tested this with a local file I created and it worked when testing on localhost. It also worked when I added localhost to the /etc/ansible/hosts file and had the following playbook instead:
---
- hosts: all
tasks:
- replace: dest={{my_dir}}/conf/{{item}} regexp="\r"
Just be sure to use the absolute filepath.
You can do something like this:
set_fact:
my_content: "{{ lookup('file', "{{my_dir}}/conf/{{item}}" ) | replace('\r\n', '\n')}}"
After this you can use the content or save in the disk.
The following converts line endings using the Jinja2 template engine. A line-ending directive is inserted at the beginning of the source file on the ansible machine (delegate_to: localhost). Sending the file to the downstream server can then be done by applying template or win_template to the file.
It handles source files with any line-ending, which could be useful if you're working through a list of files from more than one origin.
- name: prepare to add line endings
lineinfile:
insertbefore: BOF
dest: '{{ src_file }}'
line: '#jinja2: newline_sequence:"\n"'
#for Linux to Windows: #line: '#jinja2: newline_sequence:"\r\n"'
delegate_to: localhost
- name: copy changed file with correct line-endings
template: # win_template for Linux to Windows
src: '{{ src_file }}'
dest: '{{ dest_file }}'
I am a newbie to Linux and am trying to extract portion of a filename from the absolute path in a bash script. For example, if the path is /opt/data/filename-attribute.dat, I am able to get the path of the directory and the file as follows:
sourcedir=`dirname $path`
name=`basename $path`
I would like to extract attribute from the file and was wondering if there was a quick way in shell script to perform this. I can get filename-attribute by
f=${name%%[.]*}
and would like to extract just the attribute.
The easiest way is just to do it in two steps:
f="${name%.*}" # strip everything from the last dot onward
f="${f##*-}" # strip everything up through the last hyphen
If I understand you right
a="${f#*-}"