I have following registered variable with stdout and I want to remove every character starting with "#".
ok: [localhost] => {
"msg": [
"wazuh#4.3.10-4311"
]
}
Example: wazuh#4.3.10-4311 should become wazuh.
remove every character after "#"
---
- hosts: localhost
gather_facts: false
vars:
my_string: wazuh#4.3.10-4311
tasks:
- debug:
msg: "{{ my_string | regex_replace('#.*', '') }}"
Provides:
wazuh
One could also look at the use case as just interested in the left part of the #
delimitered string.
---
- hosts: localhost
gather_facts: false
vars:
my_string: wazuh#4.3.10-4311
tasks:
# For Ansible v2.9 and later
- name: Use of Python string method
debug:
msg: "{{ my_string.split('#') | first }}"
So not removing the part behind but only deliver the part before.
# For Ansible v2.10 and later
- name: Show left part with filter
debug:
msg: "{{ my_string | split('#') | first }}"
Also this solution it's working:
regex_replace('#.*$','')
This is my Ansible task:
- name: get the custom job id
ansible.builtin.set_fact:
custom_job_id: >
"{{ train_custom_image_unmanaged_response.stderr_lines |select('search', 'describe') |list |regex_search('.*/customJobs/(\\d+)', '\\1') |first }}"
when: "(gcs_model_list.stdout is not defined) or ('saved_model.pb' not in gcs_model_list.stdout)"
I am getting "line too long" as Ansible lint error for custom_job_id line.
Any idea how can I break it down in smaller parts?
You can do it using YAML multi lines syntaxes, as you started doing it.
With this syntax, the indentation is what is defining a block, so, as long as you are indented inward of the fact name custom_job_id, all the following code is considered as being the expression that is going to be assigned to that fact.
For example:
- name: get the custom job id
ansible.builtin.set_fact:
custom_job_id: >-
{{
train_custom_image_unmanaged_response.stderr_lines
| select('search', 'describe')
| list
| regex_search('.*/customJobs/(\d+)', '\1')
| first
}}
when: >-
gcs_model_list.stdout is not defined
or 'saved_model.pb' not in gcs_model_list.stdout
Here is a playbook complying with the Ansible linting demonstrating this:
- hosts: localhost
gather_facts: true
tasks:
- name: Get the custom job id
ansible.builtin.set_fact:
custom_job_id: >-
{{
train_custom_image_unmanaged_response.stderr_lines
| select('search', 'describe')
| list
| regex_search('.*/customJobs/(\d+)', '\1')
| first
}}
when: >-
gcs_model_list.stdout is not defined
or 'saved_model.pb' not in gcs_model_list.stdout
vars:
train_custom_image_unmanaged_response:
stderr_lines:
- foo
- bar
- describe - /customJobs/123
- baz
gcs_model_list:
- name: Display `custom_job_id`
ansible.builtin.debug:
var: custom_job_id
Which yields:
PLAY [localhost] **********************************************************
TASK [Get the custom job id] **********************************************
ok: [localhost]
TASK [Display `custom_job_id`] ********************************************
ok: [localhost] =>
custom_job_id: '123'
In Ansible playbooks, We have the variable 'dns_name: xyz.abc.pqr.*.com' where as we have one template file called es_config.yml there the value of cname should be (cname: .abc.pqr..com)
How can we write jinja2 expression for this ?
dns_name: xyz.abc.com (Or) xyz.abc.pqr.***.com
cname: *.abc.com (Or) .abc.pqr.**.com (We have to use variable of dns_name)
Playbook
- hosts: elastic-search-servers
gather_facts: true
vars:
es_admin_hostname: test.develop123.com
tasks:
- name: split string
set_fact:
cname: "{{ es_admin_hostname.split('.') }} | first | replace('*')"
- name: debug
debug:
msg: "{{ cname[1:] }} is dns name"
Required Output
*.develop123.com
just change your split by regex_replaces:
- name: split string
set_fact:
cname: "{{ es_admin_hostname| regex_replace('^[^\\.]+', '*') }}"
- name: debug
debug:
msg: "{{ cname }} is dns name"
result:
ok: [localhost] => {
"msg": "*.develop123.com is dns name"
}
'^[^\\.]+' means trap all chars from beginning of string until i meet a dot and replace them by * (the \\ is needed because . is special char)
I need to replace all the / by \ in a string stored in a variable.
I'm just trying to do it a simple as possible to test it with a debug, but no matter how I try it I dont get the expected result of just replacing character to character. I think it's probably just a single/double quote problem or maybe the \ needs to be escaped in a certain way I don't know.
vars:
- SecGroup: '/stuff/foo/thing'
tasks:
- name: Display modified var
debug:
msg: "{{ SecGroup | replace('/','\') }}"
Expected output : \stuff\foo\thing
Output with differents tries :
- name: Display modified var
debug:
msg: "{{ SecGroup | replace('/','\') }}"
TASK [Display modified var]
ok: [localhost] => {
"msg": "stufffoothing"
}
- name: Display modified var
debug:
msg: "{{ SecGroup | replace('/','\\') }}"
TASK [Display modified var]
fatal: [localhost]: FAILED! => {"msg": "Unexpected failure during module execution."}
- name: Display modified var
debug:
msg: "{{ SecGroup | replace('/','\\\') }}"
TASK [Display modified var]
fatal: [localhost]: FAILED! => {"msg": "Unexpected failure during module execution."}
- name: Display modified var
debug:
msg: "{{ SecGroup | replace('/','\\\\') }}"
TASK [Display modified var]
ok: [localhost] => {
"msg": "\\\\stuff\\\\foo\\\\thing"
}
I also tried to revert the quotes :
- name: Display modified var
debug:
msg: '{{ SecGroup | replace("/","\") }}'
TASK [Display modified var]
fatal: [localhost]: FAILED! => {"msg": "Unexpected failure during module execution."}
I can't explain the output of this one
- name: Display modified var
debug:
msg: '{{ SecGroup | replace("/","\\") }}'
TASK [Display modified var]
ok: [localhost] => {
"msg": "\\\\stuff\\\\foo\\\\thing"
}
I think you've stumbled upon an edge case that involves the interaction between YAML escaping and Python escaping. The only way I was able to get it to work was introducing a guard character -- something to ensure that the \ isn't the last character in the expression, which we then remove with a subsequent replace() filter. Here I'm using a semicolon (;), but you could use anything that you're certain won't be in your SecGroup string. Note that your choice of quotes is significant; quoting the entire string with single quotes inhibits YAML escaping:
- name: With guard character
debug:
msg: '{{ SecGroup | replace("/","\;") | replace(";", "") }}'
Outputs:
TASK [With guard character] *******************************************************************************************************************************************************************
ok: [localhost] => {
"msg": "\\stuff\\foo\\thing"
}
Which is exactly what you want (remembering that a single \ is encoded as \\ in JSON output).
Regarding this:
- name: Display modified var
debug:
msg: '{{ SecGroup | replace("/","\\") }}'
TASK [Display modified var]
ok: [localhost] => {
"msg": "\\\\stuff\\\\foo\\\\thing"
}
You are successfully replacing / with two backslashes, \\. Since a backslash must be encoded as \\ in JSON output, a double backslash will end up represented as \\\\, so this:
"msg": "\\\\stuff\\\\foo\\\\thing"
Means you actually have the string:
\\stuff\\foo\\thing
I wanted to add an alternative solution:
If you're familiar with Python, you can just write a custom filter module and avoid multiple layers of escaping. E.g., if you were to create filter_plugins/reslash.py with the following content:
#!/usr/bin/python
def filter_reslash(val):
return val.replace('/', '\\')
class FilterModule(object):
filter_map = {
'reslash': filter_reslash
}
def filters(self):
return self.filter_map
You could then write your playbook like this:
---
- hosts: localhost
gather_facts: false
vars:
- SecGroup: '/stuff/foo/thing'
tasks:
- debug:
msg: "{{ SecGroup | reslash }}"
That's arguably a cleaner solution.
The solution by #larsks didn't entirely work for me as described. I needed to escape the backslash with double slashes \ plus the guard character in order for it to work in the Ansible Playbook.
This works: replace('/','\\;') | replace(';', '')
Another easy solution is to leave escaping backslash to ansible itself. This is how i would have done.
- set_fact:
replacer: '\'
- name: With guard character
debug:
msg: '{{ SecGroup | replace("/",replacer)}}'
Same workaround if you want replace 1 backslash with double backslash on a windows path.
- hosts: localhost
gather_facts: False
vars:
- iis_manager_logdir: 'C:\inetpub\logs\manager-logs'
tasks:
- set_fact:
iis_mng_logs: "{{ iis_manager_logdir | regex_replace('\\\\', '\\\\;') | regex_replace(';', '\\\\') }}"
- name: Original path
debug:
msg: "{{ iis_manager_logdir }}"
- name: New path
debug:
msg: "{{ iis_mng_logs }}"
Thanks to the #larsks's answer i've managed to replace backslashes in ansible string variable value without intermediate replace. It's possible by supplying into regex_replace expression a regex quantifier {1} between last backslash and closing quote.
For example, expression like {{ install_path | regex_replace('\\\\{1}', '/') }} replaces all occurences of backslash \ to forward slash /. It was used to replace Windows path delimiters with Unix-like ones:
- name: install libs
win_shell: "pip install --no-index --find-links \"file://{{ install_path | regex_replace('\\\\{1}', '/') }}/libs\" attrs requests"
become: true
For what its worth, after countless struggles, this is what has worked for me without any workarounds:
Forward to Back Slash
ForwardtoBackSlash: "{{ 'c:/test' | regex_replace('\\\/', '\\\\') }}"
output:
c:\test
Single Slash to Double Slash
SingleSlashtoDoble: "{{ 'C:\test\logs\logfile.txt'| regex_replace('\\\\', '\\\\\\\\') }}"
Output:
C:\\test\\logs\\logfile.txt
I hope it helps someone.
My Ansible Playbook:
#Tag --> B.6 -->
- name: Change the Security Realm to CustomRealm from ManagementRealm
command: /jboss-as-7.1.1.Final/bin/jboss-cli.sh --connect--command="/core-service=management/management-interface=http-interface:read-attribute(name=security-realm)"
register: Realm
- debug:
msg: "{{ Realm.stdout_lines }}"
The output for the above command in the message is as follows:
ok: [342f2f7bed8e] => {
"msg": [
"{",
" \"outcome\" => \"success\","
" \"result\" => \"ManagementRealm\"",
"}"
]
}
is there a way to just exact \"result\" => \"ManagementRealm\"".
I tried using the
Realm.stdout_lines.find('result')
but that fails, AWk & grep commands doesn't seem to be working here.
Any thoughts is greatly appreciated.
Thak you
I think there are a few ways you could handle this.
1) Grep the output before it gets to Ansible:
# Note the change of 'command' to 'shell'
- name: Change the Security Realm to CustomRealm from ManagementRealm
shell: /jboss-as-7.1.1.Final/bin/jboss-cli.sh --connect--command="/core-service=management/management-interface=http-interface:read-attribute(name=security-realm)" | grep -o 'result.*'
register: Realm
2) If the output from the source script is always 4 lines long, you can just grab the 3rd line:
# List indexes start at 0
- debug:
msg: "{{ Realm.stdout_lines[2] | regex_replace('^ *(.*$)', '\\1') }}"
3) The nicest way if you have an option to modify jboss-cli.sh, would be to get the jboss-cli.sh to output valid JSON which can then be parsed by Ansible:
# Assuming jboss-cli.sh produces {"outcome": "success", "result": "ManagementRealm"}
- set_fact:
jboss_data: "{{ Realm.stdout | from_json }}"
- debug:
var: jboss_data.result