mirror of
https://github.com/mikefarah/yq.git
synced 2026-08-24 16:39:58 +08:00
Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c35ec752e3 | ||
|
|
fbd4c8428a | ||
|
|
d9ef03a2d1 | ||
|
|
f5bfe5a248 | ||
|
|
3b85cef340 |
@@ -75,10 +75,6 @@ func initCommand(cmd *cobra.Command, args []string) (string, []string, error) {
|
||||
outputFormat = "yaml"
|
||||
}
|
||||
} else if isAutomaticOutputFormat() {
|
||||
// automatic input worked, we can do it for output too unless specified
|
||||
if inputFormat == "json" {
|
||||
yqlib.GetLogger().Warning("JSON file output is now JSON by default (instead of yaml). Use '-oy' or '--output-format=yaml' for yaml output")
|
||||
}
|
||||
outputFormat = inputFormat
|
||||
}
|
||||
} else if isAutomaticOutputFormat() {
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ var (
|
||||
GitDescribe string
|
||||
|
||||
// Version is main version number that is being run at the moment.
|
||||
Version = "v4.42.1"
|
||||
Version = "v4.43.1"
|
||||
|
||||
// VersionPrerelease is a pre-release marker for the version. If this is "" (empty string)
|
||||
// then it means that it is a final release. Otherwise, this is a pre-release
|
||||
|
||||
@@ -11,7 +11,7 @@ require (
|
||||
github.com/goccy/go-yaml v1.11.3
|
||||
github.com/jinzhu/copier v0.4.0
|
||||
github.com/magiconair/properties v1.8.7
|
||||
github.com/pelletier/go-toml/v2 v2.1.1
|
||||
github.com/pelletier/go-toml/v2 v2.2.0
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e
|
||||
github.com/spf13/cobra v1.8.0
|
||||
github.com/spf13/pflag v1.0.5
|
||||
|
||||
@@ -43,8 +43,8 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
|
||||
github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
|
||||
github.com/pelletier/go-toml/v2 v2.2.0 h1:QLgLl2yMN7N+ruc31VynXs1vhMZa7CeHHejIeBAsoHo=
|
||||
github.com/pelletier/go-toml/v2 v2.2.0/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
@@ -57,11 +57,13 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M=
|
||||
github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
|
||||
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
||||
|
||||
@@ -55,6 +55,62 @@ a: 12
|
||||
b: 4
|
||||
```
|
||||
|
||||
## Multiply string node X int
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
b: banana
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq '.b * 4' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
bananabananabananabanana
|
||||
```
|
||||
|
||||
## Multiply int X string node
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
b: banana
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq '4 * .b' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
bananabananabananabanana
|
||||
```
|
||||
|
||||
## Multiply string X int node
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
n: 4
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq '"banana" * .n' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
bananabananabananabanana
|
||||
```
|
||||
|
||||
## Multiply int node X string
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
n: 4
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq '.n * "banana"' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
bananabananabananabanana
|
||||
```
|
||||
|
||||
## Merge objects together, returning merged result only
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
|
||||
## Omit keys from map
|
||||
Note that the order of the keys matches the omit order and non existent keys are skipped.
|
||||
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
myMap:
|
||||
cat: meow
|
||||
dog: bark
|
||||
thing: hamster
|
||||
hamster: squeak
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq '.myMap |= omit(["hamster", "cat", "goat"])' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
myMap:
|
||||
dog: bark
|
||||
thing: hamster
|
||||
```
|
||||
|
||||
## Omit indices from array
|
||||
Note that the order of the indices matches the omit order and non existent indices are skipped.
|
||||
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
- cat
|
||||
- leopard
|
||||
- lion
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq 'omit([2, 0, 734, -5])' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
- leopard
|
||||
```
|
||||
|
||||
@@ -40,6 +40,7 @@ var participleYqRules = []*participleYqRule{
|
||||
simpleOp("map", mapOpType),
|
||||
simpleOp("filter", filterOpType),
|
||||
simpleOp("pick", pickOpType),
|
||||
simpleOp("omit", omitOpType),
|
||||
|
||||
{"FlattenWithDepth", `flatten\([0-9]+\)`, flattenWithDepth(), 0},
|
||||
{"Flatten", `flatten`, opTokenWithPrefs(flattenOpType, nil, flattenPreferences{depth: -1}), 0},
|
||||
|
||||
@@ -90,6 +90,7 @@ var mapOpType = &operationType{Type: "MAP", NumArgs: 1, Precedence: 50, Handler:
|
||||
var filterOpType = &operationType{Type: "FILTER", NumArgs: 1, Precedence: 50, Handler: filterOperator}
|
||||
var errorOpType = &operationType{Type: "ERROR", NumArgs: 1, Precedence: 50, Handler: errorOperator}
|
||||
var pickOpType = &operationType{Type: "PICK", NumArgs: 1, Precedence: 50, Handler: pickOperator}
|
||||
var omitOpType = &operationType{Type: "OMIT", NumArgs: 1, Precedence: 50, Handler: omitOperator}
|
||||
var evalOpType = &operationType{Type: "EVAL", NumArgs: 1, Precedence: 50, Handler: evalOperator}
|
||||
var mapValuesOpType = &operationType{Type: "MAP_VALUES", NumArgs: 1, Precedence: 50, Handler: mapValuesOperator}
|
||||
|
||||
|
||||
@@ -91,6 +91,8 @@ func multiplyScalars(lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, er
|
||||
return multiplyIntegers(lhs, rhs)
|
||||
} else if (lhsTag == "!!int" || lhsTag == "!!float") && (rhsTag == "!!int" || rhsTag == "!!float") {
|
||||
return multiplyFloats(lhs, rhs, lhsIsCustom)
|
||||
} else if (lhsTag == "!!str" && rhsTag == "!!int") || (lhsTag == "!!int" && rhsTag == "!!str") {
|
||||
return repeatString(lhs, rhs)
|
||||
}
|
||||
return nil, fmt.Errorf("cannot multiply %v with %v", lhs.Tag, rhs.Tag)
|
||||
}
|
||||
@@ -135,6 +137,28 @@ func multiplyIntegers(lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, e
|
||||
return target, nil
|
||||
}
|
||||
|
||||
func repeatString(lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
||||
var stringNode *CandidateNode
|
||||
var intNode *CandidateNode
|
||||
if lhs.Tag == "!!str" {
|
||||
stringNode = lhs
|
||||
intNode = rhs
|
||||
} else {
|
||||
stringNode = rhs
|
||||
intNode = lhs
|
||||
}
|
||||
target := lhs.CopyWithoutContent()
|
||||
target.UpdateAttributesFrom(stringNode, assignPreferences{})
|
||||
|
||||
count, err := parseInt(intNode.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
target.Value = strings.Repeat(stringNode.Value, count)
|
||||
|
||||
return target, nil
|
||||
}
|
||||
|
||||
func mergeObjects(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode, preferences multiplyPreferences) (*CandidateNode, error) {
|
||||
var results = list.New()
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package yqlib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -174,6 +176,40 @@ var multiplyOperatorScenarios = []expressionScenario{
|
||||
"D0, P[], (!!map)::a: 12\nb: 4\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Multiply string node X int",
|
||||
document: docNoComments,
|
||||
expression: ".b * 4",
|
||||
expected: []string{
|
||||
fmt.Sprintf("D0, P[b], (!!str)::%s\n", strings.Repeat("banana", 4)),
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Multiply int X string node",
|
||||
document: docNoComments,
|
||||
expression: "4 * .b",
|
||||
expected: []string{
|
||||
fmt.Sprintf("D0, P[], (!!str)::%s\n", strings.Repeat("banana", 4)),
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Multiply string X int node",
|
||||
document: `n: 4
|
||||
`,
|
||||
expression: `"banana" * .n`,
|
||||
expected: []string{
|
||||
fmt.Sprintf("D0, P[], (!!str)::%s\n", strings.Repeat("banana", 4)),
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Multiply int node X string",
|
||||
document: `n: 4
|
||||
`,
|
||||
expression: `.n * "banana"`,
|
||||
expected: []string{
|
||||
fmt.Sprintf("D0, P[n], (!!str)::%s\n", strings.Repeat("banana", 4)),
|
||||
},
|
||||
},
|
||||
{
|
||||
skipDoc: true,
|
||||
document: doc1,
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
package yqlib
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func omitMap(original *CandidateNode, indices *CandidateNode) *CandidateNode {
|
||||
filteredContent := make([]*CandidateNode, 0, max(0, len(original.Content)-len(indices.Content)*2))
|
||||
|
||||
for index := 0; index < len(original.Content); index += 2 {
|
||||
pos := findInArray(indices, original.Content[index])
|
||||
if pos < 0 {
|
||||
clonedKey := original.Content[index].Copy()
|
||||
clonedValue := original.Content[index+1].Copy()
|
||||
filteredContent = append(filteredContent, clonedKey, clonedValue)
|
||||
}
|
||||
}
|
||||
result := original.CopyWithoutContent()
|
||||
result.AddChildren(filteredContent)
|
||||
return result
|
||||
}
|
||||
|
||||
func omitSequence(original *CandidateNode, indices *CandidateNode) *CandidateNode {
|
||||
filteredContent := make([]*CandidateNode, 0, max(0, len(original.Content)-len(indices.Content)))
|
||||
|
||||
for index := 0; index < len(original.Content); index++ {
|
||||
pos := findInArray(indices, createScalarNode(index, strconv.Itoa(index)))
|
||||
if pos < 0 {
|
||||
filteredContent = append(filteredContent, original.Content[index].Copy())
|
||||
}
|
||||
}
|
||||
result := original.CopyWithoutContent()
|
||||
result.AddChildren(filteredContent)
|
||||
return result
|
||||
}
|
||||
|
||||
func omitOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||
log.Debugf("Omit")
|
||||
|
||||
contextIndicesToOmit, err := d.GetMatchingNodes(context, expressionNode.RHS)
|
||||
|
||||
if err != nil {
|
||||
return Context{}, err
|
||||
}
|
||||
indicesToOmit := &CandidateNode{}
|
||||
if contextIndicesToOmit.MatchingNodes.Len() > 0 {
|
||||
indicesToOmit = contextIndicesToOmit.MatchingNodes.Front().Value.(*CandidateNode)
|
||||
}
|
||||
if len(indicesToOmit.Content) == 0 {
|
||||
log.Debugf("No omit indices specified")
|
||||
return context, nil
|
||||
}
|
||||
var results = list.New()
|
||||
|
||||
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||
node := el.Value.(*CandidateNode)
|
||||
|
||||
var replacement *CandidateNode
|
||||
|
||||
if node.Kind == MappingNode {
|
||||
replacement = omitMap(node, indicesToOmit)
|
||||
} else if node.Kind == SequenceNode {
|
||||
replacement = omitSequence(node, indicesToOmit)
|
||||
} else {
|
||||
log.Debugf("Omit from type %v (%v) is noop", node.Tag, node.GetNicePath())
|
||||
return context, nil
|
||||
}
|
||||
replacement.LeadingContent = node.LeadingContent
|
||||
results.PushBack(replacement)
|
||||
}
|
||||
return context.ChildContext(results), nil
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package yqlib
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
var omitOperatorScenarios = []expressionScenario{
|
||||
{
|
||||
description: "Omit keys from map",
|
||||
subdescription: "Note that the order of the keys matches the omit order and non existent keys are skipped.",
|
||||
document: "myMap: {cat: meow, dog: bark, thing: hamster, hamster: squeak}\n",
|
||||
expression: `.myMap |= omit(["hamster", "cat", "goat"])`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!map)::myMap: {dog: bark, thing: hamster}\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Omit keys from map",
|
||||
skipDoc: true,
|
||||
document: "!things myMap: {cat: meow, dog: bark, thing: hamster, hamster: squeak}\n",
|
||||
expression: `.myMap |= omit(["hamster", "cat", "goat"])`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!map)::!things myMap: {dog: bark, thing: hamster}\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Omit keys from map with comments",
|
||||
skipDoc: true,
|
||||
document: "# abc\nmyMap: {cat: meow, dog: bark, thing: hamster, hamster: squeak}\n# xyz\n",
|
||||
expression: `.myMap |= omit(["hamster", "cat", "goat"])`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!map)::# abc\nmyMap: {dog: bark, thing: hamster}\n# xyz\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Omit indices from array",
|
||||
subdescription: "Note that the order of the indices matches the omit order and non existent indices are skipped.",
|
||||
document: `[cat, leopard, lion]`,
|
||||
expression: `omit([2, 0, 734, -5])`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!seq)::[leopard]\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Omit indices from array with comments",
|
||||
skipDoc: true,
|
||||
document: "# abc\n[cat, leopard, lion]\n# xyz",
|
||||
expression: `omit([2, 0, 734, -5])`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!seq)::# abc\n[leopard]\n# xyz\n",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestOmitOperatorScenarios(t *testing.T) {
|
||||
for _, tt := range omitOperatorScenarios {
|
||||
testScenario(t, &tt)
|
||||
}
|
||||
documentOperatorScenarios(t, "omit", omitOperatorScenarios)
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
4.43.1: (not released yet)
|
||||
- Added omit operator #1989 thanks @mbenson!
|
||||
- Added tostring #72
|
||||
- Added string interpolation #1149
|
||||
- Can specify parent(n) levels #1970
|
||||
- Can now multiply strings by numbers #1988 thanks @mbenson!
|
||||
- Fixed CSV line break issue #1974
|
||||
- Adding a EvaluateAll function to StringEvaluator #1966
|
||||
- yqlib, default to colors off when using yaml library #1964
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
name: yq
|
||||
version: 'v4.42.1'
|
||||
version: 'v4.43.1'
|
||||
summary: A lightweight and portable command-line data file processor
|
||||
description: |
|
||||
`yq` uses [jq](https://github.com/stedolan/jq) like syntax but works with yaml, json, xml, csv, properties and TOML files.
|
||||
@@ -21,6 +21,6 @@ parts:
|
||||
yq:
|
||||
plugin: go
|
||||
source: https://github.com/mikefarah/yq.git
|
||||
source-tag: v4.42.1
|
||||
source-tag: v4.43.1
|
||||
build-snaps:
|
||||
- go/latest/stable
|
||||
|
||||
Reference in New Issue
Block a user