diff --git a/pkg/yqlib/doc/README.md b/pkg/yqlib/doc/README.md deleted file mode 100644 index ac099ca5..00000000 --- a/pkg/yqlib/doc/README.md +++ /dev/null @@ -1,116 +0,0 @@ -# How it works - -In `yq` expressions are made up of operators and pipes. A context of nodes is passed through the expression and each operation takes the context as input and returns a new context as output. That output is piped in as input for the next operation in the expression. To begin with, the context is set to the first yaml document of the first yaml file (if processing in sequence using eval). - -Lets look at a couple of examples. - -## Example with a simple operator - -Given a document like: - -```yaml -- [a] -- "cat" -``` - -with an expression: - -``` -.[] | length -``` - -`yq` will initially set the context as single node of the entire yaml document, an array of two elements. - -```yaml -- [a] -- "cat" -``` - -This gets piped into the splat operator `.[]` which will split out the context into a collection of two nodes `[a]` and `"cat"`. Note that this is _not_ a yaml array. - -The `length` operator take no arguments, and will simply return the length of _each_ matching node in the context. So for the context of `[a]` and `"cat"`, it will return a new context of `1` and `3`. - -This being the last operation in the expression, the results will be printed out: - -``` -1 -3 -``` - -## Example with an operator that takes arguments. - -Given a document like: - -```yaml -a: cat -b: dog -``` - -with an expression: - -``` -.a = .b -``` - -The `=` operator takes two arguments, a `lhs` expression, which in this case is `.a` and `rhs` expression which is `.b`. - -It pipes the current, lets call it 'root' context through the `lhs` expression of `.a` to return the node - -```yaml -cat -``` - -Note that this node holds not only its value 'cat', but comments and metadata too, including path and parent information. - -The `=` operator then pipes the 'root' context through the `rhs` expression of `.b` to return the node - -```yaml -dog -``` - -Both sides have now been evaluated, so now the operator copies across the value from the RHS to the value on the LHS, and it returns the now updated context: - -```yaml -a: dog -b: dog -``` - -## Relative update (e.g. `|=`) -There is another form of the `=` operator which we call the relative form. It's very similar to `=` but with one key difference when evaluating the RHS expression. - -In the plain form, we pass in the 'root' level context to the RHS expression. In relative form, we pass in _each result of the LHS_ to the RHS expression. Let's go through an example. - -Given a document like: - -```yaml -a: 1 -b: thing -``` - -with an expression: - -``` -.a |= . + 1 -``` - -Similar to the `=` operator, `|=` takes two operands, the LHS and RHS. - -It pipes the current context (the whole document) through the LHS expression of `.a` to get the node value: - -``` -1 -``` - -Now it pipes _that LHS context_ into the RHS expression `. + 1` (whereas in the `=` plain form it piped the original document context into the RHS) to yield: - - -``` -2 -``` - -The assignment operator then copies across the value from the RHS to the value on the LHS, and it returns the now updated 'root' context: - -```yaml -a: 2 -b: thing -``` \ No newline at end of file diff --git a/pkg/yqlib/doc/anchor-and-alias-operators.md b/pkg/yqlib/doc/anchor-and-alias-operators.md new file mode 100644 index 00000000..484a99bd --- /dev/null +++ b/pkg/yqlib/doc/anchor-and-alias-operators.md @@ -0,0 +1,333 @@ +# Anchor and Alias Operators + +Use the `alias` and `anchor` operators to read and write yaml aliases and anchors. The `explode` operator normalises a yaml file (dereference (or expands) aliases and remove anchor names). + +`yq` supports merge aliases (like `<<: *blah`) however this is no longer in the standard yaml spec (1.2) and so `yq` will automatically add the `!!merge` tag to these nodes as it is effectively a custom tag. + + +## Merge one map +see https://yaml.org/type/merge.html + +Given a sample.yml file of: +```yaml +- &CENTER + x: 1 + y: 2 +- &LEFT + x: 0 + y: 2 +- &BIG + r: 10 +- &SMALL + r: 1 +- !!merge <<: *CENTER + r: 10 +``` +then +```bash +yq eval '.[4] | explode(.)' sample.yml +``` +will output +```yaml +x: 1 +y: 2 +r: 10 +``` + +## Merge multiple maps +see https://yaml.org/type/merge.html + +Given a sample.yml file of: +```yaml +- &CENTER + x: 1 + y: 2 +- &LEFT + x: 0 + y: 2 +- &BIG + r: 10 +- &SMALL + r: 1 +- !!merge <<: + - *CENTER + - *BIG +``` +then +```bash +yq eval '.[4] | explode(.)' sample.yml +``` +will output +```yaml +r: 10 +x: 1 +y: 2 +``` + +## Override +see https://yaml.org/type/merge.html + +Given a sample.yml file of: +```yaml +- &CENTER + x: 1 + y: 2 +- &LEFT + x: 0 + y: 2 +- &BIG + r: 10 +- &SMALL + r: 1 +- !!merge <<: + - *BIG + - *LEFT + - *SMALL + x: 1 +``` +then +```bash +yq eval '.[4] | explode(.)' sample.yml +``` +will output +```yaml +r: 10 +x: 1 +y: 2 +``` + +## Get anchor +Given a sample.yml file of: +```yaml +a: &billyBob cat +``` +then +```bash +yq eval '.a | anchor' sample.yml +``` +will output +```yaml +billyBob +``` + +## Set anchor +Given a sample.yml file of: +```yaml +a: cat +``` +then +```bash +yq eval '.a anchor = "foobar"' sample.yml +``` +will output +```yaml +a: &foobar cat +``` + +## Set anchor relatively using assign-update +Given a sample.yml file of: +```yaml +a: + b: cat +``` +then +```bash +yq eval '.a anchor |= .b' sample.yml +``` +will output +```yaml +a: &cat + b: cat +``` + +## Get alias +Given a sample.yml file of: +```yaml +b: &billyBob meow +a: *billyBob +``` +then +```bash +yq eval '.a | alias' sample.yml +``` +will output +```yaml +billyBob +``` + +## Set alias +Given a sample.yml file of: +```yaml +b: &meow purr +a: cat +``` +then +```bash +yq eval '.a alias = "meow"' sample.yml +``` +will output +```yaml +b: &meow purr +a: *meow +``` + +## Set alias to blank does nothing +Given a sample.yml file of: +```yaml +b: &meow purr +a: cat +``` +then +```bash +yq eval '.a alias = ""' sample.yml +``` +will output +```yaml +b: &meow purr +a: cat +``` + +## Set alias relatively using assign-update +Given a sample.yml file of: +```yaml +b: &meow purr +a: + f: meow +``` +then +```bash +yq eval '.a alias |= .f' sample.yml +``` +will output +```yaml +b: &meow purr +a: *meow +``` + +## Explode alias and anchor +Given a sample.yml file of: +```yaml +f: + a: &a cat + b: *a +``` +then +```bash +yq eval 'explode(.f)' sample.yml +``` +will output +```yaml +f: + a: cat + b: cat +``` + +## Explode with no aliases or anchors +Given a sample.yml file of: +```yaml +a: mike +``` +then +```bash +yq eval 'explode(.a)' sample.yml +``` +will output +```yaml +a: mike +``` + +## Explode with alias keys +Given a sample.yml file of: +```yaml +f: + a: &a cat + *a: b +``` +then +```bash +yq eval 'explode(.f)' sample.yml +``` +will output +```yaml +f: + a: cat + cat: b +``` + +## Explode with merge anchors +Given a sample.yml file of: +```yaml +foo: &foo + a: foo_a + thing: foo_thing + c: foo_c +bar: &bar + b: bar_b + thing: bar_thing + c: bar_c +foobarList: + b: foobarList_b + !!merge <<: + - *foo + - *bar + c: foobarList_c +foobar: + c: foobar_c + !!merge <<: *foo + thing: foobar_thing +``` +then +```bash +yq eval 'explode(.)' sample.yml +``` +will output +```yaml +foo: + a: foo_a + thing: foo_thing + c: foo_c +bar: + b: bar_b + thing: bar_thing + c: bar_c +foobarList: + b: bar_b + thing: foo_thing + c: foobarList_c + a: foo_a +foobar: + c: foo_c + a: foo_a + thing: foobar_thing +``` + +## Dereference and update a field +`Use explode with multiply to dereference an object + +Given a sample.yml file of: +```yaml +item_value: &item_value + value: true +thingOne: + name: item_1 + !!merge <<: *item_value +thingTwo: + name: item_2 + !!merge <<: *item_value +``` +then +```bash +yq eval '.thingOne |= explode(.) * {"value": false}' sample.yml +``` +will output +```yaml +item_value: &item_value + value: true +thingOne: + name: item_1 + value: false +thingTwo: + name: item_2 + !!merge <<: *item_value +``` + diff --git a/pkg/yqlib/doc/assign-update.md b/pkg/yqlib/doc/assign-update.md new file mode 100644 index 00000000..ad71c81b --- /dev/null +++ b/pkg/yqlib/doc/assign-update.md @@ -0,0 +1,212 @@ +# Assign (Update) + +This operator is used to update node values. It can be used in either the: + +### plain form: `=` +Which will assign the LHS node values to the RHS node values. The RHS expression is run against the matching nodes in the pipeline. + +### relative form: `|=` +This will do a similar thing to the plain form, however, the RHS expression is run against _the LHS nodes_. This is useful for updating values based on old values, e.g. increment. +## Create yaml file +Running +```bash +yq eval --null-input '.a.b = "cat" | .x = "frog"' +``` +will output +```yaml +a: + b: cat +x: frog +``` + +## Update node to be the child value +Given a sample.yml file of: +```yaml +a: + b: + g: foof +``` +then +```bash +yq eval '.a |= .b' sample.yml +``` +will output +```yaml +a: + g: foof +``` + +## Double elements in an array +Given a sample.yml file of: +```yaml +- 1 +- 2 +- 3 +``` +then +```bash +yq eval '.[] |= . * 2' sample.yml +``` +will output +```yaml +- 2 +- 4 +- 6 +``` + +## Update node from another file +Note this will also work when the second file is a scalar (string/number) + +Given a sample.yml file of: +```yaml +a: apples +``` +And another sample another.yml file of: +```yaml +b: bob +``` +then +```bash +yq eval-all 'select(fileIndex==0).a = select(fileIndex==1) | select(fileIndex==0)' sample.yml another.yml +``` +will output +```yaml +a: + b: bob +``` + +## Update node to be the sibling value +Given a sample.yml file of: +```yaml +a: + b: child +b: sibling +``` +then +```bash +yq eval '.a = .b' sample.yml +``` +will output +```yaml +a: sibling +b: sibling +``` + +## Updated multiple paths +Given a sample.yml file of: +```yaml +a: fieldA +b: fieldB +c: fieldC +``` +then +```bash +yq eval '(.a, .c) = "potatoe"' sample.yml +``` +will output +```yaml +a: potatoe +b: fieldB +c: potatoe +``` + +## Update string value +Given a sample.yml file of: +```yaml +a: + b: apple +``` +then +```bash +yq eval '.a.b = "frog"' sample.yml +``` +will output +```yaml +a: + b: frog +``` + +## Update string value via |= +Note there is no difference between `=` and `|=` when the RHS is a scalar + +Given a sample.yml file of: +```yaml +a: + b: apple +``` +then +```bash +yq eval '.a.b |= "frog"' sample.yml +``` +will output +```yaml +a: + b: frog +``` + +## Update deeply selected results +Note that the LHS is wrapped in brackets! This is to ensure we dont first filter out the yaml and then update the snippet. + +Given a sample.yml file of: +```yaml +a: + b: apple + c: cactus +``` +then +```bash +yq eval '(.a[] | select(. == "apple")) = "frog"' sample.yml +``` +will output +```yaml +a: + b: frog + c: cactus +``` + +## Update array values +Given a sample.yml file of: +```yaml +- candy +- apple +- sandy +``` +then +```bash +yq eval '(.[] | select(. == "*andy")) = "bogs"' sample.yml +``` +will output +```yaml +- bogs +- apple +- bogs +``` + +## Update empty object +Given a sample.yml file of: +```yaml +{} +``` +then +```bash +yq eval '.a.b |= "bogs"' sample.yml +``` +will output +```yaml +{a: {b: bogs}} +``` + +## Update empty object and array +Given a sample.yml file of: +```yaml +{} +``` +then +```bash +yq eval '.a.b.[0] |= "bogs"' sample.yml +``` +will output +```yaml +{a: {b: [bogs]}} +``` + diff --git a/pkg/yqlib/doc/boolean-operators.md b/pkg/yqlib/doc/boolean-operators.md new file mode 100644 index 00000000..c60f26a8 --- /dev/null +++ b/pkg/yqlib/doc/boolean-operators.md @@ -0,0 +1,222 @@ +# Boolean Operators + +The `or` and `and` operators take two parameters and return a boolean result. + +`not` flips a boolean from true to false, or vice versa. + +`any` will return `true` if there are any `true` values in a array sequence, and `all` will return true if _all_ elements in an array are true. + +`any_c(condition)` and `all_c(condition)` are like `any` and `all` but they take a condition expression that is used against each element to determine if it's `true`. Note: in `jq` you can simply pass a condition to `any` or `all` and it simply works - `yq` isn't that clever..yet + +These are most commonly used with the `select` operator to filter particular nodes. + +## `or` example +Running +```bash +yq eval --null-input 'true or false' +``` +will output +```yaml +true +``` + +## `and` example +Running +```bash +yq eval --null-input 'true and false' +``` +will output +```yaml +false +``` + +## Matching nodes with select, equals and or +Given a sample.yml file of: +```yaml +- a: bird + b: dog +- a: frog + b: bird +- a: cat + b: fly +``` +then +```bash +yq eval '[.[] | select(.a == "cat" or .b == "dog")]' sample.yml +``` +will output +```yaml +- a: bird + b: dog +- a: cat + b: fly +``` + +## `any` returns true if any boolean in a given array is true +Given a sample.yml file of: +```yaml +- false +- true +``` +then +```bash +yq eval 'any' sample.yml +``` +will output +```yaml +true +``` + +## `any` returns false for an empty array +Given a sample.yml file of: +```yaml +[] +``` +then +```bash +yq eval 'any' sample.yml +``` +will output +```yaml +false +``` + +## `any_c` returns true if any element in the array is true for the given condition. +Given a sample.yml file of: +```yaml +a: + - rad + - awesome +b: + - meh + - whatever +``` +then +```bash +yq eval '.[] |= any_c(. == "awesome")' sample.yml +``` +will output +```yaml +a: true +b: false +``` + +## `all` returns true if all booleans in a given array are true +Given a sample.yml file of: +```yaml +- true +- true +``` +then +```bash +yq eval 'all' sample.yml +``` +will output +```yaml +true +``` + +## `all` returns true for an empty array +Given a sample.yml file of: +```yaml +[] +``` +then +```bash +yq eval 'all' sample.yml +``` +will output +```yaml +true +``` + +## `all_c` returns true if all elements in the array are true for the given condition. +Given a sample.yml file of: +```yaml +a: + - rad + - awesome +b: + - meh + - 12 +``` +then +```bash +yq eval '.[] |= all_c(tag == "!!str")' sample.yml +``` +will output +```yaml +a: true +b: false +``` + +## Not true is false +Running +```bash +yq eval --null-input 'true | not' +``` +will output +```yaml +false +``` + +## Not false is true +Running +```bash +yq eval --null-input 'false | not' +``` +will output +```yaml +true +``` + +## String values considered to be true +Running +```bash +yq eval --null-input '"cat" | not' +``` +will output +```yaml +false +``` + +## Empty string value considered to be true +Running +```bash +yq eval --null-input '"" | not' +``` +will output +```yaml +false +``` + +## Numbers are considered to be true +Running +```bash +yq eval --null-input '1 | not' +``` +will output +```yaml +false +``` + +## Zero is considered to be true +Running +```bash +yq eval --null-input '0 | not' +``` +will output +```yaml +false +``` + +## Null is considered to be false +Running +```bash +yq eval --null-input '~ | not' +``` +will output +```yaml +true +``` + diff --git a/pkg/yqlib/doc/collect-into-array.md b/pkg/yqlib/doc/collect-into-array.md new file mode 100644 index 00000000..8195ee9a --- /dev/null +++ b/pkg/yqlib/doc/collect-into-array.md @@ -0,0 +1,41 @@ +# Collect into Array + +This creates an array using the expression between the square brackets. + + +## Collect empty +Running +```bash +yq eval --null-input '[]' +``` +will output +```yaml +[] +``` + +## Collect single +Running +```bash +yq eval --null-input '["cat"]' +``` +will output +```yaml +- cat +``` + +## Collect many +Given a sample.yml file of: +```yaml +a: cat +b: dog +``` +then +```bash +yq eval '[.a, .b]' sample.yml +``` +will output +```yaml +- cat +- dog +``` + diff --git a/pkg/yqlib/doc/comment-operators.md b/pkg/yqlib/doc/comment-operators.md new file mode 100644 index 00000000..507a6a7f --- /dev/null +++ b/pkg/yqlib/doc/comment-operators.md @@ -0,0 +1,159 @@ +# Comment Operators + +Use these comment operators to set or retrieve comments. + +Like the `=` and `|=` assign operators, the same syntax applies when updating comments: + +### plain form: `=` +This will assign the LHS nodes comments to the expression on the RHS. The RHS is run against the matching nodes in the pipeline + +### relative form: `|=` +Similar to the plain form, however the RHS evaluates against each matching LHS node! This is useful if you want to set the comments as a relative expression of the node, for instance its value or path. + +## Set line comment +Given a sample.yml file of: +```yaml +a: cat +``` +then +```bash +yq eval '.a lineComment="single"' sample.yml +``` +will output +```yaml +a: cat # single +``` + +## Use update assign to perform relative updates +Given a sample.yml file of: +```yaml +a: cat +b: dog +``` +then +```bash +yq eval '.. lineComment |= .' sample.yml +``` +will output +```yaml +a: cat # cat +b: dog # dog +``` + +## Set head comment +Given a sample.yml file of: +```yaml +a: cat +``` +then +```bash +yq eval '. headComment="single"' sample.yml +``` +will output +```yaml +# single + +a: cat +``` + +## Set foot comment, using an expression +Given a sample.yml file of: +```yaml +a: cat +``` +then +```bash +yq eval '. footComment=.a' sample.yml +``` +will output +```yaml +a: cat + +# cat +``` + +## Remove comment +Given a sample.yml file of: +```yaml +a: cat # comment +b: dog # leave this +``` +then +```bash +yq eval '.a lineComment=""' sample.yml +``` +will output +```yaml +a: cat +b: dog # leave this +``` + +## Remove (strip) all comments +Note the use of `...` to ensure key nodes are included. + +Given a sample.yml file of: +```yaml +a: cat # comment +# great +b: # key comment +``` +then +```bash +yq eval '... comments=""' sample.yml +``` +will output +```yaml +a: cat +b: +``` + +## Get line comment +Given a sample.yml file of: +```yaml +a: cat # meow +``` +then +```bash +yq eval '.a | lineComment' sample.yml +``` +will output +```yaml +meow +``` + +## Get head comment +Given a sample.yml file of: +```yaml +# welcome! + +a: cat # meow + +# have a great day +``` +then +```bash +yq eval '. | headComment' sample.yml +``` +will output +```yaml +welcome! +``` + +## Get foot comment +Given a sample.yml file of: +```yaml +# welcome! + +a: cat # meow + +# have a great day +``` +then +```bash +yq eval '. | footComment' sample.yml +``` +will output +```yaml +have a great day +``` + diff --git a/pkg/yqlib/doc/contains.md b/pkg/yqlib/doc/contains.md new file mode 100644 index 00000000..5f3c7f3b --- /dev/null +++ b/pkg/yqlib/doc/contains.md @@ -0,0 +1,88 @@ +# Contains + +This returns `true` if the context contains the passed in parameter, and false otherwise. + +## Array contains array +Array is equal or subset of + +Given a sample.yml file of: +```yaml +- foobar +- foobaz +- blarp +``` +then +```bash +yq eval 'contains(["baz", "bar"])' sample.yml +``` +will output +```yaml +true +``` + +## Object included in array +Given a sample.yml file of: +```yaml +"foo": 12 +"bar": + - 1 + - 2 + - "barp": 12 + "blip": 13 +``` +then +```bash +yq eval 'contains({"bar": [{"barp": 12}]})' sample.yml +``` +will output +```yaml +true +``` + +## Object not included in array +Given a sample.yml file of: +```yaml +"foo": 12 +"bar": + - 1 + - 2 + - "barp": 12 + "blip": 13 +``` +then +```bash +yq eval 'contains({"foo": 12, "bar": [{"barp": 15}]})' sample.yml +``` +will output +```yaml +false +``` + +## String contains substring +Given a sample.yml file of: +```yaml +foobar +``` +then +```bash +yq eval 'contains("bar")' sample.yml +``` +will output +```yaml +true +``` + +## String equals string +Given a sample.yml file of: +```yaml +meow +``` +then +```bash +yq eval 'contains("meow")' sample.yml +``` +will output +```yaml +true +``` + diff --git a/pkg/yqlib/doc/create-collect-into-object.md b/pkg/yqlib/doc/create-collect-into-object.md new file mode 100644 index 00000000..66c446af --- /dev/null +++ b/pkg/yqlib/doc/create-collect-into-object.md @@ -0,0 +1,82 @@ +# Create, Collect into Object + +This is used to construct objects (or maps). This can be used against existing yaml, or to create fresh yaml documents. + +## Collect empty object +Running +```bash +yq eval --null-input '{}' +``` +will output +```yaml +{} +``` + +## Wrap (prefix) existing object +Given a sample.yml file of: +```yaml +name: Mike +``` +then +```bash +yq eval '{"wrap": .}' sample.yml +``` +will output +```yaml +wrap: + name: Mike +``` + +## Using splat to create multiple objects +Given a sample.yml file of: +```yaml +name: Mike +pets: + - cat + - dog +``` +then +```bash +yq eval '{.name: .pets.[]}' sample.yml +``` +will output +```yaml +Mike: cat +Mike: dog +``` + +## Working with multiple documents +Given a sample.yml file of: +```yaml +name: Mike +pets: + - cat + - dog +--- +name: Rosey +pets: + - monkey + - sheep +``` +then +```bash +yq eval '{.name: .pets.[]}' sample.yml +``` +will output +```yaml +Mike: cat +Mike: dog +Rosey: monkey +Rosey: sheep +``` + +## Creating yaml from scratch +Running +```bash +yq eval --null-input '{"wrap": "frog"}' +``` +will output +```yaml +wrap: frog +``` + diff --git a/pkg/yqlib/doc/delete.md b/pkg/yqlib/doc/delete.md new file mode 100644 index 00000000..a7073327 --- /dev/null +++ b/pkg/yqlib/doc/delete.md @@ -0,0 +1,120 @@ +# Delete + +Deletes matching entries in maps or arrays. + +## Delete entry in map +Given a sample.yml file of: +```yaml +a: cat +b: dog +``` +then +```bash +yq eval 'del(.b)' sample.yml +``` +will output +```yaml +a: cat +``` + +## Delete nested entry in map +Given a sample.yml file of: +```yaml +a: + a1: fred + a2: frood +``` +then +```bash +yq eval 'del(.a.a1)' sample.yml +``` +will output +```yaml +a: + a2: frood +``` + +## Delete entry in array +Given a sample.yml file of: +```yaml +- 1 +- 2 +- 3 +``` +then +```bash +yq eval 'del(.[1])' sample.yml +``` +will output +```yaml +- 1 +- 3 +``` + +## Delete nested entry in array +Given a sample.yml file of: +```yaml +- a: cat + b: dog +``` +then +```bash +yq eval 'del(.[0].a)' sample.yml +``` +will output +```yaml +- b: dog +``` + +## Delete no matches +Given a sample.yml file of: +```yaml +a: cat +b: dog +``` +then +```bash +yq eval 'del(.c)' sample.yml +``` +will output +```yaml +a: cat +b: dog +``` + +## Delete matching entries +Given a sample.yml file of: +```yaml +a: cat +b: dog +c: bat +``` +then +```bash +yq eval 'del( .[] | select(. == "*at") )' sample.yml +``` +will output +```yaml +b: dog +``` + +## Recursively delete matching keys +Given a sample.yml file of: +```yaml +a: + name: frog + b: + name: blog + age: 12 +``` +then +```bash +yq eval 'del(.. | select(has("name")).name)' sample.yml +``` +will output +```yaml +a: + b: + age: 12 +``` + diff --git a/pkg/yqlib/doc/document-index.md b/pkg/yqlib/doc/document-index.md new file mode 100644 index 00000000..acd479b5 --- /dev/null +++ b/pkg/yqlib/doc/document-index.md @@ -0,0 +1,91 @@ +# Document Index + +Use the `documentIndex` operator (or the `di` shorthand) to select nodes of a particular document. + +## Retrieve a document index +Given a sample.yml file of: +```yaml +a: cat +--- +a: frog +``` +then +```bash +yq eval '.a | documentIndex' sample.yml +``` +will output +```yaml +0 +--- +1 +``` + +## Retrieve a document index, shorthand +Given a sample.yml file of: +```yaml +a: cat +--- +a: frog +``` +then +```bash +yq eval '.a | di' sample.yml +``` +will output +```yaml +0 +--- +1 +``` + +## Filter by document index +Given a sample.yml file of: +```yaml +a: cat +--- +a: frog +``` +then +```bash +yq eval 'select(documentIndex == 1)' sample.yml +``` +will output +```yaml +a: frog +``` + +## Filter by document index shorthand +Given a sample.yml file of: +```yaml +a: cat +--- +a: frog +``` +then +```bash +yq eval 'select(di == 1)' sample.yml +``` +will output +```yaml +a: frog +``` + +## Print Document Index with matches +Given a sample.yml file of: +```yaml +a: cat +--- +a: frog +``` +then +```bash +yq eval '.a | ({"match": ., "doc": documentIndex})' sample.yml +``` +will output +```yaml +match: cat +doc: 0 +match: frog +doc: 1 +``` + diff --git a/pkg/yqlib/doc/entries.md b/pkg/yqlib/doc/entries.md new file mode 100644 index 00000000..95fae132 --- /dev/null +++ b/pkg/yqlib/doc/entries.md @@ -0,0 +1,121 @@ +# Entries + +Similar to the same named functions in `jq` these functions convert to/from an object and an array of key-value pairs. This is most useful for performing operations on keys of maps. + +## to_entries Map +Given a sample.yml file of: +```yaml +a: 1 +b: 2 +``` +then +```bash +yq eval 'to_entries' sample.yml +``` +will output +```yaml +- key: a + value: 1 +- key: b + value: 2 +``` + +## to_entries Array +Given a sample.yml file of: +```yaml +- a +- b +``` +then +```bash +yq eval 'to_entries' sample.yml +``` +will output +```yaml +- key: 0 + value: a +- key: 1 + value: b +``` + +## to_entries null +Given a sample.yml file of: +```yaml +null +``` +then +```bash +yq eval 'to_entries' sample.yml +``` +will output +```yaml +``` + +## from_entries map +Given a sample.yml file of: +```yaml +a: 1 +b: 2 +``` +then +```bash +yq eval 'to_entries | from_entries' sample.yml +``` +will output +```yaml +a: 1 +b: 2 +``` + +## from_entries with numeric key indexes +from_entries always creates a map, even for numeric keys + +Given a sample.yml file of: +```yaml +- a +- b +``` +then +```bash +yq eval 'to_entries | from_entries' sample.yml +``` +will output +```yaml +0: a +1: b +``` + +## Use with_entries to update keys +Given a sample.yml file of: +```yaml +a: 1 +b: 2 +``` +then +```bash +yq eval 'with_entries(.key |= "KEY_" + .)' sample.yml +``` +will output +```yaml +KEY_a: 1 +KEY_b: 2 +``` + +## Use with_entries to filter the map +Given a sample.yml file of: +```yaml +a: + b: bird +c: + d: dog +``` +then +```bash +yq eval 'with_entries(select(.value | has("b")))' sample.yml +``` +will output +```yaml +a: + b: bird +``` + diff --git a/pkg/yqlib/doc/env-variable-operators.md b/pkg/yqlib/doc/env-variable-operators.md new file mode 100644 index 00000000..e4a1817f --- /dev/null +++ b/pkg/yqlib/doc/env-variable-operators.md @@ -0,0 +1,79 @@ +# Env Variable Operators + +This operator is used to handle environment variables usage in path expressions. While environment variables can, of course, be passed in via your CLI with string interpolation, this often comes with complex quote escaping and can be tricky to write and read. Note that there are two forms, `env` which will parse the environment variable as a yaml (be it a map, array, string, number of boolean) and `strenv` which will always parse the argument as a string. + +## Read string environment variable +Running +```bash +myenv="cat meow" yq eval --null-input '.a = env(myenv)' +``` +will output +```yaml +a: cat meow +``` + +## Read boolean environment variable +Running +```bash +myenv="true" yq eval --null-input '.a = env(myenv)' +``` +will output +```yaml +a: true +``` + +## Read numeric environment variable +Running +```bash +myenv="12" yq eval --null-input '.a = env(myenv)' +``` +will output +```yaml +a: 12 +``` + +## Read yaml environment variable +Running +```bash +myenv="{b: fish}" yq eval --null-input '.a = env(myenv)' +``` +will output +```yaml +a: {b: fish} +``` + +## Read boolean environment variable as a string +Running +```bash +myenv="true" yq eval --null-input '.a = strenv(myenv)' +``` +will output +```yaml +a: "true" +``` + +## Read numeric environment variable as a string +Running +```bash +myenv="12" yq eval --null-input '.a = strenv(myenv)' +``` +will output +```yaml +a: "12" +``` + +## Dynamic key lookup with environment variable +Given a sample.yml file of: +```yaml +cat: meow +dog: woof +``` +then +```bash +myenv="cat" yq eval '.[env(myenv)]' sample.yml +``` +will output +```yaml +meow +``` + diff --git a/pkg/yqlib/doc/equals.md b/pkg/yqlib/doc/equals.md new file mode 100644 index 00000000..d7129a79 --- /dev/null +++ b/pkg/yqlib/doc/equals.md @@ -0,0 +1,124 @@ +# Equals + +This is a boolean operator that will return `true` if the LHS is equal to the RHS and `false` otherwise. + +``` +.a == .b +``` + +It is most often used with the select operator to find particular nodes: + +``` +select(.a == .b) +``` + +## Match string +Given a sample.yml file of: +```yaml +- cat +- goat +- dog +``` +then +```bash +yq eval '.[] | (. == "*at")' sample.yml +``` +will output +```yaml +true +true +false +``` + +## Don't match string +Given a sample.yml file of: +```yaml +- cat +- goat +- dog +``` +then +```bash +yq eval '.[] | (. != "*at")' sample.yml +``` +will output +```yaml +false +false +true +``` + +## Match number +Given a sample.yml file of: +```yaml +- 3 +- 4 +- 5 +``` +then +```bash +yq eval '.[] | (. == 4)' sample.yml +``` +will output +```yaml +false +true +false +``` + +## Dont match number +Given a sample.yml file of: +```yaml +- 3 +- 4 +- 5 +``` +then +```bash +yq eval '.[] | (. != 4)' sample.yml +``` +will output +```yaml +true +false +true +``` + +## Match nulls +Running +```bash +yq eval --null-input 'null == ~' +``` +will output +```yaml +true +``` + +## Non exisitant key doesn't equal a value +Given a sample.yml file of: +```yaml +a: frog +``` +then +```bash +yq eval 'select(.b != "thing")' sample.yml +``` +will output +```yaml +a: frog +``` + +## Two non existant keys are equal +Given a sample.yml file of: +```yaml +a: frog +``` +then +```bash +yq eval 'select(.b == .c)' sample.yml +``` +will output +```yaml +a: frog +``` + diff --git a/pkg/yqlib/doc/file-operators.md b/pkg/yqlib/doc/file-operators.md new file mode 100644 index 00000000..a5e2bef3 --- /dev/null +++ b/pkg/yqlib/doc/file-operators.md @@ -0,0 +1,74 @@ +# File Operators + +File operators are most often used with merge when needing to merge specific files together. Note that when doing this, you will need to use `eval-all` to ensure all yaml documents are loaded into memory before performing the merge (as opposed to `eval` which runs the expression once per document). + +Note that the `fileIndex` operator has a short alias of `fi`. + +## Merging files +Note the use of eval-all to ensure all documents are loaded into memory. +```bash +yq eval-all 'select(fi == 0) * select(filename == "file2.yaml")' file1.yaml file2.yaml +``` + +## Get filename +Given a sample.yml file of: +```yaml +a: cat +``` +then +```bash +yq eval 'filename' sample.yml +``` +will output +```yaml +sample.yml +``` + +## Get file index +Given a sample.yml file of: +```yaml +a: cat +``` +then +```bash +yq eval 'fileIndex' sample.yml +``` +will output +```yaml +0 +``` + +## Get file indices of multiple documents +Given a sample.yml file of: +```yaml +a: cat +``` +And another sample another.yml file of: +```yaml +a: cat +``` +then +```bash +yq eval-all 'fileIndex' sample.yml another.yml +``` +will output +```yaml +0 +--- +1 +``` + +## Get file index alias +Given a sample.yml file of: +```yaml +a: cat +``` +then +```bash +yq eval 'fi' sample.yml +``` +will output +```yaml +0 +``` + diff --git a/pkg/yqlib/doc/has.md b/pkg/yqlib/doc/has.md new file mode 100644 index 00000000..00cd5feb --- /dev/null +++ b/pkg/yqlib/doc/has.md @@ -0,0 +1,70 @@ +# Has + +This is operation that returns true if the key exists in a map (or index in an array), false otherwise. + +## Has map key +Given a sample.yml file of: +```yaml +- a: yes +- a: ~ +- a: +- b: nope +``` +then +```bash +yq eval '.[] | has("a")' sample.yml +``` +will output +```yaml +true +true +true +false +``` + +## Select, checking for existence of deep paths +Simply pipe in parent expressions into `has` + +Given a sample.yml file of: +```yaml +- a: + b: + c: cat +- a: + b: + d: dog +``` +then +```bash +yq eval '.[] | select(.a.b | has("c"))' sample.yml +``` +will output +```yaml +a: + b: + c: cat +``` + +## Has array index +Given a sample.yml file of: +```yaml +- [] +- [1] +- [1, 2] +- [1, null] +- [1, 2, 3] + +``` +then +```bash +yq eval '.[] | has(1)' sample.yml +``` +will output +```yaml +false +false +true +true +true +``` + diff --git a/pkg/yqlib/doc/headers/Delete.md b/pkg/yqlib/doc/headers/Delete.md deleted file mode 100644 index e3c0d1cc..00000000 --- a/pkg/yqlib/doc/headers/Delete.md +++ /dev/null @@ -1 +0,0 @@ -Deletes matching entries in maps or arrays. \ No newline at end of file diff --git a/pkg/yqlib/doc/headers/Keys.md b/pkg/yqlib/doc/headers/Keys.md deleted file mode 100644 index ba8c9992..00000000 --- a/pkg/yqlib/doc/headers/Keys.md +++ /dev/null @@ -1,3 +0,0 @@ -# Keys - -Use the `keys` operator to return map keys or array indices. \ No newline at end of file diff --git a/pkg/yqlib/doc/headers/Union.md b/pkg/yqlib/doc/headers/Union.md deleted file mode 100644 index 787bda7f..00000000 --- a/pkg/yqlib/doc/headers/Union.md +++ /dev/null @@ -1 +0,0 @@ -This operator is used to combine different results together. \ No newline at end of file diff --git a/pkg/yqlib/doc/headers/anchor-and-alias-operators.md b/pkg/yqlib/doc/headers/anchor-and-alias-operators.md index cbc089ea..5c6f29e5 100644 --- a/pkg/yqlib/doc/headers/anchor-and-alias-operators.md +++ b/pkg/yqlib/doc/headers/anchor-and-alias-operators.md @@ -1,3 +1,5 @@ +# Anchor and Alias Operators + Use the `alias` and `anchor` operators to read and write yaml aliases and anchors. The `explode` operator normalises a yaml file (dereference (or expands) aliases and remove anchor names). `yq` supports merge aliases (like `<<: *blah`) however this is no longer in the standard yaml spec (1.2) and so `yq` will automatically add the `!!merge` tag to these nodes as it is effectively a custom tag. diff --git a/pkg/yqlib/doc/headers/assign-update.md b/pkg/yqlib/doc/headers/assign-update.md index 5cd669cf..89a2dc86 100644 --- a/pkg/yqlib/doc/headers/assign-update.md +++ b/pkg/yqlib/doc/headers/assign-update.md @@ -1,3 +1,5 @@ +# Assign (Update) + This operator is used to update node values. It can be used in either the: ### plain form: `=` diff --git a/pkg/yqlib/doc/headers/boolean-operators.md b/pkg/yqlib/doc/headers/boolean-operators.md index 4e4a8d54..ddf571a4 100644 --- a/pkg/yqlib/doc/headers/boolean-operators.md +++ b/pkg/yqlib/doc/headers/boolean-operators.md @@ -1,3 +1,5 @@ +# Boolean Operators + The `or` and `and` operators take two parameters and return a boolean result. `not` flips a boolean from true to false, or vice versa. diff --git a/pkg/yqlib/doc/headers/comment-operators.md b/pkg/yqlib/doc/headers/comment-operators.md index 0740a76d..3d5f2703 100644 --- a/pkg/yqlib/doc/headers/comment-operators.md +++ b/pkg/yqlib/doc/headers/comment-operators.md @@ -1,11 +1,11 @@ +# Comment Operators + Use these comment operators to set or retrieve comments. Like the `=` and `|=` assign operators, the same syntax applies when updating comments: - ### plain form: `=` This will assign the LHS nodes comments to the expression on the RHS. The RHS is run against the matching nodes in the pipeline ### relative form: `|=` Similar to the plain form, however the RHS evaluates against each matching LHS node! This is useful if you want to set the comments as a relative expression of the node, for instance its value or path. - diff --git a/pkg/yqlib/doc/headers/contains.md b/pkg/yqlib/doc/headers/contains.md new file mode 100644 index 00000000..9d549cbc --- /dev/null +++ b/pkg/yqlib/doc/headers/contains.md @@ -0,0 +1,3 @@ +# Contains + +This returns `true` if the context contains the passed in parameter, and false otherwise. diff --git a/pkg/yqlib/doc/headers/create-collect-into-object.md b/pkg/yqlib/doc/headers/create-collect-into-object.md index e0552575..85db0faa 100644 --- a/pkg/yqlib/doc/headers/create-collect-into-object.md +++ b/pkg/yqlib/doc/headers/create-collect-into-object.md @@ -1 +1,3 @@ -This is used to construct objects (or maps). This can be used against existing yaml, or to create fresh yaml documents. \ No newline at end of file +# Create, Collect into Object + +This is used to construct objects (or maps). This can be used against existing yaml, or to create fresh yaml documents. diff --git a/pkg/yqlib/doc/headers/delete.md b/pkg/yqlib/doc/headers/delete.md new file mode 100644 index 00000000..1a635871 --- /dev/null +++ b/pkg/yqlib/doc/headers/delete.md @@ -0,0 +1,3 @@ +# Delete + +Deletes matching entries in maps or arrays. diff --git a/pkg/yqlib/doc/headers/Document Index.md b/pkg/yqlib/doc/headers/document-index.md similarity index 54% rename from pkg/yqlib/doc/headers/Document Index.md rename to pkg/yqlib/doc/headers/document-index.md index 2f29853b..8b2e655c 100644 --- a/pkg/yqlib/doc/headers/Document Index.md +++ b/pkg/yqlib/doc/headers/document-index.md @@ -1 +1,3 @@ -Use the `documentIndex` operator (or the `di` shorthand) to select nodes of a particular document. \ No newline at end of file +# Document Index + +Use the `documentIndex` operator (or the `di` shorthand) to select nodes of a particular document. diff --git a/pkg/yqlib/doc/headers/Entries.md b/pkg/yqlib/doc/headers/entries.md similarity index 66% rename from pkg/yqlib/doc/headers/Entries.md rename to pkg/yqlib/doc/headers/entries.md index f13824be..38ab12d9 100644 --- a/pkg/yqlib/doc/headers/Entries.md +++ b/pkg/yqlib/doc/headers/entries.md @@ -1 +1,3 @@ -Similar to the same named functions in `jq` these functions convert to/from an object and an array of key-value pairs. This is most useful for performing operations on keys of maps. \ No newline at end of file +# Entries + +Similar to the same named functions in `jq` these functions convert to/from an object and an array of key-value pairs. This is most useful for performing operations on keys of maps. diff --git a/pkg/yqlib/doc/headers/Env Variable Operators.md b/pkg/yqlib/doc/headers/env-variable-operators.md similarity index 94% rename from pkg/yqlib/doc/headers/Env Variable Operators.md rename to pkg/yqlib/doc/headers/env-variable-operators.md index d18e72fc..ed85e2a4 100644 --- a/pkg/yqlib/doc/headers/Env Variable Operators.md +++ b/pkg/yqlib/doc/headers/env-variable-operators.md @@ -1,2 +1,3 @@ -This operator is used to handle environment variables usage in path expressions. While environment variables can, of course, be passed in via your CLI with string interpolation, this often comes with complex quote escaping and can be tricky to write and read. Note that there are two forms, `env` which will parse the environment variable as a yaml (be it a map, array, string, number of boolean) and `strenv` which will always parse the argument as a string. +# Env Variable Operators +This operator is used to handle environment variables usage in path expressions. While environment variables can, of course, be passed in via your CLI with string interpolation, this often comes with complex quote escaping and can be tricky to write and read. Note that there are two forms, `env` which will parse the environment variable as a yaml (be it a map, array, string, number of boolean) and `strenv` which will always parse the argument as a string. diff --git a/pkg/yqlib/doc/headers/Equals.md b/pkg/yqlib/doc/headers/equals.md similarity index 51% rename from pkg/yqlib/doc/headers/Equals.md rename to pkg/yqlib/doc/headers/equals.md index 5e68bc50..580563e4 100644 --- a/pkg/yqlib/doc/headers/Equals.md +++ b/pkg/yqlib/doc/headers/equals.md @@ -1,4 +1,6 @@ -This is a boolean operator that will return ```true``` if the LHS is equal to the RHS and ``false`` otherwise. +# Equals + +This is a boolean operator that will return `true` if the LHS is equal to the RHS and `false` otherwise. ``` .a == .b @@ -9,4 +11,3 @@ It is most often used with the select operator to find particular nodes: ``` select(.a == .b) ``` - diff --git a/pkg/yqlib/doc/headers/File Operators.md b/pkg/yqlib/doc/headers/file-operators.md similarity index 96% rename from pkg/yqlib/doc/headers/File Operators.md rename to pkg/yqlib/doc/headers/file-operators.md index baa80755..7729d941 100644 --- a/pkg/yqlib/doc/headers/File Operators.md +++ b/pkg/yqlib/doc/headers/file-operators.md @@ -1,3 +1,5 @@ +# File Operators + File operators are most often used with merge when needing to merge specific files together. Note that when doing this, you will need to use `eval-all` to ensure all yaml documents are loaded into memory before performing the merge (as opposed to `eval` which runs the expression once per document). Note that the `fileIndex` operator has a short alias of `fi`. @@ -6,4 +8,4 @@ Note that the `fileIndex` operator has a short alias of `fi`. Note the use of eval-all to ensure all documents are loaded into memory. ```bash yq eval-all 'select(fi == 0) * select(filename == "file2.yaml")' file1.yaml file2.yaml -``` \ No newline at end of file +``` diff --git a/pkg/yqlib/doc/headers/Has.md b/pkg/yqlib/doc/headers/has.md similarity index 57% rename from pkg/yqlib/doc/headers/Has.md rename to pkg/yqlib/doc/headers/has.md index df898780..3be66fee 100644 --- a/pkg/yqlib/doc/headers/Has.md +++ b/pkg/yqlib/doc/headers/has.md @@ -1 +1,3 @@ -This is operation that returns true if the key exists in a map (or index in an array), false otherwise. \ No newline at end of file +# Has + +This is operation that returns true if the key exists in a map (or index in an array), false otherwise. diff --git a/pkg/yqlib/doc/headers/keys.md b/pkg/yqlib/doc/headers/keys.md new file mode 100644 index 00000000..63622601 --- /dev/null +++ b/pkg/yqlib/doc/headers/keys.md @@ -0,0 +1,3 @@ +# Keys + +Use the `keys` operator to return map keys or array indices. diff --git a/pkg/yqlib/doc/headers/Length.md b/pkg/yqlib/doc/headers/length.md similarity index 89% rename from pkg/yqlib/doc/headers/Length.md rename to pkg/yqlib/doc/headers/length.md index 7415c182..cb5bf09b 100644 --- a/pkg/yqlib/doc/headers/Length.md +++ b/pkg/yqlib/doc/headers/length.md @@ -1 +1,3 @@ +# Length + Returns the lengths of the nodes. Length is defined according to the type of the node. diff --git a/pkg/yqlib/doc/headers/Multiply (Merge).md b/pkg/yqlib/doc/headers/multiply-merge.md similarity index 97% rename from pkg/yqlib/doc/headers/Multiply (Merge).md rename to pkg/yqlib/doc/headers/multiply-merge.md index 37730575..5f6e079a 100644 --- a/pkg/yqlib/doc/headers/Multiply (Merge).md +++ b/pkg/yqlib/doc/headers/multiply-merge.md @@ -1,3 +1,5 @@ +# Multiply (Merge) + Like the multiple operator in jq, depending on the operands, this multiply operator will do different things. Currently numbers, arrays and objects are supported. ## Objects and arrays - merging diff --git a/pkg/yqlib/doc/headers/Path.md b/pkg/yqlib/doc/headers/path.md similarity index 97% rename from pkg/yqlib/doc/headers/Path.md rename to pkg/yqlib/doc/headers/path.md index 3347edea..8f4d0baf 100644 --- a/pkg/yqlib/doc/headers/Path.md +++ b/pkg/yqlib/doc/headers/path.md @@ -1,3 +1,5 @@ +# Path + The path operator can be used to get the traversal paths of matching nodes in an expression. The path is returned as an array, which if traversed in order will lead to the matching node. You can get the key/index of matching nodes by using the `path` operator to return the path array then piping that through `.[-1]` to get the last element of that array, the key. diff --git a/pkg/yqlib/doc/headers/Pipe.md b/pkg/yqlib/doc/headers/pipe.md similarity index 90% rename from pkg/yqlib/doc/headers/Pipe.md rename to pkg/yqlib/doc/headers/pipe.md index 6b3c7e84..cce80d7f 100644 --- a/pkg/yqlib/doc/headers/Pipe.md +++ b/pkg/yqlib/doc/headers/pipe.md @@ -1 +1,3 @@ +# Pipe + Pipe the results of an expression into another. Like the bash operator. diff --git a/pkg/yqlib/doc/headers/Recursive Descent (Glob).md b/pkg/yqlib/doc/headers/recursive-descent-glob.md similarity index 96% rename from pkg/yqlib/doc/headers/Recursive Descent (Glob).md rename to pkg/yqlib/doc/headers/recursive-descent-glob.md index 10bb6096..da42da3b 100644 --- a/pkg/yqlib/doc/headers/Recursive Descent (Glob).md +++ b/pkg/yqlib/doc/headers/recursive-descent-glob.md @@ -1,3 +1,5 @@ +# Recursive Descent (Glob) + This operator recursively matches (or globs) all children nodes given of a particular element, including that node itself. This is most often used to apply a filter recursively against all matches. It can be used in either the ## match values form `..` diff --git a/pkg/yqlib/doc/headers/Reduce.md b/pkg/yqlib/doc/headers/reduce.md similarity index 99% rename from pkg/yqlib/doc/headers/Reduce.md rename to pkg/yqlib/doc/headers/reduce.md index 24416e32..03386645 100644 --- a/pkg/yqlib/doc/headers/Reduce.md +++ b/pkg/yqlib/doc/headers/reduce.md @@ -1,3 +1,5 @@ +# Reduce + Reduce is a powerful way to process a collection of data into a new form. ``` @@ -18,4 +20,3 @@ On the RHS there is ``, the starting value of the accumulator and ` Reduce syntax in `yq` is a little different from `jq` - as `yq` (currently) isn't as sophisticated as `jq` and its only supports infix notation (e.g. a + b, where the operator is in the middle of the two parameters) - where as `jq` uses a mix of infix notation with _prefix_ notation (e.g. `reduce a b` is like writing `+ a b`). To that end, the reduce operator is called `ireduce` for backwards compatability if a `jq` like prefix version of `reduce` is ever added. - diff --git a/pkg/yqlib/doc/headers/Select.md b/pkg/yqlib/doc/headers/select.md similarity index 84% rename from pkg/yqlib/doc/headers/Select.md rename to pkg/yqlib/doc/headers/select.md index 3d1546f1..b1851e7e 100644 --- a/pkg/yqlib/doc/headers/Select.md +++ b/pkg/yqlib/doc/headers/select.md @@ -1 +1,3 @@ -Select is used to filter arrays and maps by a boolean expression. \ No newline at end of file +# Select + +Select is used to filter arrays and maps by a boolean expression. diff --git a/pkg/yqlib/doc/headers/Sort Keys.md b/pkg/yqlib/doc/headers/sort-keys.md similarity index 96% rename from pkg/yqlib/doc/headers/Sort Keys.md rename to pkg/yqlib/doc/headers/sort-keys.md index 00eb71a9..932b855b 100644 --- a/pkg/yqlib/doc/headers/Sort Keys.md +++ b/pkg/yqlib/doc/headers/sort-keys.md @@ -1,3 +1,5 @@ +# Sort Keys + The Sort Keys operator sorts maps by their keys (based on their string value). This operator does not do anything to arrays or scalars (so you can easily recursively apply it to all maps). Sort is particularly useful for diffing two different yaml documents: diff --git a/pkg/yqlib/doc/headers/Split into Documents.md b/pkg/yqlib/doc/headers/split-into-documents.md similarity index 100% rename from pkg/yqlib/doc/headers/Split into Documents.md rename to pkg/yqlib/doc/headers/split-into-documents.md diff --git a/pkg/yqlib/doc/headers/String Operators.md b/pkg/yqlib/doc/headers/string-operators.md similarity index 95% rename from pkg/yqlib/doc/headers/String Operators.md rename to pkg/yqlib/doc/headers/string-operators.md index b438bbf6..d361a87e 100644 --- a/pkg/yqlib/doc/headers/String Operators.md +++ b/pkg/yqlib/doc/headers/string-operators.md @@ -4,7 +4,7 @@ This uses golangs native regex functions under the hood - See https://github.com/google/re2/wiki/Syntax for the supported syntax. -# String blocks, bash and newlines +## String blocks, bash and newlines Bash is notorious for chomping on precious trailing newline characters, making it tricky to set strings with newlines properly. In particular, the `$( exp )` _will trim trailing newlines_. For instance to get this yaml: @@ -41,4 +41,4 @@ Similarly, if you're trying to set the content from a file, and want a trailing ``` IFS= read -rd '' output < <(cat my_file) output=$output ./yq e '.data.values = strenv(output)' first.yml -``` \ No newline at end of file +``` diff --git a/pkg/yqlib/doc/headers/Style.md b/pkg/yqlib/doc/headers/style.md similarity index 60% rename from pkg/yqlib/doc/headers/Style.md rename to pkg/yqlib/doc/headers/style.md index 08fc4d23..c31c328e 100644 --- a/pkg/yqlib/doc/headers/Style.md +++ b/pkg/yqlib/doc/headers/style.md @@ -1 +1,3 @@ -The style operator can be used to get or set the style of nodes (e.g. string style, yaml style) \ No newline at end of file +# Style + +The style operator can be used to get or set the style of nodes (e.g. string style, yaml style) diff --git a/pkg/yqlib/doc/headers/subtract.md b/pkg/yqlib/doc/headers/subtract.md new file mode 100644 index 00000000..37b12b84 --- /dev/null +++ b/pkg/yqlib/doc/headers/subtract.md @@ -0,0 +1,3 @@ +# Subtract + +You can use subtract to subtract numbers, as well as removing elements from an array. diff --git a/pkg/yqlib/doc/headers/Tag.md b/pkg/yqlib/doc/headers/tag.md similarity index 62% rename from pkg/yqlib/doc/headers/Tag.md rename to pkg/yqlib/doc/headers/tag.md index 0d329fc1..99d03477 100644 --- a/pkg/yqlib/doc/headers/Tag.md +++ b/pkg/yqlib/doc/headers/tag.md @@ -1 +1,3 @@ -The tag operator can be used to get or set the tag of nodes (e.g. `!!str`, `!!int`, `!!bool`). \ No newline at end of file +# Tag + +The tag operator can be used to get or set the tag of nodes (e.g. `!!str`, `!!int`, `!!bool`). diff --git a/pkg/yqlib/doc/headers/Traverse (Read).md b/pkg/yqlib/doc/headers/traverse-read.md similarity index 50% rename from pkg/yqlib/doc/headers/Traverse (Read).md rename to pkg/yqlib/doc/headers/traverse-read.md index 8420cada..6c012407 100644 --- a/pkg/yqlib/doc/headers/Traverse (Read).md +++ b/pkg/yqlib/doc/headers/traverse-read.md @@ -1 +1,3 @@ -This is the simplest (and perhaps most used) operator, it is used to navigate deeply into yaml structures. \ No newline at end of file +# Traverse (Read) + +This is the simplest (and perhaps most used) operator, it is used to navigate deeply into yaml structures. diff --git a/pkg/yqlib/doc/headers/union.md b/pkg/yqlib/doc/headers/union.md new file mode 100644 index 00000000..7967b9c1 --- /dev/null +++ b/pkg/yqlib/doc/headers/union.md @@ -0,0 +1,3 @@ +# Union + +This operator is used to combine different results together. diff --git a/pkg/yqlib/doc/headers/Unique.md b/pkg/yqlib/doc/headers/unique.md similarity index 85% rename from pkg/yqlib/doc/headers/Unique.md rename to pkg/yqlib/doc/headers/unique.md index 50999ce7..5735be21 100644 --- a/pkg/yqlib/doc/headers/Unique.md +++ b/pkg/yqlib/doc/headers/unique.md @@ -1 +1,3 @@ +# Unique + This is used to filter out duplicated items in an array. diff --git a/pkg/yqlib/doc/headers/Variable Operators.md b/pkg/yqlib/doc/headers/variable-operators.md similarity index 93% rename from pkg/yqlib/doc/headers/Variable Operators.md rename to pkg/yqlib/doc/headers/variable-operators.md index a76ccff4..fd918488 100644 --- a/pkg/yqlib/doc/headers/Variable Operators.md +++ b/pkg/yqlib/doc/headers/variable-operators.md @@ -1,3 +1,5 @@ +# Variable Operators + Like the `jq` equivalents, variables are sometimes required for the more complex expressions (or swapping values between fields). Note that there is also an additional `ref` operator that holds a reference (instead of a copy) of the path, allowing you to make multiple changes to the same path. diff --git a/pkg/yqlib/doc/headers/With.md b/pkg/yqlib/doc/headers/with.md similarity index 96% rename from pkg/yqlib/doc/headers/With.md rename to pkg/yqlib/doc/headers/with.md index eeeb26b9..f83adbd0 100644 --- a/pkg/yqlib/doc/headers/With.md +++ b/pkg/yqlib/doc/headers/with.md @@ -1 +1,3 @@ +# With + Use the `with` operator to conveniently make multiple updates to a deeply nested path, or to update array elements relatively to each other. The first argument expression sets the root context, and the second expression runs against that root context. diff --git a/pkg/yqlib/doc/keys.md b/pkg/yqlib/doc/keys.md new file mode 100644 index 00000000..9d1cea1b --- /dev/null +++ b/pkg/yqlib/doc/keys.md @@ -0,0 +1,36 @@ +# Keys + +Use the `keys` operator to return map keys or array indices. + +## Map keys +Given a sample.yml file of: +```yaml +dog: woof +cat: meow +``` +then +```bash +yq eval 'keys' sample.yml +``` +will output +```yaml +- dog +- cat +``` + +## Array keys +Given a sample.yml file of: +```yaml +- apple +- banana +``` +then +```bash +yq eval 'keys' sample.yml +``` +will output +```yaml +- 0 +- 1 +``` + diff --git a/pkg/yqlib/doc/length.md b/pkg/yqlib/doc/length.md new file mode 100644 index 00000000..b4e78276 --- /dev/null +++ b/pkg/yqlib/doc/length.md @@ -0,0 +1,70 @@ +# Length + +Returns the lengths of the nodes. Length is defined according to the type of the node. + +## String length +returns length of string + +Given a sample.yml file of: +```yaml +a: cat +``` +then +```bash +yq eval '.a | length' sample.yml +``` +will output +```yaml +3 +``` + +## null length +Given a sample.yml file of: +```yaml +a: null +``` +then +```bash +yq eval '.a | length' sample.yml +``` +will output +```yaml +0 +``` + +## Map length +returns number of entries + +Given a sample.yml file of: +```yaml +a: cat +c: dog +``` +then +```bash +yq eval 'length' sample.yml +``` +will output +```yaml +2 +``` + +## Array length +returns number of elements + +Given a sample.yml file of: +```yaml +- 2 +- 4 +- 6 +- 8 +``` +then +```bash +yq eval 'length' sample.yml +``` +will output +```yaml +4 +``` + diff --git a/pkg/yqlib/doc/multiply-merge.md b/pkg/yqlib/doc/multiply-merge.md new file mode 100644 index 00000000..ffc36b11 --- /dev/null +++ b/pkg/yqlib/doc/multiply-merge.md @@ -0,0 +1,368 @@ +# Multiply (Merge) + +Like the multiple operator in jq, depending on the operands, this multiply operator will do different things. Currently numbers, arrays and objects are supported. + +## Objects and arrays - merging +Objects are merged deeply matching on matching keys. By default, array values override and are not deeply merged. + +Note that when merging objects, this operator returns the merged object (not the parent). This will be clearer in the examples below. + +### Merge Flags +You can control how objects are merged by using one or more of the following flags. Multiple flags can be used together, e.g. `.a *+? .b`. See examples below + +- `+` to append arrays +- `?` to only merge existing fields +- `d` to deeply merge arrays + +### Merging files +Note the use of `eval-all` to ensure all documents are loaded into memory. + +```bash +yq eval-all 'select(fileIndex == 0) * select(fileIndex == 1)' file1.yaml file2.yaml +``` + +## Multiply integers +Running +```bash +yq eval --null-input '3 * 4' +``` +will output +```yaml +12 +``` + +## Merge objects together, returning merged result only +Given a sample.yml file of: +```yaml +a: + field: me + fieldA: cat +b: + field: + g: wizz + fieldB: dog +``` +then +```bash +yq eval '.a * .b' sample.yml +``` +will output +```yaml +field: + g: wizz +fieldA: cat +fieldB: dog +``` + +## Merge objects together, returning parent object +Given a sample.yml file of: +```yaml +a: + field: me + fieldA: cat +b: + field: + g: wizz + fieldB: dog +``` +then +```bash +yq eval '. * {"a":.b}' sample.yml +``` +will output +```yaml +a: + field: + g: wizz + fieldA: cat + fieldB: dog +b: + field: + g: wizz + fieldB: dog +``` + +## Merge keeps style of LHS +Given a sample.yml file of: +```yaml +a: {things: great} +b: + also: "me" +``` +then +```bash +yq eval '. * {"a":.b}' sample.yml +``` +will output +```yaml +a: {things: great, also: "me"} +b: + also: "me" +``` + +## Merge arrays +Given a sample.yml file of: +```yaml +a: + - 1 + - 2 + - 3 +b: + - 3 + - 4 + - 5 +``` +then +```bash +yq eval '. * {"a":.b}' sample.yml +``` +will output +```yaml +a: + - 3 + - 4 + - 5 +b: + - 3 + - 4 + - 5 +``` + +## Merge, only existing fields +Given a sample.yml file of: +```yaml +a: + thing: one + cat: frog +b: + missing: two + thing: two +``` +then +```bash +yq eval '.a *? .b' sample.yml +``` +will output +```yaml +thing: two +cat: frog +``` + +## Merge, appending arrays +Given a sample.yml file of: +```yaml +a: + array: + - 1 + - 2 + - animal: dog + value: coconut +b: + array: + - 3 + - 4 + - animal: cat + value: banana +``` +then +```bash +yq eval '.a *+ .b' sample.yml +``` +will output +```yaml +array: + - 1 + - 2 + - animal: dog + - 3 + - 4 + - animal: cat +value: banana +``` + +## Merge, only existing fields, appending arrays +Given a sample.yml file of: +```yaml +a: + thing: + - 1 + - 2 +b: + thing: + - 3 + - 4 + another: + - 1 +``` +then +```bash +yq eval '.a *?+ .b' sample.yml +``` +will output +```yaml +thing: + - 1 + - 2 + - 3 + - 4 +``` + +## Merge, deeply merging arrays +Merging arrays deeply means arrays are merge like objects, with indexes as their key. In this case, we merge the first item in the array, and do nothing with the second. + +Given a sample.yml file of: +```yaml +a: + - name: fred + age: 12 + - name: bob + age: 32 +b: + - name: fred + age: 34 +``` +then +```bash +yq eval '.a *d .b' sample.yml +``` +will output +```yaml +- name: fred + age: 34 +- name: bob + age: 32 +``` + +## Merge arrays of objects together, matching on a key +It's a complex command, the trickyness comes from needing to have the right context in the expressions. +First we save the second array into a variable '$two' which lets us reference it later. +We then need to update the first array. We will use the relative update (|=) because we need to update relative to the current element of the array in the LHS in the RHS expression. +We set the current element of the first array as $cur. Now we multiply (merge) $cur with the matching entry in $two, by passing $two through a select filter. + + +Given a sample.yml file of: +```yaml +- a: apple + b: appleB +- a: kiwi + b: kiwiB +- a: banana + b: bananaB +``` +And another sample another.yml file of: +```yaml +- a: banana + c: bananaC +- a: apple + b: appleB2 +- a: dingo + c: dingoC +``` +then +```bash +yq eval-all '(select(fi==1) | .[]) as $two | select(fi==0) | .[] |= (. as $cur | $cur * ($two | select(.a == $cur.a)))' sample.yml another.yml +``` +will output +```yaml +- a: apple + b: appleB2 +- a: kiwi + b: kiwiB +- a: banana + b: bananaB + c: bananaC +``` + +## Merge to prefix an element +Given a sample.yml file of: +```yaml +a: cat +b: dog +``` +then +```bash +yq eval '. * {"a": {"c": .a}}' sample.yml +``` +will output +```yaml +a: + c: cat +b: dog +``` + +## Merge with simple aliases +Given a sample.yml file of: +```yaml +a: &cat + c: frog +b: + f: *cat +c: + g: thongs +``` +then +```bash +yq eval '.c * .b' sample.yml +``` +will output +```yaml +g: thongs +f: *cat +``` + +## Merge copies anchor names +Given a sample.yml file of: +```yaml +a: + c: &cat frog +b: + f: *cat +c: + g: thongs +``` +then +```bash +yq eval '.c * .a' sample.yml +``` +will output +```yaml +g: thongs +c: &cat frog +``` + +## Merge with merge anchors +Given a sample.yml file of: +```yaml +foo: &foo + a: foo_a + thing: foo_thing + c: foo_c +bar: &bar + b: bar_b + thing: bar_thing + c: bar_c +foobarList: + b: foobarList_b + !!merge <<: + - *foo + - *bar + c: foobarList_c +foobar: + c: foobar_c + !!merge <<: *foo + thing: foobar_thing +``` +then +```bash +yq eval '.foobar * .foobarList' sample.yml +``` +will output +```yaml +c: foobarList_c +!!merge <<: + - *foo + - *bar +thing: foobar_thing +b: foobarList_b +``` + diff --git a/pkg/yqlib/doc/path.md b/pkg/yqlib/doc/path.md new file mode 100644 index 00000000..3f8313bf --- /dev/null +++ b/pkg/yqlib/doc/path.md @@ -0,0 +1,94 @@ +# Path + +The path operator can be used to get the traversal paths of matching nodes in an expression. The path is returned as an array, which if traversed in order will lead to the matching node. + +You can get the key/index of matching nodes by using the `path` operator to return the path array then piping that through `.[-1]` to get the last element of that array, the key. + +## Map path +Given a sample.yml file of: +```yaml +a: + b: cat +``` +then +```bash +yq eval '.a.b | path' sample.yml +``` +will output +```yaml +- a +- b +``` + +## Get map key +Given a sample.yml file of: +```yaml +a: + b: cat +``` +then +```bash +yq eval '.a.b | path | .[-1]' sample.yml +``` +will output +```yaml +b +``` + +## Array path +Given a sample.yml file of: +```yaml +a: + - cat + - dog +``` +then +```bash +yq eval '.a.[] | select(. == "dog") | path' sample.yml +``` +will output +```yaml +- a +- 1 +``` + +## Get array index +Given a sample.yml file of: +```yaml +a: + - cat + - dog +``` +then +```bash +yq eval '.a.[] | select(. == "dog") | path | .[-1]' sample.yml +``` +will output +```yaml +1 +``` + +## Print path and value +Given a sample.yml file of: +```yaml +a: + - cat + - dog + - frog +``` +then +```bash +yq eval '.a.[] | select(. == "*og") | [{"path":path, "value":.}]' sample.yml +``` +will output +```yaml +- path: + - a + - 1 + value: dog +- path: + - a + - 2 + value: frog +``` + diff --git a/pkg/yqlib/doc/pipe.md b/pkg/yqlib/doc/pipe.md new file mode 100644 index 00000000..b6da327d --- /dev/null +++ b/pkg/yqlib/doc/pipe.md @@ -0,0 +1,37 @@ +# Pipe + +Pipe the results of an expression into another. Like the bash operator. + +## Simple Pipe +Given a sample.yml file of: +```yaml +a: + b: cat +``` +then +```bash +yq eval '.a | .b' sample.yml +``` +will output +```yaml +cat +``` + +## Multiple updates +Given a sample.yml file of: +```yaml +a: cow +b: sheep +c: same +``` +then +```bash +yq eval '.a = "cat" | .b = "dog"' sample.yml +``` +will output +```yaml +a: cat +b: dog +c: same +``` + diff --git a/pkg/yqlib/doc/recursive-descent-glob.md b/pkg/yqlib/doc/recursive-descent-glob.md new file mode 100644 index 00000000..4edede1e --- /dev/null +++ b/pkg/yqlib/doc/recursive-descent-glob.md @@ -0,0 +1,156 @@ +# Recursive Descent (Glob) + +This operator recursively matches (or globs) all children nodes given of a particular element, including that node itself. This is most often used to apply a filter recursively against all matches. It can be used in either the + +## match values form `..` +This will, like the `jq` equivalent, recursively match all _value_ nodes. Use it to find/manipulate particular values. + +For instance to set the `style` of all _value_ nodes in a yaml doc, excluding map keys: + +```bash +yq eval '.. style= "flow"' file.yaml +``` + +## match values and map keys form `...` +The also includes map keys in the results set. This is particularly useful in YAML as unlike JSON, map keys can have their own styling, tags and use anchors and aliases. + +For instance to set the `style` of all nodes in a yaml doc, including the map keys: + +```bash +yq eval '... style= "flow"' file.yaml +``` +## Recurse map (values only) +Given a sample.yml file of: +```yaml +a: frog +``` +then +```bash +yq eval '..' sample.yml +``` +will output +```yaml +a: frog +frog +``` + +## Recursively find nodes with keys +Note that this example has wrapped the expression in `[]` to show that there are two matches returned. You do not have to wrap in `[]` in your path expression. + +Given a sample.yml file of: +```yaml +a: + name: frog + b: + name: blog + age: 12 +``` +then +```bash +yq eval '[.. | select(has("name"))]' sample.yml +``` +will output +```yaml +- name: frog + b: + name: blog + age: 12 +- name: blog + age: 12 +``` + +## Recursively find nodes with values +Given a sample.yml file of: +```yaml +a: + nameA: frog + b: + nameB: frog + age: 12 +``` +then +```bash +yq eval '.. | select(. == "frog")' sample.yml +``` +will output +```yaml +frog +frog +``` + +## Recurse map (values and keys) +Note that the map key appears in the results + +Given a sample.yml file of: +```yaml +a: frog +``` +then +```bash +yq eval '...' sample.yml +``` +will output +```yaml +a: frog +a +frog +``` + +## Aliases are not traversed +Given a sample.yml file of: +```yaml +a: &cat + c: frog +b: *cat +``` +then +```bash +yq eval '[..]' sample.yml +``` +will output +```yaml +- a: &cat + c: frog + b: *cat +- &cat + c: frog +- frog +- *cat +``` + +## Merge docs are not traversed +Given a sample.yml file of: +```yaml +foo: &foo + a: foo_a + thing: foo_thing + c: foo_c +bar: &bar + b: bar_b + thing: bar_thing + c: bar_c +foobarList: + b: foobarList_b + !!merge <<: + - *foo + - *bar + c: foobarList_c +foobar: + c: foobar_c + !!merge <<: *foo + thing: foobar_thing +``` +then +```bash +yq eval '.foobar | [..]' sample.yml +``` +will output +```yaml +- c: foobar_c + !!merge <<: *foo + thing: foobar_thing +- foobar_c +- *foo +- foobar_thing +``` + diff --git a/pkg/yqlib/doc/reduce.md b/pkg/yqlib/doc/reduce.md new file mode 100644 index 00000000..1cb9f592 --- /dev/null +++ b/pkg/yqlib/doc/reduce.md @@ -0,0 +1,77 @@ +# Reduce + +Reduce is a powerful way to process a collection of data into a new form. + +``` + as $ ireduce (; ) +``` + +e.g. + +``` +.[] as $item ireduce (0; . + $item) +``` + +On the LHS we are configuring the collection of items that will be reduced `` as well as what each element will be called `$`. Note that the array has been splatted into its individual elements. + +On the RHS there is ``, the starting value of the accumulator and ``, the expression that will update the accumulator for each element in the collection. Note that within the block expression, `.` will evaluate to the current value of the accumulator. + +## yq vs jq syntax +Reduce syntax in `yq` is a little different from `jq` - as `yq` (currently) isn't as sophisticated as `jq` and its only supports infix notation (e.g. a + b, where the operator is in the middle of the two parameters) - where as `jq` uses a mix of infix notation with _prefix_ notation (e.g. `reduce a b` is like writing `+ a b`). + +To that end, the reduce operator is called `ireduce` for backwards compatability if a `jq` like prefix version of `reduce` is ever added. + +## Sum numbers +Given a sample.yml file of: +```yaml +- 10 +- 2 +- 5 +- 3 +``` +then +```bash +yq eval '.[] as $item ireduce (0; . + $item)' sample.yml +``` +will output +```yaml +20 +``` + +## Merge all yaml files together +Given a sample.yml file of: +```yaml +a: cat +``` +And another sample another.yml file of: +```yaml +b: dog +``` +then +```bash +yq eval-all '. as $item ireduce ({}; . * $item )' sample.yml another.yml +``` +will output +```yaml +a: cat +b: dog +``` + +## Convert an array to an object +Given a sample.yml file of: +```yaml +- name: Cathy + has: apples +- name: Bob + has: bananas +``` +then +```bash +yq eval '.[] as $item ireduce ({}; .[$item | .name] = ($item | .has) )' sample.yml +``` +will output +```yaml +Cathy: apples +Bob: bananas +``` + diff --git a/pkg/yqlib/doc/select.md b/pkg/yqlib/doc/select.md new file mode 100644 index 00000000..aabed120 --- /dev/null +++ b/pkg/yqlib/doc/select.md @@ -0,0 +1,41 @@ +# Select + +Select is used to filter arrays and maps by a boolean expression. + +## Select elements from array +Given a sample.yml file of: +```yaml +- cat +- goat +- dog +``` +then +```bash +yq eval '.[] | select(. == "*at")' sample.yml +``` +will output +```yaml +cat +goat +``` + +## Select and update matching values in map +Given a sample.yml file of: +```yaml +a: + things: cat + bob: goat + horse: dog +``` +then +```bash +yq eval '(.a.[] | select(. == "*at")) |= "rabbit"' sample.yml +``` +will output +```yaml +a: + things: rabbit + bob: rabbit + horse: dog +``` + diff --git a/pkg/yqlib/doc/sort-keys.md b/pkg/yqlib/doc/sort-keys.md new file mode 100644 index 00000000..e8f4d6dc --- /dev/null +++ b/pkg/yqlib/doc/sort-keys.md @@ -0,0 +1,70 @@ +# Sort Keys + +The Sort Keys operator sorts maps by their keys (based on their string value). This operator does not do anything to arrays or scalars (so you can easily recursively apply it to all maps). + +Sort is particularly useful for diffing two different yaml documents: + +```bash +yq eval -i 'sortKeys(..)' file1.yml +yq eval -i 'sortKeys(..)' file2.yml +diff file1.yml file2.yml +``` + +## Sort keys of map +Given a sample.yml file of: +```yaml +c: frog +a: blah +b: bing +``` +then +```bash +yq eval 'sortKeys(.)' sample.yml +``` +will output +```yaml +a: blah +b: bing +c: frog +``` + +## Sort keys recursively +Note the array elements are left unsorted, but maps inside arrays are sorted + +Given a sample.yml file of: +```yaml +bParent: + c: dog + array: + - 3 + - 1 + - 2 +aParent: + z: donkey + x: + - c: yum + b: delish + - b: ew + a: apple +``` +then +```bash +yq eval 'sortKeys(..)' sample.yml +``` +will output +```yaml +aParent: + x: + - b: delish + c: yum + - a: apple + b: ew + z: donkey +bParent: + array: + - 3 + - 1 + - 2 + c: dog +``` + diff --git a/pkg/yqlib/doc/split-into-documents.md b/pkg/yqlib/doc/split-into-documents.md new file mode 100644 index 00000000..a902b43c --- /dev/null +++ b/pkg/yqlib/doc/split-into-documents.md @@ -0,0 +1,31 @@ +# Split into Documents + +This operator splits all matches into separate documents + +## Split empty +Running +```bash +yq eval --null-input 'splitDoc' +``` +will output +```yaml + +``` + +## Split array +Given a sample.yml file of: +```yaml +- a: cat +- b: dog +``` +then +```bash +yq eval '.[] | splitDoc' sample.yml +``` +will output +```yaml +a: cat +--- +b: dog +``` + diff --git a/pkg/yqlib/doc/string-operators.md b/pkg/yqlib/doc/string-operators.md new file mode 100644 index 00000000..dafe6f30 --- /dev/null +++ b/pkg/yqlib/doc/string-operators.md @@ -0,0 +1,296 @@ +# String Operators + +## RegEx +This uses golangs native regex functions under the hood - See https://github.com/google/re2/wiki/Syntax for the supported syntax. + + +## String blocks, bash and newlines +Bash is notorious for chomping on precious trailing newline characters, making it tricky to set strings with newlines properly. In particular, the `$( exp )` _will trim trailing newlines_. + +For instance to get this yaml: + +``` +a: | + cat +``` + +Using `$( exp )` wont work, as it will trim the trailing new line. + +``` +m=$(echo "cat\n") yq e -n '.a = strenv(m)' +a: cat +``` + +However, using printf works: +``` +printf -v m "cat\n" ; m="$m" yq e -n '.a = strenv(m)' +a: | + cat +``` + +As well as having multiline expressions: +``` +m="cat +" yq e -n '.a = strenv(m)' +a: | + cat +``` + +Similarly, if you're trying to set the content from a file, and want a trailing new line: + +``` +IFS= read -rd '' output < <(cat my_file) +output=$output ./yq e '.data.values = strenv(output)' first.yml +``` + +## Join strings +Given a sample.yml file of: +```yaml +- cat +- meow +- 1 +- null +- true +``` +then +```bash +yq eval 'join("; ")' sample.yml +``` +will output +```yaml +cat; meow; 1; ; true +``` + +## Match string +Given a sample.yml file of: +```yaml +foo bar foo +``` +then +```bash +yq eval 'match("foo")' sample.yml +``` +will output +```yaml +string: foo +offset: 0 +length: 3 +captures: [] +``` + +## Match string, case insensitive +Given a sample.yml file of: +```yaml +foo bar FOO +``` +then +```bash +yq eval '[match("(?i)foo"; "g")]' sample.yml +``` +will output +```yaml +- string: foo + offset: 0 + length: 3 + captures: [] +- string: FOO + offset: 8 + length: 3 + captures: [] +``` + +## Match with capture groups +Given a sample.yml file of: +```yaml +abc abc +``` +then +```bash +yq eval '[match("(abc)+"; "g")]' sample.yml +``` +will output +```yaml +- string: abc + offset: 0 + length: 3 + captures: + - string: abc + offset: 0 + length: 3 +- string: abc + offset: 4 + length: 3 + captures: + - string: abc + offset: 4 + length: 3 +``` + +## Match with named capture groups +Given a sample.yml file of: +```yaml +foo bar foo foo foo +``` +then +```bash +yq eval '[match("foo (?Pbar)? foo"; "g")]' sample.yml +``` +will output +```yaml +- string: foo bar foo + offset: 0 + length: 11 + captures: + - string: bar + offset: 4 + length: 3 + name: bar123 +- string: foo foo + offset: 12 + length: 8 + captures: + - string: null + offset: -1 + length: 0 + name: bar123 +``` + +## Capture named groups into a map +Given a sample.yml file of: +```yaml +xyzzy-14 +``` +then +```bash +yq eval 'capture("(?P[a-z]+)-(?P[0-9]+)")' sample.yml +``` +will output +```yaml +a: xyzzy +n: "14" +``` + +## Match without global flag +Given a sample.yml file of: +```yaml +cat cat +``` +then +```bash +yq eval 'match("cat")' sample.yml +``` +will output +```yaml +string: cat +offset: 0 +length: 3 +captures: [] +``` + +## Match with global flag +Given a sample.yml file of: +```yaml +cat cat +``` +then +```bash +yq eval '[match("cat"; "g")]' sample.yml +``` +will output +```yaml +- string: cat + offset: 0 + length: 3 + captures: [] +- string: cat + offset: 4 + length: 3 + captures: [] +``` + +## Test using regex +Like jq'q equivalant, this works like match but only returns true/false instead of full match details + +Given a sample.yml file of: +```yaml +- cat +- dog +``` +then +```bash +yq eval '.[] | test("at")' sample.yml +``` +will output +```yaml +true +false +``` + +## Substitute / Replace string +This uses golang regex, described [here](https://github.com/google/re2/wiki/Syntax) +Note the use of `|=` to run in context of the current string value. + +Given a sample.yml file of: +```yaml +a: dogs are great +``` +then +```bash +yq eval '.a |= sub("dogs", "cats")' sample.yml +``` +will output +```yaml +a: cats are great +``` + +## Substitute / Replace string with regex +This uses golang regex, described [here](https://github.com/google/re2/wiki/Syntax) +Note the use of `|=` to run in context of the current string value. + +Given a sample.yml file of: +```yaml +a: cat +b: heat +``` +then +```bash +yq eval '.[] |= sub("(a)", "${1}r")' sample.yml +``` +will output +```yaml +a: cart +b: heart +``` + +## Split strings +Given a sample.yml file of: +```yaml +cat; meow; 1; ; true +``` +then +```bash +yq eval 'split("; ")' sample.yml +``` +will output +```yaml +- cat +- meow +- "1" +- "" +- "true" +``` + +## Split strings one match +Given a sample.yml file of: +```yaml +word +``` +then +```bash +yq eval 'split("; ")' sample.yml +``` +will output +```yaml +- word +``` + diff --git a/pkg/yqlib/doc/style.md b/pkg/yqlib/doc/style.md new file mode 100644 index 00000000..3bc3e4c1 --- /dev/null +++ b/pkg/yqlib/doc/style.md @@ -0,0 +1,240 @@ +# Style + +The style operator can be used to get or set the style of nodes (e.g. string style, yaml style) + +## Update and set style of a particular node (simple) +Given a sample.yml file of: +```yaml +a: + b: thing + c: something +``` +then +```bash +yq eval '.a.b = "new" | .a.b style="double"' sample.yml +``` +will output +```yaml +a: + b: "new" + c: something +``` + +## Update and set style of a particular node using path variables +Given a sample.yml file of: +```yaml +a: + b: thing + c: something +``` +then +```bash +yq eval 'with(.a.b ; . = "new" | . style="double")' sample.yml +``` +will output +```yaml +a: + b: "new" + c: something +``` + +## Set tagged style +Given a sample.yml file of: +```yaml +a: cat +b: 5 +c: 3.2 +e: true +``` +then +```bash +yq eval '.. style="tagged"' sample.yml +``` +will output +```yaml +!!map +a: !!str cat +b: !!int 5 +c: !!float 3.2 +e: !!bool true +``` + +## Set double quote style +Given a sample.yml file of: +```yaml +a: cat +b: 5 +c: 3.2 +e: true +``` +then +```bash +yq eval '.. style="double"' sample.yml +``` +will output +```yaml +a: "cat" +b: "5" +c: "3.2" +e: "true" +``` + +## Set double quote style on map keys too +Given a sample.yml file of: +```yaml +a: cat +b: 5 +c: 3.2 +e: true +``` +then +```bash +yq eval '... style="double"' sample.yml +``` +will output +```yaml +"a": "cat" +"b": "5" +"c": "3.2" +"e": "true" +``` + +## Set single quote style +Given a sample.yml file of: +```yaml +a: cat +b: 5 +c: 3.2 +e: true +``` +then +```bash +yq eval '.. style="single"' sample.yml +``` +will output +```yaml +a: 'cat' +b: '5' +c: '3.2' +e: 'true' +``` + +## Set literal quote style +Given a sample.yml file of: +```yaml +a: cat +b: 5 +c: 3.2 +e: true +``` +then +```bash +yq eval '.. style="literal"' sample.yml +``` +will output +```yaml +a: |- + cat +b: |- + 5 +c: |- + 3.2 +e: |- + true +``` + +## Set folded quote style +Given a sample.yml file of: +```yaml +a: cat +b: 5 +c: 3.2 +e: true +``` +then +```bash +yq eval '.. style="folded"' sample.yml +``` +will output +```yaml +a: >- + cat +b: >- + 5 +c: >- + 3.2 +e: >- + true +``` + +## Set flow quote style +Given a sample.yml file of: +```yaml +a: cat +b: 5 +c: 3.2 +e: true +``` +then +```bash +yq eval '.. style="flow"' sample.yml +``` +will output +```yaml +{a: cat, b: 5, c: 3.2, e: true} +``` + +## Reset style - or pretty print +Set empty (default) quote style, note the usage of `...` to match keys too. Note that there is a `--prettyPrint/-P` short flag for this. + +Given a sample.yml file of: +```yaml +a: cat +"b": 5 +'c': 3.2 +"e": true +``` +then +```bash +yq eval '... style=""' sample.yml +``` +will output +```yaml +a: cat +b: 5 +c: 3.2 +e: true +``` + +## Set style relatively with assign-update +Given a sample.yml file of: +```yaml +a: single +b: double +``` +then +```bash +yq eval '.[] style |= .' sample.yml +``` +will output +```yaml +a: 'single' +b: "double" +``` + +## Read style +Given a sample.yml file of: +```yaml +{a: "cat", b: 'thing'} +``` +then +```bash +yq eval '.. | style' sample.yml +``` +will output +```yaml +flow +double +single +``` + diff --git a/pkg/yqlib/doc/subtract.md b/pkg/yqlib/doc/subtract.md new file mode 100644 index 00000000..bcc93615 --- /dev/null +++ b/pkg/yqlib/doc/subtract.md @@ -0,0 +1,113 @@ +# Subtract + +You can use subtract to subtract numbers, as well as removing elements from an array. + +## Array subtraction +Running +```bash +yq eval --null-input '[1,2] - [2,3]' +``` +will output +```yaml +- 1 +``` + +## Array subtraction with nested array +Running +```bash +yq eval --null-input '[[1], 1, 2] - [[1], 3]' +``` +will output +```yaml +- 1 +- 2 +``` + +## Array subtraction with nested object +Note that order of the keys does not matter + +Given a sample.yml file of: +```yaml +- a: b + c: d +- a: b +``` +then +```bash +yq eval '. - [{"c": "d", "a": "b"}]' sample.yml +``` +will output +```yaml +- a: b +``` + +## Number subtraction - float +If the lhs or rhs are floats then the expression will be calculated with floats. + +Given a sample.yml file of: +```yaml +a: 3 +b: 4.5 +``` +then +```bash +yq eval '.a = .a - .b' sample.yml +``` +will output +```yaml +a: -1.5 +b: 4.5 +``` + +## Number subtraction - float +If the lhs or rhs are floats then the expression will be calculated with floats. + +Given a sample.yml file of: +```yaml +a: 3 +b: 4.5 +``` +then +```bash +yq eval '.a = .a - .b' sample.yml +``` +will output +```yaml +a: -1.5 +b: 4.5 +``` + +## Number subtraction - int +If both the lhs and rhs are ints then the expression will be calculated with ints. + +Given a sample.yml file of: +```yaml +a: 3 +b: 4 +``` +then +```bash +yq eval '.a = .a - .b' sample.yml +``` +will output +```yaml +a: -1 +b: 4 +``` + +## Decrement numbers +Given a sample.yml file of: +```yaml +a: 3 +b: 5 +``` +then +```bash +yq eval '.[] -= 1' sample.yml +``` +will output +```yaml +a: 2 +b: 4 +``` + diff --git a/pkg/yqlib/doc/tag.md b/pkg/yqlib/doc/tag.md new file mode 100644 index 00000000..96895a67 --- /dev/null +++ b/pkg/yqlib/doc/tag.md @@ -0,0 +1,61 @@ +# Tag + +The tag operator can be used to get or set the tag of nodes (e.g. `!!str`, `!!int`, `!!bool`). + +## Get tag +Given a sample.yml file of: +```yaml +a: cat +b: 5 +c: 3.2 +e: true +f: [] +``` +then +```bash +yq eval '.. | tag' sample.yml +``` +will output +```yaml +!!map +!!str +!!int +!!float +!!bool +!!seq +``` + +## Set custom tag +Given a sample.yml file of: +```yaml +a: str +``` +then +```bash +yq eval '.a tag = "!!mikefarah"' sample.yml +``` +will output +```yaml +a: !!mikefarah str +``` + +## Find numbers and convert them to strings +Given a sample.yml file of: +```yaml +a: cat +b: 5 +c: 3.2 +e: true +``` +then +```bash +yq eval '(.. | select(tag == "!!int")) tag= "!!str"' sample.yml +``` +will output +```yaml +a: cat +b: "5" +c: 3.2 +e: true +``` + diff --git a/pkg/yqlib/doc/traverse-read.md b/pkg/yqlib/doc/traverse-read.md new file mode 100644 index 00000000..4cf25c3b --- /dev/null +++ b/pkg/yqlib/doc/traverse-read.md @@ -0,0 +1,485 @@ +# Traverse (Read) + +This is the simplest (and perhaps most used) operator, it is used to navigate deeply into yaml structures. + +## Simple map navigation +Given a sample.yml file of: +```yaml +a: + b: apple +``` +then +```bash +yq eval '.a' sample.yml +``` +will output +```yaml +b: apple +``` + +## Splat +Often used to pipe children into other operators + +Given a sample.yml file of: +```yaml +- b: apple +- c: banana +``` +then +```bash +yq eval '.[]' sample.yml +``` +will output +```yaml +b: apple +c: banana +``` + +## Optional Splat +Just like splat, but won't error if you run it against scalars + +Given a sample.yml file of: +```yaml +cat +``` +then +```bash +yq eval '.[]' sample.yml +``` +will output +```yaml +``` + +## Special characters +Use quotes with brackets around path elements with special characters + +Given a sample.yml file of: +```yaml +"{}": frog +``` +then +```bash +yq eval '.["{}"]' sample.yml +``` +will output +```yaml +frog +``` + +## Nested special characters +Given a sample.yml file of: +```yaml +a: + "key.withdots": + "another.key": apple +``` +then +```bash +yq eval '.a["key.withdots"]["another.key"]' sample.yml +``` +will output +```yaml +apple +``` + +## Keys with spaces +Use quotes with brackets around path elements with special characters + +Given a sample.yml file of: +```yaml +"red rabbit": frog +``` +then +```bash +yq eval '.["red rabbit"]' sample.yml +``` +will output +```yaml +frog +``` + +## Dynamic keys +Expressions within [] can be used to dynamically lookup / calculate keys + +Given a sample.yml file of: +```yaml +b: apple +apple: crispy yum +banana: soft yum +``` +then +```bash +yq eval '.[.b]' sample.yml +``` +will output +```yaml +crispy yum +``` + +## Children don't exist +Nodes are added dynamically while traversing + +Given a sample.yml file of: +```yaml +c: banana +``` +then +```bash +yq eval '.a.b' sample.yml +``` +will output +```yaml +null +``` + +## Optional identifier +Like jq, does not output an error when the yaml is not an array or object as expected + +Given a sample.yml file of: +```yaml +- 1 +- 2 +- 3 +``` +then +```bash +yq eval '.a?' sample.yml +``` +will output +```yaml +``` + +## Wildcard matching +Given a sample.yml file of: +```yaml +a: + cat: apple + mad: things +``` +then +```bash +yq eval '.a."*a*"' sample.yml +``` +will output +```yaml +apple +things +``` + +## Aliases +Given a sample.yml file of: +```yaml +a: &cat + c: frog +b: *cat +``` +then +```bash +yq eval '.b' sample.yml +``` +will output +```yaml +*cat +``` + +## Traversing aliases with splat +Given a sample.yml file of: +```yaml +a: &cat + c: frog +b: *cat +``` +then +```bash +yq eval '.b[]' sample.yml +``` +will output +```yaml +frog +``` + +## Traversing aliases explicitly +Given a sample.yml file of: +```yaml +a: &cat + c: frog +b: *cat +``` +then +```bash +yq eval '.b.c' sample.yml +``` +will output +```yaml +frog +``` + +## Traversing arrays by index +Given a sample.yml file of: +```yaml +- 1 +- 2 +- 3 +``` +then +```bash +yq eval '.[0]' sample.yml +``` +will output +```yaml +1 +``` + +## Traversing nested arrays by index +Given a sample.yml file of: +```yaml +[[], [cat]] +``` +then +```bash +yq eval '.[1][0]' sample.yml +``` +will output +```yaml +cat +``` + +## Maps with numeric keys +Given a sample.yml file of: +```yaml +2: cat +``` +then +```bash +yq eval '.[2]' sample.yml +``` +will output +```yaml +cat +``` + +## Maps with non existing numeric keys +Given a sample.yml file of: +```yaml +a: b +``` +then +```bash +yq eval '.[0]' sample.yml +``` +will output +```yaml +null +``` + +## Traversing merge anchors +Given a sample.yml file of: +```yaml +foo: &foo + a: foo_a + thing: foo_thing + c: foo_c +bar: &bar + b: bar_b + thing: bar_thing + c: bar_c +foobarList: + b: foobarList_b + !!merge <<: + - *foo + - *bar + c: foobarList_c +foobar: + c: foobar_c + !!merge <<: *foo + thing: foobar_thing +``` +then +```bash +yq eval '.foobar.a' sample.yml +``` +will output +```yaml +foo_a +``` + +## Traversing merge anchors with override +Given a sample.yml file of: +```yaml +foo: &foo + a: foo_a + thing: foo_thing + c: foo_c +bar: &bar + b: bar_b + thing: bar_thing + c: bar_c +foobarList: + b: foobarList_b + !!merge <<: + - *foo + - *bar + c: foobarList_c +foobar: + c: foobar_c + !!merge <<: *foo + thing: foobar_thing +``` +then +```bash +yq eval '.foobar.c' sample.yml +``` +will output +```yaml +foo_c +``` + +## Traversing merge anchors with local override +Given a sample.yml file of: +```yaml +foo: &foo + a: foo_a + thing: foo_thing + c: foo_c +bar: &bar + b: bar_b + thing: bar_thing + c: bar_c +foobarList: + b: foobarList_b + !!merge <<: + - *foo + - *bar + c: foobarList_c +foobar: + c: foobar_c + !!merge <<: *foo + thing: foobar_thing +``` +then +```bash +yq eval '.foobar.thing' sample.yml +``` +will output +```yaml +foobar_thing +``` + +## Splatting merge anchors +Given a sample.yml file of: +```yaml +foo: &foo + a: foo_a + thing: foo_thing + c: foo_c +bar: &bar + b: bar_b + thing: bar_thing + c: bar_c +foobarList: + b: foobarList_b + !!merge <<: + - *foo + - *bar + c: foobarList_c +foobar: + c: foobar_c + !!merge <<: *foo + thing: foobar_thing +``` +then +```bash +yq eval '.foobar[]' sample.yml +``` +will output +```yaml +foo_c +foo_a +foobar_thing +``` + +## Traversing merge anchor lists +Note that the later merge anchors override previous + +Given a sample.yml file of: +```yaml +foo: &foo + a: foo_a + thing: foo_thing + c: foo_c +bar: &bar + b: bar_b + thing: bar_thing + c: bar_c +foobarList: + b: foobarList_b + !!merge <<: + - *foo + - *bar + c: foobarList_c +foobar: + c: foobar_c + !!merge <<: *foo + thing: foobar_thing +``` +then +```bash +yq eval '.foobarList.thing' sample.yml +``` +will output +```yaml +bar_thing +``` + +## Splatting merge anchor lists +Given a sample.yml file of: +```yaml +foo: &foo + a: foo_a + thing: foo_thing + c: foo_c +bar: &bar + b: bar_b + thing: bar_thing + c: bar_c +foobarList: + b: foobarList_b + !!merge <<: + - *foo + - *bar + c: foobarList_c +foobar: + c: foobar_c + !!merge <<: *foo + thing: foobar_thing +``` +then +```bash +yq eval '.foobarList[]' sample.yml +``` +will output +```yaml +bar_b +foo_a +bar_thing +foobarList_c +``` + +## Select multiple indices +Given a sample.yml file of: +```yaml +a: + - a + - b + - c +``` +then +```bash +yq eval '.a[0, 2]' sample.yml +``` +will output +```yaml +a +c +``` + diff --git a/pkg/yqlib/doc/union.md b/pkg/yqlib/doc/union.md new file mode 100644 index 00000000..0c0abd38 --- /dev/null +++ b/pkg/yqlib/doc/union.md @@ -0,0 +1,33 @@ +# Union + +This operator is used to combine different results together. + +## Combine scalars +Running +```bash +yq eval --null-input '1, true, "cat"' +``` +will output +```yaml +1 +true +cat +``` + +## Combine selected paths +Given a sample.yml file of: +```yaml +a: fieldA +b: fieldB +c: fieldC +``` +then +```bash +yq eval '.a, .c' sample.yml +``` +will output +```yaml +fieldA +fieldC +``` + diff --git a/pkg/yqlib/doc/unique.md b/pkg/yqlib/doc/unique.md new file mode 100644 index 00000000..6be33dce --- /dev/null +++ b/pkg/yqlib/doc/unique.md @@ -0,0 +1,84 @@ +# Unique + +This is used to filter out duplicated items in an array. + +## Unique array of scalars (string/numbers) +Given a sample.yml file of: +```yaml +- 1 +- 2 +- 3 +- 2 +``` +then +```bash +yq eval 'unique' sample.yml +``` +will output +```yaml +- 1 +- 2 +- 3 +``` + +## Unique nulls +Unique works on the node value, so it considers different representations of nulls to be different + +Given a sample.yml file of: +```yaml +- ~ +- null +- ~ +- null +``` +then +```bash +yq eval 'unique' sample.yml +``` +will output +```yaml +- ~ +- null +``` + +## Unique all nulls +Run against the node tag to unique all the nulls + +Given a sample.yml file of: +```yaml +- ~ +- null +- ~ +- null +``` +then +```bash +yq eval 'unique_by(tag)' sample.yml +``` +will output +```yaml +- ~ +``` + +## Unique array object fields +Given a sample.yml file of: +```yaml +- name: harry + pet: cat +- name: billy + pet: dog +- name: harry + pet: dog +``` +then +```bash +yq eval 'unique_by(.name)' sample.yml +``` +will output +```yaml +- name: harry + pet: cat +- name: billy + pet: dog +``` + diff --git a/pkg/yqlib/doc/variable-operators.md b/pkg/yqlib/doc/variable-operators.md new file mode 100644 index 00000000..c2a0c224 --- /dev/null +++ b/pkg/yqlib/doc/variable-operators.md @@ -0,0 +1,98 @@ +# Variable Operators + +Like the `jq` equivalents, variables are sometimes required for the more complex expressions (or swapping values between fields). + +Note that there is also an additional `ref` operator that holds a reference (instead of a copy) of the path, allowing you to make multiple changes to the same path. + +## Single value variable +Given a sample.yml file of: +```yaml +a: cat +``` +then +```bash +yq eval '.a as $foo | $foo' sample.yml +``` +will output +```yaml +cat +``` + +## Multi value variable +Given a sample.yml file of: +```yaml +- cat +- dog +``` +then +```bash +yq eval '.[] as $foo | $foo' sample.yml +``` +will output +```yaml +cat +dog +``` + +## Using variables as a lookup +Example taken from [jq](https://stedolan.github.io/jq/manual/#Variable/SymbolicBindingOperator:...as$identifier|...) + +Given a sample.yml file of: +```yaml +"posts": + - "title": Frist psot + "author": anon + - "title": A well-written article + "author": person1 +"realnames": + "anon": Anonymous Coward + "person1": Person McPherson +``` +then +```bash +yq eval '.realnames as $names | .posts[] | {"title":.title, "author": $names[.author]}' sample.yml +``` +will output +```yaml +title: Frist psot +author: Anonymous Coward +title: A well-written article +author: Person McPherson +``` + +## Using variables to swap values +Given a sample.yml file of: +```yaml +a: a_value +b: b_value +``` +then +```bash +yq eval '.a as $x | .b as $y | .b = $x | .a = $y' sample.yml +``` +will output +```yaml +a: b_value +b: a_value +``` + +## Use ref to reference a path repeatedly +Note: You may find the `with` operator more useful. + +Given a sample.yml file of: +```yaml +a: + b: thing + c: something +``` +then +```bash +yq eval '.a.b ref $x | $x = "new" | $x style="double"' sample.yml +``` +will output +```yaml +a: + b: "new" + c: something +``` + diff --git a/pkg/yqlib/doc/with.md b/pkg/yqlib/doc/with.md new file mode 100644 index 00000000..a8224e60 --- /dev/null +++ b/pkg/yqlib/doc/with.md @@ -0,0 +1,64 @@ +# With + +Use the `with` operator to conveniently make multiple updates to a deeply nested path, or to update array elements relatively to each other. The first argument expression sets the root context, and the second expression runs against that root context. + +## Update and style +Given a sample.yml file of: +```yaml +a: + deeply: + nested: value +``` +then +```bash +yq eval 'with(.a.deeply.nested; . = "newValue" | . style="single")' sample.yml +``` +will output +```yaml +a: + deeply: + nested: 'newValue' +``` + +## Update multiple deeply nested properties +Given a sample.yml file of: +```yaml +a: + deeply: + nested: value + other: thing +``` +then +```bash +yq eval 'with(.a.deeply; .nested = "newValue" | .other= "newThing")' sample.yml +``` +will output +```yaml +a: + deeply: + nested: newValue + other: newThing +``` + +## Update array elements relatively +The second expression runs with each element of the array as it's contextual root. This allows you to make updates relative to the element. + +Given a sample.yml file of: +```yaml +myArray: + - a: apple + - a: banana +``` +then +```bash +yq eval 'with(.myArray[]; .b = .a + " yum")' sample.yml +``` +will output +```yaml +myArray: + - a: apple + b: apple yum + - a: banana + b: banana yum +``` + diff --git a/pkg/yqlib/operator_booleans_test.go b/pkg/yqlib/operator_booleans_test.go index 50708998..ac6de3eb 100644 --- a/pkg/yqlib/operator_booleans_test.go +++ b/pkg/yqlib/operator_booleans_test.go @@ -229,5 +229,5 @@ func TestBooleanOperatorScenarios(t *testing.T) { for _, tt := range booleanOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "Boolean Operators", booleanOperatorScenarios) + documentScenarios(t, "boolean-operators", booleanOperatorScenarios) } diff --git a/pkg/yqlib/operator_collect_object_test.go b/pkg/yqlib/operator_collect_object_test.go index 64891216..81c86450 100644 --- a/pkg/yqlib/operator_collect_object_test.go +++ b/pkg/yqlib/operator_collect_object_test.go @@ -134,5 +134,5 @@ func TestCollectObjectOperatorScenarios(t *testing.T) { for _, tt := range collectObjectOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "Create, Collect into Object", collectObjectOperatorScenarios) + documentScenarios(t, "create-collect-into-object", collectObjectOperatorScenarios) } diff --git a/pkg/yqlib/operator_collect_test.go b/pkg/yqlib/operator_collect_test.go index b5c2c542..d78833e1 100644 --- a/pkg/yqlib/operator_collect_test.go +++ b/pkg/yqlib/operator_collect_test.go @@ -91,5 +91,5 @@ func TestCollectOperatorScenarios(t *testing.T) { for _, tt := range collectOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "Collect into Array", collectOperatorScenarios) + documentScenarios(t, "collect-into-array", collectOperatorScenarios) } diff --git a/pkg/yqlib/operator_comments_test.go b/pkg/yqlib/operator_comments_test.go index d2798c00..14b1d83e 100644 --- a/pkg/yqlib/operator_comments_test.go +++ b/pkg/yqlib/operator_comments_test.go @@ -104,17 +104,19 @@ var commentOperatorScenarios = []expressionScenario{ }, }, { - description: "Get head comment", - document: "# welcome!\n\na: cat # meow\n\n# have a great day", - expression: `. | headComment`, + description: "Get head comment", + dontFormatInputForDoc: true, + document: "# welcome!\n\na: cat # meow\n\n# have a great day", + expression: `. | headComment`, expected: []string{ "D0, P[], (!!str)::welcome!\n", }, }, { - description: "Get foot comment", - document: "# welcome!\n\na: cat # meow\n\n# have a great day", - expression: `. | footComment`, + description: "Get foot comment", + dontFormatInputForDoc: true, + document: "# welcome!\n\na: cat # meow\n\n# have a great day", + expression: `. | footComment`, expected: []string{ "D0, P[], (!!str)::have a great day\n", }, @@ -125,5 +127,5 @@ func TestCommentOperatorScenarios(t *testing.T) { for _, tt := range commentOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "Comment Operators", commentOperatorScenarios) + documentScenarios(t, "comment-operators", commentOperatorScenarios) } diff --git a/pkg/yqlib/operator_contains_test.go b/pkg/yqlib/operator_contains_test.go index 116a778b..e1d3dd01 100644 --- a/pkg/yqlib/operator_contains_test.go +++ b/pkg/yqlib/operator_contains_test.go @@ -78,5 +78,5 @@ func TestContainsOperatorScenarios(t *testing.T) { for _, tt := range containsOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "Contains", containsOperatorScenarios) + documentScenarios(t, "contains", containsOperatorScenarios) } diff --git a/pkg/yqlib/operator_delete_test.go b/pkg/yqlib/operator_delete_test.go index 78c44cb7..02efcb75 100644 --- a/pkg/yqlib/operator_delete_test.go +++ b/pkg/yqlib/operator_delete_test.go @@ -171,5 +171,5 @@ func TestDeleteOperatorScenarios(t *testing.T) { for _, tt := range deleteOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "Delete", deleteOperatorScenarios) + documentScenarios(t, "delete", deleteOperatorScenarios) } diff --git a/pkg/yqlib/operator_document_index_test.go b/pkg/yqlib/operator_document_index_test.go index 11e4c772..1e35cd19 100644 --- a/pkg/yqlib/operator_document_index_test.go +++ b/pkg/yqlib/operator_document_index_test.go @@ -54,5 +54,5 @@ func TestDocumentIndexScenarios(t *testing.T) { for _, tt := range documentIndexScenarios { testScenario(t, &tt) } - documentScenarios(t, "Document Index", documentIndexScenarios) + documentScenarios(t, "document-index", documentIndexScenarios) } diff --git a/pkg/yqlib/operator_entries_test.go b/pkg/yqlib/operator_entries_test.go index 2dad859a..57d77034 100644 --- a/pkg/yqlib/operator_entries_test.go +++ b/pkg/yqlib/operator_entries_test.go @@ -66,5 +66,5 @@ func TestEntriesOperatorScenarios(t *testing.T) { for _, tt := range entriesOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "Entries", entriesOperatorScenarios) + documentScenarios(t, "entries", entriesOperatorScenarios) } diff --git a/pkg/yqlib/operator_env_test.go b/pkg/yqlib/operator_env_test.go index 8066e356..1cc1ba67 100644 --- a/pkg/yqlib/operator_env_test.go +++ b/pkg/yqlib/operator_env_test.go @@ -68,5 +68,5 @@ func TestEnvOperatorScenarios(t *testing.T) { for _, tt := range envOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "Env Variable Operators", envOperatorScenarios) + documentScenarios(t, "env-variable-operators", envOperatorScenarios) } diff --git a/pkg/yqlib/operator_equals_test.go b/pkg/yqlib/operator_equals_test.go index 06fd7811..360e7e99 100644 --- a/pkg/yqlib/operator_equals_test.go +++ b/pkg/yqlib/operator_equals_test.go @@ -186,5 +186,5 @@ func TestEqualOperatorScenarios(t *testing.T) { for _, tt := range equalsOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "Equals", equalsOperatorScenarios) + documentScenarios(t, "equals", equalsOperatorScenarios) } diff --git a/pkg/yqlib/operator_file_test.go b/pkg/yqlib/operator_file_test.go index cff642bf..88d1422f 100644 --- a/pkg/yqlib/operator_file_test.go +++ b/pkg/yqlib/operator_file_test.go @@ -53,5 +53,5 @@ func TestFileOperatorsScenarios(t *testing.T) { for _, tt := range fileOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "File Operators", fileOperatorScenarios) + documentScenarios(t, "file-operators", fileOperatorScenarios) } diff --git a/pkg/yqlib/operator_has_test.go b/pkg/yqlib/operator_has_test.go index ef8e79d4..fbd31d5b 100644 --- a/pkg/yqlib/operator_has_test.go +++ b/pkg/yqlib/operator_has_test.go @@ -77,5 +77,5 @@ func TestHasOperatorScenarios(t *testing.T) { for _, tt := range hasOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "Has", hasOperatorScenarios) + documentScenarios(t, "has", hasOperatorScenarios) } diff --git a/pkg/yqlib/operator_keys_test.go b/pkg/yqlib/operator_keys_test.go index 91bd649e..34becb21 100644 --- a/pkg/yqlib/operator_keys_test.go +++ b/pkg/yqlib/operator_keys_test.go @@ -43,5 +43,5 @@ func TestKeysOperatorScenarios(t *testing.T) { for _, tt := range keysOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "Keys", keysOperatorScenarios) + documentScenarios(t, "keys", keysOperatorScenarios) } diff --git a/pkg/yqlib/operator_length_test.go b/pkg/yqlib/operator_length_test.go index e9ecc2d0..d08c1326 100644 --- a/pkg/yqlib/operator_length_test.go +++ b/pkg/yqlib/operator_length_test.go @@ -62,5 +62,5 @@ func TestLengthOperatorScenarios(t *testing.T) { for _, tt := range lengthOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "Length", lengthOperatorScenarios) + documentScenarios(t, "length", lengthOperatorScenarios) } diff --git a/pkg/yqlib/operator_multiply_test.go b/pkg/yqlib/operator_multiply_test.go index b8a294e2..39bb83a9 100644 --- a/pkg/yqlib/operator_multiply_test.go +++ b/pkg/yqlib/operator_multiply_test.go @@ -429,5 +429,5 @@ func TestMultiplyOperatorScenarios(t *testing.T) { for _, tt := range multiplyOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "Multiply (Merge)", multiplyOperatorScenarios) + documentScenarios(t, "multiply-merge", multiplyOperatorScenarios) } diff --git a/pkg/yqlib/operator_path_test.go b/pkg/yqlib/operator_path_test.go index ba0bb666..f3e2bee3 100644 --- a/pkg/yqlib/operator_path_test.go +++ b/pkg/yqlib/operator_path_test.go @@ -74,5 +74,5 @@ func TestPathOperatorsScenarios(t *testing.T) { for _, tt := range pathOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "Path", pathOperatorScenarios) + documentScenarios(t, "path", pathOperatorScenarios) } diff --git a/pkg/yqlib/operator_pipe_test.go b/pkg/yqlib/operator_pipe_test.go index c9df9e93..fc36cde9 100644 --- a/pkg/yqlib/operator_pipe_test.go +++ b/pkg/yqlib/operator_pipe_test.go @@ -27,5 +27,5 @@ func TestPipeOperatorScenarios(t *testing.T) { for _, tt := range pipeOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "Pipe", pipeOperatorScenarios) + documentScenarios(t, "pipe", pipeOperatorScenarios) } diff --git a/pkg/yqlib/operator_recursive_descent_test.go b/pkg/yqlib/operator_recursive_descent_test.go index 00416cd1..61caab12 100644 --- a/pkg/yqlib/operator_recursive_descent_test.go +++ b/pkg/yqlib/operator_recursive_descent_test.go @@ -233,5 +233,5 @@ func TestRecursiveDescentOperatorScenarios(t *testing.T) { for _, tt := range recursiveDescentOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "Recursive Descent (Glob)", recursiveDescentOperatorScenarios) + documentScenarios(t, "recursive-descent-glob", recursiveDescentOperatorScenarios) } diff --git a/pkg/yqlib/operator_reduce_test.go b/pkg/yqlib/operator_reduce_test.go index 1bd1c4fb..dde2c33f 100644 --- a/pkg/yqlib/operator_reduce_test.go +++ b/pkg/yqlib/operator_reduce_test.go @@ -36,5 +36,5 @@ func TestReduceOperatorScenarios(t *testing.T) { for _, tt := range reduceOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "Reduce", reduceOperatorScenarios) + documentScenarios(t, "reduce", reduceOperatorScenarios) } diff --git a/pkg/yqlib/operator_select_test.go b/pkg/yqlib/operator_select_test.go index b046c748..4a1e8723 100644 --- a/pkg/yqlib/operator_select_test.go +++ b/pkg/yqlib/operator_select_test.go @@ -99,5 +99,5 @@ func TestSelectOperatorScenarios(t *testing.T) { for _, tt := range selectOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "Select", selectOperatorScenarios) + documentScenarios(t, "select", selectOperatorScenarios) } diff --git a/pkg/yqlib/operator_sort_keys_test.go b/pkg/yqlib/operator_sort_keys_test.go index dc1a909b..130b8019 100644 --- a/pkg/yqlib/operator_sort_keys_test.go +++ b/pkg/yqlib/operator_sort_keys_test.go @@ -36,5 +36,5 @@ func TestSortKeysOperatorScenarios(t *testing.T) { for _, tt := range sortKeysOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "Sort Keys", sortKeysOperatorScenarios) + documentScenarios(t, "sort-keys", sortKeysOperatorScenarios) } diff --git a/pkg/yqlib/operator_split_document_test.go b/pkg/yqlib/operator_split_document_test.go index 5b2b3c0b..813f3173 100644 --- a/pkg/yqlib/operator_split_document_test.go +++ b/pkg/yqlib/operator_split_document_test.go @@ -28,5 +28,5 @@ func TestSplitDocOperatorScenarios(t *testing.T) { for _, tt := range splitDocOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "Split into Documents", splitDocOperatorScenarios) + documentScenarios(t, "split-into-documents", splitDocOperatorScenarios) } diff --git a/pkg/yqlib/operator_strings_test.go b/pkg/yqlib/operator_strings_test.go index b7d80cd7..f1218010 100644 --- a/pkg/yqlib/operator_strings_test.go +++ b/pkg/yqlib/operator_strings_test.go @@ -170,5 +170,5 @@ func TestStringsOperatorScenarios(t *testing.T) { for _, tt := range stringsOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "String Operators", stringsOperatorScenarios) + documentScenarios(t, "string-operators", stringsOperatorScenarios) } diff --git a/pkg/yqlib/operator_style_test.go b/pkg/yqlib/operator_style_test.go index 77d88aca..e02fa3ad 100644 --- a/pkg/yqlib/operator_style_test.go +++ b/pkg/yqlib/operator_style_test.go @@ -152,5 +152,5 @@ func TestStyleOperatorScenarios(t *testing.T) { for _, tt := range styleOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "Style", styleOperatorScenarios) + documentScenarios(t, "style", styleOperatorScenarios) } diff --git a/pkg/yqlib/operator_subtract_test.go b/pkg/yqlib/operator_subtract_test.go index 09db0035..7e209d5e 100644 --- a/pkg/yqlib/operator_subtract_test.go +++ b/pkg/yqlib/operator_subtract_test.go @@ -99,5 +99,5 @@ func TestSubtractOperatorScenarios(t *testing.T) { for _, tt := range subtractOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "Subtract", subtractOperatorScenarios) + documentScenarios(t, "subtract", subtractOperatorScenarios) } diff --git a/pkg/yqlib/operator_tag_test.go b/pkg/yqlib/operator_tag_test.go index 1d47c501..fd90f734 100644 --- a/pkg/yqlib/operator_tag_test.go +++ b/pkg/yqlib/operator_tag_test.go @@ -64,5 +64,5 @@ func TestTagOperatorScenarios(t *testing.T) { for _, tt := range tagOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "Tag", tagOperatorScenarios) + documentScenarios(t, "tag", tagOperatorScenarios) } diff --git a/pkg/yqlib/operator_traverse_path_test.go b/pkg/yqlib/operator_traverse_path_test.go index 59fddde3..b556ba28 100644 --- a/pkg/yqlib/operator_traverse_path_test.go +++ b/pkg/yqlib/operator_traverse_path_test.go @@ -519,5 +519,5 @@ func TestTraversePathOperatorScenarios(t *testing.T) { for _, tt := range traversePathOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "Traverse (Read)", traversePathOperatorScenarios) + documentScenarios(t, "traverse-read", traversePathOperatorScenarios) } diff --git a/pkg/yqlib/operator_union_test.go b/pkg/yqlib/operator_union_test.go index 6d8f8931..a5f12de7 100644 --- a/pkg/yqlib/operator_union_test.go +++ b/pkg/yqlib/operator_union_test.go @@ -44,5 +44,5 @@ func TestUnionOperatorScenarios(t *testing.T) { for _, tt := range unionOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "Union", unionOperatorScenarios) + documentScenarios(t, "union", unionOperatorScenarios) } diff --git a/pkg/yqlib/operator_unique_test.go b/pkg/yqlib/operator_unique_test.go index bc465783..347f4d71 100644 --- a/pkg/yqlib/operator_unique_test.go +++ b/pkg/yqlib/operator_unique_test.go @@ -61,5 +61,5 @@ func TestUniqueOperatorScenarios(t *testing.T) { for _, tt := range uniqueOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "Unique", uniqueOperatorScenarios) + documentScenarios(t, "unique", uniqueOperatorScenarios) } diff --git a/pkg/yqlib/operator_variables_test.go b/pkg/yqlib/operator_variables_test.go index 2d890d88..a6e8d854 100644 --- a/pkg/yqlib/operator_variables_test.go +++ b/pkg/yqlib/operator_variables_test.go @@ -66,5 +66,5 @@ func TestVariableOperatorScenarios(t *testing.T) { for _, tt := range variableOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "Variable Operators", variableOperatorScenarios) + documentScenarios(t, "variable-operators", variableOperatorScenarios) } diff --git a/pkg/yqlib/operator_with_test.go b/pkg/yqlib/operator_with_test.go index d029fba9..11ea08c1 100644 --- a/pkg/yqlib/operator_with_test.go +++ b/pkg/yqlib/operator_with_test.go @@ -34,5 +34,5 @@ func TestWithOperatorScenarios(t *testing.T) { for _, tt := range withOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "With", withOperatorScenarios) + documentScenarios(t, "with", withOperatorScenarios) } diff --git a/scripts/generate-man-page-md.sh b/scripts/generate-man-page-md.sh index c2806b3b..d3acb1af 100755 --- a/scripts/generate-man-page-md.sh +++ b/scripts/generate-man-page-md.sh @@ -8,9 +8,5 @@ printf "\n# HOW IT WORKS\n" >> man.md tail -n +2 how-it-works.md >> man.md for f in ./pkg/yqlib/doc/*.md; do - docNameWithExt="${f##*/}" - docName="${docNameWithExt%.*}" - docNameCap=$(echo $docName | tr [a-z] [A-Z]) - printf "\n\n# ${docNameCap}\n" >> man.md - tail -n +2 "$f" >> man.md + cat "$f" >> man.md done