Can I concatenate aliases in YAML? - yaml

I would like to do something like:
opt-flags : &opt_flags -DCMAKE_BUILD_TYPE=Release
dbg-flags : &dbg_flags -DCMAKE_BUILD_TYPE=Debug
common-flags: &common -DENABLE_EXAMPLES=ON -DENABLE_TESTS=ON
# concatenate previous definitions to create composed definitions
dbg: *common *dbg_flags
opt: *common *opt_flags
This doesn't work directly. Is it possible to do something equivalent to this in YAML?

No you cannot do that, an alias replaces a complete node.
However if you are dealing with mappings, you can use the merge key language-independent type if your parser supports it to combine multiple sets of keys into a new mapping:
opt-flags : &opt_flags -DCMAKE_BUILD_TYPE=Release
dbg-flags : &dbg_flags -DCMAKE_BUILD_TYPE=Debug
common-flags: &common -DENABLE_EXAMPLES=ON -DENABLE_TESTS=ON
dbg:
<< : [*common_flags, *dbg_flags]
opt:
<< : [*common_flags, *opt_flags]
This however will make two entries each, and not concatenate the strings scalars that are anchored, and will need a program that can combine the multiple values, for which the ordering is not guaranteed.

Unfortunately in 2022 you still cannot concatenate or join aliases with other aliases or strings. For mappings, there is another syntax which works the same as "merge keys" (described in the accepted answer) but is less ambiguous and easier to read IMO. You can reference multiple anchors like so (this works in docker-compose btw):
x-foo: &foo
VAR1: value1
x-bar: &bar
VAR2: value2
foobar:
<<: *foo
<<: *bar
# foobar:
# VAR1: value1
# VAR2: value2
Also worth noting that you can nest anchors too:
x-foo: &foo
VAR1: value1
bar: &bar
VAR2: value2
foobar:
<<: *foo
bar:
<<: *bar
VAR3: value3
# foobar:
# VAR1: value1
# bar:
# VAR2: value2
# VAR3: value3

Related

yq append multiple file into list yaml

Assume I have a file root.yml
keyA: valA
keyB: valB
myList:
Then I receive some yml file, such as
1.yml
project_id: abc
description: xyz
2.yml
project_id: cba
description: zyx
And so on (they may stored in same folder)
Now I want to append the content of 1.yml, 2.yml (and so on) to the myList of root.yml and output to console
Expected:
keyA: valA
keyB: valB
myList:
- project_id: abc
description: xyz
- project_id: cba
description: zyx
- (so on...)
I have searched some examples but they hard code the list item in the yq command, like this post: Stack Overflow
But I want it load from files, not from hard code
Please forgive for my bad english
With mikefarah/yq, you could use the load function with the filename of the YAML files to be included, i.e. with your example
yq '.myList += [ load("1.yaml"), load("2.yaml") ]' root.yml
producing a YAML result as
keyA: valA
keyB: valB
myList:
- project_id: abc
description: xyz
- project_id: cba
description: zyx
As indicated in your comment, if one of the object has a parent structure and you want to extract the element from it, you can do
yq 'load("1.yaml") as $f | .myList += [ $f.config[], load("2.yaml") ]' root.yml
Tested on yq version 4.27.2

yq: Add new value to list in alphabetical order

I have a simple yaml file called foo.yaml
foo:
- a
- c
bar:
- foo: bar
foo2: bar2
I'm trying to add a new value (b) to foo, in alphabetical order. I can add the value with +=, but it doesn't get alphabatized
$ yq '.foo += "b"' foo.yaml
foo:
- a
- c
- b
bar:
- foo: bar
foo2: bar2
If I use + I can use sort, but I only get the raw values. e.g.:
$ yq '.foo + "b" | sort()' foo.yaml
- a
- b
- c
I tried to set this into a bash variable and then use it with =, but it appears as a multi-line text
$ variable=$(yq '.foo + "b" | sort()' foo.yaml)
$ yq ".foo = \"$variable\"" foo.yaml
foo: |-
- a
- b
- c
bar:
- foo: bar
foo2: bar2
Is there an easier way to insert a new value into foo alphabetically, while keeping the rest of the yaml in tact?
The reason you are getting the raw values is that you've told yq to traverse into 'foo'. Instead try:
yq '.foo = (.foo + "b" | sort)' file.yaml
yields:
foo:
- a
- b
- c
bar:
- foo: bar
foo2: bar2
Explanation:
you need to update the entry in 'foo'
then, in brackets, set the new value. Normally you can use +=, but because you want to sort I've used '='
Disclaimer: I wrote yq

How to upsert an array with yq

Consider the following hello.yaml:
foos:
- foo: foo1
bar: hello
- foo: foo2
bar: world
If I want to update the bar value where foo = "foo1", I can invoke the following command:
yq '( .foos[] | select(.foo == "foo1") | .bar) |= "goodbye cruel"' hello.yaml
And that correctly outputs:
foos:
- foo: foo1
bar: goodbye cruel
- foo: foo2
bar: world
However, if I do not know that I have an item that matches, I would like to insert the appropriate entries e.g. something like yq '( .foos[] | select(.foo == "foo3") | .bar) ... would output
foos:
- foo: foo1
bar: hello
- foo: foo2
bar: world
- foo: foo3
bar: goodbye cruel
Is there a way in yq to "upsert" the array, or do I have to evaluate if the key exists upfront and perform one of two commands to insert or update?
Many thanks
Like Inian said; there is no upsert operation (at the moment). This is how I would do it - not sure if there is a better way?
yq '
with(.foos ;
select( all_c(.foo != "foo3")) | . += {"foo": "foo3"}
) |
(.foos[] | select(.foo == "foo3") | .bar) = "cool"
' hello.yaml
Explanation:
In the with block, match arrays that don't have foo: foo3, and add it.
Next, find all the elements with foo: foo3 and update them.
Disclaimer: I wrote yq

How can I add an indented line in the end of a yaml file with bash?

I have a file test.yaml with content:
'12345'
key1: 'foo'
key2: 'bar'
I have a system/env variable called ENV1 with value "baz"
How can I have this file as an outcome with bash?
'12345':
key1: 'foo'
key2: 'bar'
key3: 'baz'
$ sed "\$a\ key3: '$ENV1'" file
'12345'
key1: 'foo'
key2: 'bar'
key3: 'baz'
or set -i for in place.
Another alternative
$ cat file <(echo " key3: '$ENV1'")
I think this the easiest approach (this will write to the file):
echo " key3: '$ENV1'" >> file
If just want to print appending the content of the environment variable (not modifying the file)
cat file <(echo " key3: '$ENV1'")

Use of variable name in shell script

First of all I'm sorry for a probable bad title, but I don't even know what to call this.
I'm trying the following:
#!/bin/sh
VAR1="28-00000202070c"
VAR2="28-0000018776d3"
VAR3="28-0000033a6174"
for sensor in VAR1 VAR2 VAR3
do
echo "$sensor: $$sensor"
done
The expected output would be:
VAR1: 28-00000202070c
VAR2: 28-0000018776d3
VAR3: 28-0000033a6174
The real output is:
VAR1: 24038sensor
VAR2: 24038sensor
VAR3: 24038sensor
and the strange prefix number keeps growing...
VAR1: 24039sensor
VAR2: 24039sensor
VAR3: 24039sensor
...
I'd like to ask:
1) What are the correct terms/keywords that describe what I'm trying to do here
2) How to get to the expected output
Thanks,
Joaoabs
This is something that sh does not support, while bash does.
The correct syntax you should use is:
echo "$sensor ${!sensor}"
Test
$ cat a
#!/bin/bash <----- note I changed /bin/sh to /bin/bash
VAR1="28-00000202070c"
VAR2="28-0000018776d3"
VAR3="28-0000033a6174"
for sensor in VAR1 VAR2 VAR3
do
echo "$sensor ${!sensor}"
done
$ ./a
VAR1 28-00000202070c
VAR2 28-0000018776d3
VAR3 28-0000033a6174

Resources