Parse YAML using shell - parsing

I have a yaml something of this sort and need to parse and get the value of 'url' here.
name: Yaml_Test
servers:
- name: host_ip
host: host_name
port: 443
scheme: https
variables:
- constant:
name: url
value: https://url_here

With the current versions of both kislyuk/yq and mikefarah/yq, use select to filter the array items:
.variables[].constant | select(.name == "url").value
https://url_here
Using kislyuk/yq, you may want to add the -r option to output raw text:
yq -r '…' file.yaml
Using an older version of mikefarah/yq, you need to specify the command e or eval:
yq e '…' file.yaml
Edit: Although not recommended, here's as requested in a comment a grep-only solution which heavily relies on how the YAML document is represented textually, based on the sample provided (i.e. no other occurrence of name: url etc.):
grep -A1 'name: url' file.yaml | grep -o 'http.*'
https://url_here

Related

How to convert system.string to a win module path type

My overall goal is to determine whether or not the file control.lnk exists in the Windows administrator's desktop. The challenge of this (as i currently see it) is to get the complete path/file spec of such into win_stat as a valid path argument. Originally, i used variations of $env:USERPROFILE and appended \Desktop\control.lnk to such but got system.string is not path errors (dialog to that effect). Lastly, i tried to derive this path into an Ansible variable via Powershell task and then use the ansible variable ...
- name: Get Profile path
# This will get logon user path; different than ansible_environment path
ansible.windows.win_powershell:
script:
$env:USERPROFILE
register: profilePath
- name: Debug Play essentials
debug:
msg:
- "UserProfile path: {{ profilePath.output}}"
... and this produces the path as i expect. However (in all cases), i still get errors of the form:
msg: argument for path is of type System.String and we were unable to convert to path: string value contains invalid path characters, cannot convert to path" }
Here's the win_stat task i've been trying along with the path-failures I've tried as comments ...
` - name: Are Shortcuts installed
ansible.windows.win_stat:
# This is a typical without the variable ...
# path: C:\Users\Administrator.PAxAnsible\Desktop\Control.lnk
# Using $env:USERPROFILE directly doesn't work; is resolved as type system.string and not of type path | cannot covert ...
# $env:USERPROFILE\Desktop\Control.lnk
# '$env:USERPROFILE\Desktop\Control.lnk'
# "$env:USERPROFILE\Desktop\Control.lnk" -invalid syntax; cannot commit
# "$env:USERPROFILE\\Desktop\\Control.lnk"
# '$env:USERPROFILE\\Desktop\\Control.lnk'
# '"$env:USERPROFILE\\Desktop\\Control.lnk"'
# $env:USERPROFILE\\Desktop\\Control.lnk
# "$env:USERPROFILE/Desktop/Control.lnk"
# $env:USERPROFILE/Desktop/Control.lnk
# '"$env:USERPROFILE"/Desktop/Control.lnk' -string contains invalid path characters, cannot convert to path"
# '$env:USERPROFILE + /Desktop/Control.lnk'
# $env:USERPROFILE + '/Desktop/Control.lnk'
# Template error ...
# '"{{ $env:USERPROFILE}}"/Desktop/Control.lnk' -unexpected char '$' at 4.
#
# System.String issues while using a derived path (>> is how it's translated) ...
# '{{ profilePath.output}}/Desktop/Control.lnk' >> "['C:\\\\Users\\\\Administrator']/Desktop/Control.lnk"
# '{{ profilePath.output}}\\Desktop\\Control.lnk' >> "['C:\\\\Users\\\\Administrator']\\\\Desktop\\\\Control.lnk"
# "{{ profilePath.output}}\Desktop\Control.lnk" >> syntax error; cannot community
# '"{{ profilePath.output}}\Desktop\Control.lnk"' >> "\"['C:\\\\Users\\\\Administrator']\\Desktop\\Control.lnk\""
# "{{ profilePath.output}}\\Desktop\\Control.lnk" >> "['C:\\\\Users\\\\Administrator']\\Desktop\\Control.lnk"
path: '"{{ profilePath.output}}"\\Desktop\\Control.lnk'
register: Shortcuts
when: AppServShortcuts != "none"
I see that the bordering brackets as are of the output are not being removed when combined with the static text as is within the path argument. -I suspect this will have to be done before using the output variable as a component of the path argument. As for using $env:USERPROFILE directly as a path component, I haven't figured out a syntax that works.
I'm hoping someone out there can advise correct syntax to make this win_stat query work ;) ...

grep between pattern and exclude start/end pattern in output

I have file.txt from which I need to grep first occurrence of a pattern. How can I grep and get matched string only ':whitespace' and 'end of line'
I m trying below command
cat file.txt | grep -m1 -P "(:\s+).*ccas-apache$"
But it gives me
name: nginx-ccas-apache
and I want is
nginx-ccas-apache
file.txt
pod: nginx-ccas-apache-0
name: nginx-ccas-apache
image: myregnapq//ccas_apache
name: nginx-filebeat
pod: nginx-ccas-apache-1
name: nginx-ccas-apache
image: myregnapq/ccas_apache
name: nginx-filebeat
Another approach using sed:
sed -En '/^[[:space:]]+name:[[:space:]](.*ccas-apache)$/{s//\1/p;q}' file.txt
Explanation
-En Use extended regexp with -E and prevent the default printing of a line by sed with -n
/^[[:space:]]+name:[[:space:]](.*ccas-apache)$/ The pattern that specifies what to match
If the previous pattern matched, run commands between the curly brackets
s//\1/p Use the last matched pattern with // and replace with group 1. Then print the pattern space with p
q exit sed
The regex matches:
^ Start of string
[[:space:]]+name:[[:space:]] Match name: with leading spaces and single space after
(.*ccas-apache) Capture group 1, match optional chars and ccas-apache
$ End of string
Output
nginx-ccas-apache
Note that you don't have to use cat
See an online demo.
Using grep
$ grep -Pom1 'name: \K.*$' file.txt
nginx-ccas-apache
You can use awk, too:
awk -F: '/:[[:space:]].*ccas-apache$/{sub(/^[[:space:]]+/, "", $2); print $2; exit}' file
Details:
-F: - a colon is used as a field separator
:[[:space:]].*ccas-apache$ - searches for a line with :, a whitespace, then any text, ccas-apache at the end of the string, and once found
sub(/^[[:space:]]+/, "", $2) - remove the initial whitespaces from Field 2
print $2 - then print the Field 2 value
exit - stop processing the file.
See the online demo:
#!/bin/bash
s='pod: nginx-ccas-apache-0
name: nginx-ccas-apache
image: myregnapq//ccas_apache
name: nginx-filebeat
pod: nginx-ccas-apache-1
name: nginx-ccas-apache
image: myregnapq/ccas_apache
name: nginx-filebeat'
awk -F: '/:[[:space:]].*ccas-apache$/{sub(/^[[:space:]]+/, "", $2); print $2; exit}' <<< "$s"
Output: nginx-ccas-apache
INPUT
pod: nginx-ccas-apache-0
name: nginx-ccas-apache
image: myregnapq//ccas_apache
name: nginx-filebeat
pod: nginx-ccas-apache-1
name: nginx-ccas-apache
image: myregnapq/ccas_apache
name: nginx-filebeat
CODE
enter any properly-escaped pattern for __ that includes the string tail $
3 ways of saying the same thing
any one solution works in gawk, mawk-1, mawk-2, or macos nawk
mawk '_{exit} _=$(NF=NF)~__' FS='^.*[ \t]' __='ccas-apache$' OFS=
or
gawk '_{exit} NF*=_=$(NF)~__' FS='^.*[ \t]' __='ccas-apache$' OFS=
or
nawk '_{exit} _=NF*=$NF~__' FS='^.*[ \t]' __='ccas-apache$' OFS=
OUTPUT
nginx-ccas-apache
GENERIC SOLUTION
not just at the tail
this time enter pattern at FS
CODE
{m,g}awk '_{exit} _=(!_<NF)*sub("[^ \t]*"(FS)"[^ \t]*","\4&\4")*\
gsub("^[^\4]*\4|\4[^\4]*$","")' FS='your_pattern_here'
OUTPUT
FS='image'
>>> `image:`
FS='myregnapq'
>>> `myregnapq//ccas_apache`
With your shown samples please try following awk code.
awk -F':[[:space:]]+' '
$1~/^[[:space:]]+name$/ && $2~/^[^-]*-ccas-apache$/{
print $2
exit
}
' Input_file
Explanation: Simple explanation would be, setting field separator as colon followed by space(1 or more occurrences). In main program checking condition if first field matches regex starts with space followed by name AND 2nd field matches regex ^[^-]*-ccas-apache$ then printing 2nd field of that line and `exit from program.

Extract environment variables from grep search

I'd like to generate some .env.example files for a dozen of projects.
In order to be smart and avoid tedious copy/pasting I thought about using the grep command.
How can I get from :
➜ app-service git:(chore/adding-env-example) ✗ c;grep -r "process\.env\." --exclude-d
ir=node_modules
./src/config/config_prod.js: level: process.env.LOG_LEVEL || 'error'
./src/config/config_prod.js: bypassToken: process.env.BYPASS_TOKEN || null
./src/config/config.js: port: process.env.APP_PORT || 3000,
./src/config/config.js: frontUrl: process.env.FRONT_URL,
./src/config/config.js: launchAppTopic: process.env.AWS_SNS_LAUNCH_APP_ARN
...
to
LOG_LEVEL=
BYPASS_TOKEN=
APP_PORT=
FRONT_URL=
AWS_SNS_LAUNCH_APP_ARN=
?
Retrieving the Environment Variables:
grep -oP "(?<=\.)[[:upper:]_]*(?=[ ,]*)"
Results:
LOG_LEVEL
BYPASS_TOKEN
APP_PORT
FRONT_URL
AWS_SNS_LAUNCH_APP_ARN
Retrieving the Environment Variables and adding =
sed -r -n 's|^.*\.([[:upper:]_]+).*$|\1=|p'
Results:
LOG_LEVEL=
BYPASS_TOKEN=
APP_PORT=
FRONT_URL=
AWS_SNS_LAUNCH_APP_ARN=
If you want to find all occurrences of an uppercase letter followed by uppercase letters and/or underscores, and then an =, and only if it is a word in itself:
grep -o -P '\b[A-Z][A-Z_]*='

How to edit "Version: xxx" from a script to automate a debian package build?

The Debian control file has a line like this (among many others):
Version: 1.1.0
We are using jenkins to build our application as a .deb package. in Jenkins we are doing something like this:
cp -r $WORKSPACE/p1.1/ourap/scripts/ourapp_debian $TARGET/
cd $TARGET
fakeroot dpkg-deb --build ourapp_debian
We would like to do shomething like this in our control file:
Packages: ourapp
Version: 1.1.$BUILD_NUMBER
but obviously this is not possible.
So we need something like a sed script to find the line starting with Version: and replace anything after it with a constant plus the BUILD_NUMBER env var which Jenkins creates.
We have tried things like this:
$ sed -i 's/xxx/$BUILD_NUMBER/g' control
then put "Version: xxx" in our file, but this doesn't work, and there must be a better way?
Any ideas?
We don't use the change-log, as this package will be installed on servers which no one has access to. the change logs are word docs given to the customer.
We don't use or need any of the Debian helper tools.
Create two files:
f.awk
function vp(s) { # return 1 for a string with version info
return s ~ /[ \t]*Version:/
}
function upd() { # an example of version number update function
v[3] = ENVIRON["BUILD_NUMBER"]
}
vp($0) {
gsub("[^.0-9]", "") # get rid of everything but `.' and digits
split($0, v, "[.]") # split version info into array `v' elements
upd()
printf "Version: %s.%s.%s\n", v[1], v[2], v[3]
next # done with this line
}
{ # print the rest without modifications
print
}
f.example
rest1
Version: 1.1.0
rest2
Run the command
BUILD_NUMBER=42 awk -f f.awk f.example
Expected output is
rest1
Version: 1.1.42
rest2
With single quote:
sed -ri "s/(Version.*\.)[0-9]*/\1$BUILD_NUMBER/g" <control file>
OR
sed -ni "/Version/{s/[0-9]*$/$BUILD_NUMBER/};p" <control file>

why ansible always replaces double quotes with single quotes in templates?

I am trying to generate Dockerfiles with Ansible template - see the role source and the template in Ansible Galaxy and Github
I need to genarate a standard Dockerfile line like:
...
VOLUME ["/etc/postgresql/9.4"]
...
However, when I put this in the input file:
...
instruction: CMD
value: "[\"/etc/postgresql/{{postgresql_version}}\"]"
...
It ends up rendered like:
...
VOLUME ['/etc/postgresql/9.4']
...
and I lose the " (which renders the Dockerfiles useless)
Any help ? How can I convince Jinja to not substitute " with ' ? I tried \" , |safe filter, even {% raw %} - it just keeps doing it!
Update:
Here is how to reproduce the issue:
Go get the peruncs.docker role from galaxy.ansible.com or Github (link is given above)
Write up a simple playbook (say demo.yml) with the below content and run: ansible-playbook -v demo.yml. The -v option will allow you to see the temp directory where the generated Dockerfile goes with the broken content, so you can examine it. Generating the docker image is not important to succeed, just try to get the Dockerfile right.
- name: Build docker image
hosts: localhost
vars:
- somevar: whatever
- image_tag: "blabla/booboo"
- docker_copy_files: []
- docker_file_content:
- instruction: CMD
value: '["/usr/bin/runit", "{{somevar}}"]'
roles:
- peruncs.docker
Thanks in advance!
Something in Ansible appears to be recognizing that as valid Python, so it's getting transformed into a Python list and then serialized using Python's str(), which is why you end up with the single-quoted values.
An easy way to work around this is to stick a space at the beginning of the value, which seems to prevent it from getting converted into Python:
- name: Build docker image
hosts: localhost
vars:
- somevar: whatever
- image_tag: "blabla/booboo"
- docker_copy_files: []
- docker_file_content:
- instruction: CMD
value: ' ["/usr/bin/runit", "{{somevar}}"]'
roles:
- peruncs.docker
This results in:
CMD ["/usr/bin/runit", "whatever"]

Resources