Ansible command quotes - command

I would like to have the following command integrated in an Ansible playbook task:
cut -f 1 -d: /etc/passwd | xargs -n 1 -I {} bash -c ' echo -e "\n{}" ; chage -l {}'.
Any quote inside breaks the whole command. How I can avoid it to make it run the whole string?
Many thanks in advance.

You can simply use the YAML literal block string syntax. In that way you don't need to escape any quotes. Instead, you can pass your shell command as is.
Example:
- name: test task
shell:
cmd: |
cut -f 1 -d: /etc/passwd | xargs -n 1 -I {} bash -c ' echo -e "\n{}" ; chage -l {}'
tags: test

You can escape them with \”
example: "hello=\"hi\""

Related

Problems with SED in Ansible playbook

I'm installing Docker on some hosts and using Ansible playbook to do this. However, we have a startup script for Consul that breaks when docker is installed, as Docker adds a virtual NIC and adds an extra value to the variable.
Original Variables
NODEIP=`hostname -I`
NODE=`hostname -I |sed 's/[.]/-/g'`
I can manually change them to the following and this works.
NODEIP=$(hostname -I | grep -o "[^ ]\\+" | awk /^10\./"{print $1}")
NODE=$(hostname -I | grep -o "[^ ]\+" | awk /^10\./"{print $1}" |sed "s/[.]/-/g")
However, I need to add these to an Ansible playbook. I've modified the variable for NODE and it gets updated in the script, but NODEIP does not. See sample playbook code below.
name: Fix consul startup script for Docker virtual network interface
shell: sed -i 's/NODEIP=`hostname -I`/s_.*_NODEIP=$(hostname -I | grep -o \"[^ ]\\+\" | awk /^10\./\"{print \$1}\")' filename
shell: sed -i '/NODE=`hostname -I |sed/s_.*_NODE=$(echo $NODEIP|sed 's/[.]/-/g')_' filename
I'm going insane trying to get this to work properly. Can anyone help?
Whenever you run an ansible-playbook, it will gather_facts by default. This task will populate your ansible-playbook execution with the variables listed here: Ansible facts
In your case, you are looking for:
NODEIP={{ ansible_default_ipv4.address }}
NODE={{ ansible_hostname }}
Below code should solve your requirement
- name: Command for NodeIP
shell: hostname -I | grep -o "[^ ]\\+" | awk /^10\./"{print $1}"
register: NODEIP
- name: Command for NodeName
shell: hostname -I | grep -o "[^ ]\+" | awk /^10\./"{print $1}" |sed "s/[.]/-/g"
register: NODE
Above code will store command output to NODEIP and NODE variables respectively.
To learn more about usage of ansible return values, refer https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html
Thanks to all for the input. Here's what I ended up coming up with (had to escape some characters with backslashes). Delighted it works
- name: replace NODEIP variable in consul startup script
lineinfile:
path: filename
regexp: '^NODEIP='
line: "NODEIP=$(hostname -I | grep -o \"[^ ]\\+\" | awk /^10\\./\"{print $1}\")"
backrefs: yes
- name: replace NODE variable in consul startup script
lineinfile:
path: filename
regexp: '^NODE='
line: "NODE=$(hostname -I | grep -o \"[^ ]\\+\" | awk /^10\\./\"{print $1}\" |sed \"s/[.]/-/g\")"
backrefs: yes

xargs lines containing -e and -n processed differently

When running the following command with xargs (GNU findutils) 4.7.0
xargs -n1 <<<"-d -e -n -o"
I get this output
-d
-o
Why is -e and -n not present in the output?
From man xargs:
[...] and executes the command (default is /bin/echo) [...]
So it runs:
echo -d
echo -e
echo -n
echo -o
But from man echo:
-n do not output the trailing newline
-e enable interpretation of backslash escapes
And echo -n outputs nothing, and echo -e outputs one empty newlines that you see in the output.

Command substitution in fish

I'm trying to migrate this working command
docker-compose $(find docker-compose* | sed -e "s/^/-f /") up -d --remove-orphans
from bash to fish. The intention of this command is to get this
docker-compose -f docker-compose.backups.yml ... -f docker-compose.wiki.yml up -d --remove-orphans
My naive try
docker-compose (find docker-compose* | sed -e "s/^/-f /") up -d --remove-orphans
is not working, though. The error is:
ERROR: .FileNotFoundError: [Errno 2] No such file or directory: './ docker-compose.backups.yml'
What is the correct translation?
The difference in behavior is due to the fact fish, sanely, only splits the output of a command capture on line boundaries. Whereas POSIX shells like bash split it on whitespace by default. That is, POSIX shells split the output of $(...) on the value of $IFS which is space, tab, and newline by default.
There are several ways to rewrite that command so it works in fish. The one that requires the smallest change is to change the sed to insert a newline between the -f and the filename:
docker-compose (find docker-compose* | sed -e "s/^/-f\n/") up -d --remove-orphans

How to replace < and > symbols in a file with \< and \> respectively with sed inside Jenkins Groovy?

I have a file named body.txt which contains the following:
<table><tr><td>Hello</td><td>World</td></tr></table>
I want to put \ in front of each < and > so that the file body.txt contains the following:
\<table\>\<tr\>\<td\>Hello\</td\>\<td\>World\</td\>\</tr\>\</table\>
I am trying to do this from inside a Jenkins Groovy script.
I tried the following approaches:
Approach 1:
sh "sed -i 's/</\\</g' body.txt"
sh "sed -i 's/</\\</g' body.txt"
Approach 2:
sh '''
#!bin/bash
sed -i "s/</\\</g" body.txt
sed -i "s/>/\\>/g" body.txt
'''
Approach 3:
env.lt="<"
env.lts="\\<"
env.gt=">"
env.gts="\\>"
sh '''
#!bin/bash
sed -i "s/${lt}/${lts}/g" body.txt
sed -i "s/${gt}/${gts}/g" body.txt
'''
Approach 4:
env.lt="<"
env.lts="\\<"
env.gt=">"
env.gts="\\>"
sh "sed -i 's/${lt}/${lts}/g' body.txt"
sh "sed -i 's/${gt}/${gts}/g' body.txt"
Approach 5:
sh "cat body.txt |tr '<' '\\<' > body1.txt"
sh "cat body1.txt|tr '>' '\\>' > body2.txt"
sh "cp body2.txt body.txt"
sh "rm body1.txt body2.txt"
None of these approaches are working.
I am not getting any error, but replacement of < and > symbols are not happening.
I think this is what you want. You need to use gsub in awk to be able to perform the replacement. You also need to put two of \ when you need to replace e.g. > with \>.
cat body.txt | awk '{gsub (/>/,"\\>");print}' | awk '{gsub (/</,"\\<");print}'
Another way using sed :
cat body.txt | sed 's,>,\\>,g' | sed 's,<,\\<,g'
The below command worked for me that ran inside Jenkins Groovy declarative pipeline script:
#!/bin/bash
cat body.txt | awk '{gsub (/>/,"\\\\>");print}' | awk '{gsub (/</,"\\\\<");print}'

using here document and pipeline of sh at the same time

I'm using here document of sh to run some commands. now I want to parse the output of those commands using awk. However, everytime I execute it, I get the output of the command append with something like this "% No such child process"
This is how my script looks like.
#!/bin/sh
com = "sudo -u username /path/of/file -l"
$com <<EOF | awk '{print $0}'
Commands.
.
.
.
EOF
How am I going to use heredoc and pipeline without appending that unwanted string?
Thanks
Your variable assignment is wrong in a couple of ways. First, you aren't actually assigning a variable; you're trying to run a command named com whose arguments are = and a string "sudo ...". Spaces must not be used on either side of the =:
com="sudo ..."
Second, command lines should not be stored in a variable; the shell's parser can only make that work they way you intend for very simple commands. Type the command out in full, or use a shell function.
com () {
sudo -u username /path/to/file -l
}
com <<EOF | awk '{print $0}'
...
EOF
There's no problem, check :
$ cat <<EOF | awk '{print $1}'
a b c
1 2 3
EOF
a
1