yq/usage/tips-and-tricks.md

200 lines
6.2 KiB
Markdown
Raw Normal View History

2021-10-30 03:14:39 +00:00
# Tips, Tricks, Troubleshooting
## Validating yaml files
Yaml files can be surprisingly lenient in what can be parsed as a yaml file. A reasonable way of validation a yaml file is to ensure the top level is a map or array (although it is valid yaml to have scalars at the top level, but often this is not what you want). This can be done by:
```
2022-01-28 01:50:13 +00:00
yq --exit-status 'tag == "!!map" or tag== "!!seq"' file.txt > /dev/null
2021-10-30 03:14:39 +00:00
```
2021-12-05 00:11:59 +00:00
## Split expressions over multiple lines to improve readability
2021-10-30 03:14:39 +00:00
Feel free to use multiple lines in your expression to improve readability.
2021-12-05 00:11:59 +00:00
Use `with` if you need to make several updates to the same path.
2021-10-30 03:14:39 +00:00
```bash
2022-01-28 01:50:13 +00:00
yq --inplace '
2021-12-05 00:11:59 +00:00
with(.a.deeply.nested;
. = "newValue" | . style="single") |
with(.b.another.nested;
. = "cool" | . style="folded")
2021-10-30 03:14:39 +00:00
' my_file.yaml
```
## Create bash array
Given a yaml file like
```yaml
coolActions:
- create
- edit
- delete
```
You can create a bash array named `actions` by:
```bash
2022-01-28 01:50:13 +00:00
> readarray actions < <(yq '.coolActions[]' sample.yaml)
2021-10-30 03:14:39 +00:00
> echo "${actions[1]}"
edit
```
## Set contents from another file
2022-01-30 21:23:09 +00:00
Use the [load](https://mikefarah.gitbook.io/yq/operators/load) operator to load contents from another file.
2021-12-05 01:15:19 +00:00
2021-10-30 03:14:39 +00:00
## Special characters in strings
The `strenv` operator is a great way to handle special characters in strings:
```bash
2022-01-28 01:50:13 +00:00
VAL='.a |!@ == "string2"' yq '.a = strenv(VAL)' example.yaml
2021-10-30 03:14:39 +00:00
```
## String blocks and newline issues
2022-07-21 22:52:45 +00:00
There are a couple of tricks to getting the right string representation, take a look at [string operators](https://mikefarah.gitbook.io/yq/operators/string-operators#string-blocks-bash-and-newlines) for more details:
2021-10-30 03:14:39 +00:00
## Quotes in Windows Powershell
Powershell has its [own](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about\_quoting\_rules?view=powershell-7.1) way of handling quotes:
```bash
2022-01-28 01:50:13 +00:00
PS > yq -n '.test = ""something""'
2021-10-30 03:14:39 +00:00
test: something
PS >
```
See [https://github.com/mikefarah/yq/issues/747](https://github.com/mikefarah/yq/issues/747) for more trickery.
2021-10-30 03:14:39 +00:00
## Merge / combine all documents into one
To merge all given yaml files into one, use the `reduce` operator with the `*` (multiply) operator. Note the use of `ea` or `eval-all` to load all files into memory so that they can be merged.
```
yq ea '. as $item ireduce ({}; . * $item )' file1.yml file2.yml ...
```
2022-03-29 02:35:30 +00:00
## Merge - showing the source file and line
To see the original source file and line number of your merged result, you can pre-process the files and add that information in as line comments, then perform the merge.
```bash
yq ea '(.. lineComment |= filename + ":" + line) | select(fi==0) * select(fi==1)' data1.yaml data2.yaml
```
2021-11-29 10:06:49 +00:00
## Merge an array of objects by key
See [here](https://mikefarah.gitbook.io/yq/operators/multiply-merge#merge-arrays-of-objects-together-matching-on-a-key) for a working example.
2021-10-30 03:14:39 +00:00
## Creating a new file / working with blank documents
To create a new `yaml` file simply:
```
2022-01-28 01:50:13 +00:00
yq -n '.someNew="content"' > newfile.yml
2021-10-30 03:14:39 +00:00
```
## Comparing yaml files
The best way to run a diff is to use `yq` to normalise the yaml files and then just use diff. Here is a simple example of using pretty print `-P` to normalise the styling and running diff:
```
2022-01-28 01:50:13 +00:00
diff <(yq -P 'sort_keys(..)' file1.yaml) <(yq -P 'sort_keys(..)' file2.yaml)
2021-10-30 03:14:39 +00:00
```
2021-12-05 00:11:59 +00:00
This way you can use the full power of `diff` and normalise the yaml files as you like.
You may also want to remove all comments using `... comments=""`
2021-10-30 03:14:39 +00:00
## Reading multiple streams (STDINs)
Like `diff` and other bash commands, you can use `<(exp)` to pipe in multiple streams of data into `yq`. instance:
```
2022-01-28 01:50:13 +00:00
yq '.apple' <(curl -s https://somewhere/data1.yaml) <(cat file.yml)
2021-10-30 03:14:39 +00:00
```
## Updating deeply selected paths
2021-12-05 00:11:59 +00:00
### or why is yq only returning the updated yaml
2021-10-30 03:14:39 +00:00
The most important thing to remember to do is to have brackets around the LHS expression - otherwise what `yq` will do is first filter by the selection, and then, separately, update the filtered result and return that subset.
```
yq '(.foo.bar[] | select(name == "fred) | .apple) = "cool"'
```
## Combining multiple files into one
In order to combine multiple yaml files into a single file (with `---` separators) you can just:
```
2022-01-28 01:50:13 +00:00
yq '.' somewhere/*.yaml
2021-10-30 03:14:39 +00:00
```
## Multiple updates to the same path
2021-10-30 03:14:39 +00:00
You can use the [with](../operators/with.md) operator to set a nested context:
```
2022-01-28 01:50:13 +00:00
yq 'with(.a.deeply ; .nested = "newValue" | .other= "newThing")' sample.yml
2021-10-30 03:14:39 +00:00
```
The first argument expression sets the root context, and the second expression runs against that root context.
2022-04-01 02:41:39 +00:00
## Logic without if/elif/else
`yq` has not yet added `if` expressions - however you should be able to use `with` and `select` to achieve the same outcome. Lets use an example:
```yaml
- animal: cat
- animal: dog
- animal: frog
```
Now, if you were using good ol' jq - you may have a script with `if`s like so:
```bash
jq ' .[] |=
if (.animal == "cat") then
.noise = "meow" |
.whiskers = true
elif (.animal == "dog") then
.noise = "woof" |
.happy = true
else
.noise = "??"
end
' < file.yaml
```
Using `yq` - you can get the same result by:
```bash
yq '.[] |= (
with(select(.animal == "cat");
.noise = "meow" |
.whiskers = true
) |
with(select(.animal == "dog");
.noise = "woof" |
.happy = true
) |
with(select(.noise == null);
.noise = "???"
)
)' < file.yml
```
Note that the logic isn't quite the same, as there is no concept of 'else'. So you may need to put additional logic in the expressions, as this has for the 'else' logic.
2021-10-30 03:14:39 +00:00
## yq adds a !!merge tag automatically
The merge functionality from yaml v1.1 (e.g. `<<:`has actually been removed in the 1.2 spec. Thankfully, `yq` underlying yaml parser still supports that tag - and it's extra nice in that it explicitly puts the `!!merge` tag on key of the map entry. This tag tells other yaml parsers that this entry is a merge entry, as opposed to a regular string key that happens to have a value of `<<:`. This is backwards compatible with the 1.1 spec of yaml, it's simply an explicit way of specifying the type (for instance, you can use a `!!str` tag to enforce a particular value to be a string.
Although this does affect the readability of the yaml to humans, it still works and processes fine with various yaml processors.