1
0
mirror of https://github.com/taigrr/yq synced 2025-01-18 04:53:17 -08:00

Compare commits

..

52 Commits

Author SHA1 Message Date
Mike Farah
c2c49dcb17 Fixed help length to prevent horizontal scroll in README 2018-06-27 19:37:18 +10:00
Mike Farah
60de18391c Incrementing version for next release 2018-06-27 12:06:46 +10:00
Mike Farah
c86f8b426b Fixed writing inplace from docker 2018-06-27 12:06:31 +10:00
Mike Farah
b3532e0a61 Cache devtools and vendor libs in docker 2018-06-27 12:05:52 +10:00
Mike Farah
b3b60665e4 Fixed toJson command line option, should only apply to read command 2018-06-26 14:09:56 +10:00
Mike Farah
df08b055cf Shorten instructions in readme to avoid horizontal scroll 2018-06-20 19:50:19 +10:00
Mike Farah
e822313e82 Merge branch 'multi' 2018-06-20 19:48:44 +10:00
Mike Farah
a9f25c9d76 Added more snapcraft instructions 2018-06-20 19:46:47 +10:00
Roberto Mier Escandon
0a8c268dcc Bumped deb version 2018-06-20 19:28:44 +10:00
Mike Farah
a0e70279a8 Updating snapcraft version 2018-06-20 17:51:07 +10:00
Mike Farah
25c9c22d8d Updating docs 2018-06-20 14:29:17 +10:00
Mike Farah
d46d555b07 Incrementing version 2018-06-20 14:29:10 +10:00
Mike Farah
fb87f638f2 Multi doc supports updating all docs 2018-06-20 11:45:51 +10:00
Mike Farah
facc81d1f4 github version of mousetrap required for xcompile 2018-06-20 08:14:14 +10:00
Mike Farah
c1f9065c68 pflag has to be github :eye_roll: 2018-06-18 20:12:09 +10:00
Mike Farah
be84cc3082 Add pflag back in 2018-06-18 20:08:41 +10:00
Mike Farah
a2571da1a1 Updated docs to refer to gopkg.in when using go get 2018-06-18 11:45:46 +10:00
Mike Farah
6d6e476ac8 Use gopkg managed versions of dependencies, for better go get support 2018-06-18 11:37:42 +10:00
Mike Farah
ae0c042ae6 Use gopkg managed version of yaml to properly support go get 2018-06-18 11:12:52 +10:00
Mike Farah
867ec92d3a Version bump 2018-06-15 20:50:20 +10:00
Mike Farah
113586b5e0 Updating help for multi doc 2018-06-15 20:31:29 +10:00
Mike Farah
c38f19e0a9 Enabled multi document support for merge (first document only) 2018-06-15 16:48:36 +10:00
Mike Farah
8ca85b1c64 Simplified merge command 2018-06-15 16:40:52 +10:00
Mike Farah
08870f8ec9 Simplified 'new' command 2018-06-15 16:21:18 +10:00
Mike Farah
94b217984c Better error handling 2018-06-15 16:11:13 +10:00
Mike Farah
2f5a481cc3 Detect when there is no document X to update 2018-06-15 09:54:11 +10:00
Mike Farah
1a4064429d Delete now supports multi docs! 2018-06-15 09:43:20 +10:00
Mike Farah
1b22e1d812 Fixed delete command for arrays 2018-06-15 09:03:42 +10:00
Mike Farah
297522cbdd Write supports multidoc yaml, better use of yaml library streaming 2018-06-15 08:39:59 +10:00
Mike Farah
be991fdacd Read test 2018-06-15 08:39:29 +10:00
Mike Farah
be08214773 fixed version test 2018-06-13 10:00:01 +10:00
Mike Farah
9e971ebeae Beta version of multiple document support for read, write coming soon 2018-06-13 09:26:52 +10:00
Mike Farah
f340db5795 Extract out reading of write commands 2018-06-13 09:24:37 +10:00
Mike Farah
ab852ceafa Separate reading stream from processing 2018-06-13 09:11:54 +10:00
Mike Farah
06a843e9b2 Read now handles multiple documents 2018-06-12 15:41:09 +10:00
Mike Farah
ebdc092688 Updating user docs 2018-06-12 10:17:09 +10:00
Roberto Mier Escandon
27089d1ca1 Updated some documentation pointing to deb install option 2018-06-12 10:12:55 +10:00
Roberto Mier Escandon
1853585d22 Add debian packaging 2018-06-12 10:12:55 +10:00
Mike Farah
0124d26086 Added mkdocs instructions to Contribute 2018-06-12 10:11:15 +10:00
Mike Farah
0d68bea3dc Release instructions 2018-05-08 10:55:16 +10:00
Mike Farah
54603b3607 Updated version in snapcraft.yml 2018-05-08 10:06:41 +10:00
Mike Farah
c86aeca325 Fixed script for publishing from a mac 2018-05-07 20:39:18 +10:00
Mike Farah
a4f124f24f Updated repo name 2018-05-07 16:40:23 +10:00
Mike Farah
090b377fa5 Incrementing version to reflect new delete feature! 2018-05-07 16:38:26 +10:00
Mike Farah
3a4d62d820 Adding delete command documentation 2018-05-07 16:34:29 +10:00
Mike Farah
6053c8c136 Use dev scripts for making docker image 2018-05-07 16:31:26 +10:00
Mike Farah
c5a45ba7d5 Removed dud instructions 2018-05-07 16:26:38 +10:00
Matthew Huxtable
56a0771cd1 Mark test helpers as such 2018-05-07 15:52:29 +10:00
Matthew Huxtable
8072e66d46 Add delete command
The delete (short option "d") will delete the YAML subtree at the
provided path in the specified file (or STDIN), if it the node exists.

More complex support is currently omitted, for example:

  - specify nodes to delete using an external script
  - deleting common elements from all elements of an array
2018-05-07 15:52:29 +10:00
Tim Hobbs
26153b3eb5 Rename to yq 2018-04-09 15:58:12 +10:00
Tim Hobbs
f2e10f21c7 Rename to yq 2018-04-09 15:58:12 +10:00
spawnia
d3ecf7aa88 Add Dockerfile for building the official CLI container 2018-04-03 09:25:00 +10:00
46 changed files with 2224 additions and 401 deletions

24
Dockerfile Normal file
View File

@@ -0,0 +1,24 @@
FROM golang:1.9 as builder
WORKDIR /go/src/mikefarah/yq
# cache devtools
COPY ./scripts/devtools.sh /go/src/mikefarah/yq/scripts/devtools.sh
RUN ./scripts/devtools.sh
# cache vendor
COPY ./vendor/vendor.json /go/src/mikefarah/yq/vendor/vendor.json
RUN govendor sync
COPY . /go/src/mikefarah/yq
RUN CGO_ENABLED=0 make local build
# Choose alpine as a base image to make this useful for CI, as many
# CI tools expect an interactive shell inside the container
FROM alpine:3.7
COPY --from=builder /go/src/mikefarah/yq/yq /usr/bin/yq
RUN chmod +x /usr/bin/yq
WORKDIR /workdir

View File

@@ -12,9 +12,29 @@ On Ubuntu and other Linux distros supporting `snap` packages:
```
snap install yq
```
On Ubuntu 16.04 or higher from Debian package:
```
sudo add-apt-repository ppa:rmescandon/yq
sudo apt update
sudo apt install yq -y
```
or, [Download latest binary](https://github.com/mikefarah/yq/releases/latest) or alternatively:
```
go get github.com/mikefarah/yq
go get gopkg.in/mikefarah/yq.v2
```
## Run with Docker
Oneshot use:
```bash
docker run -v ${PWD}:/workdir mikefarah/yq yq [flags] <command> FILE...
```
Run commands interactively:
```bash
docker run -it -v ${PWD}:/workdir mikefarah/yq sh
```
## Features
@@ -30,6 +50,7 @@ go get github.com/mikefarah/yq
- Pipe data in by using '-'
- Merge multiple yaml files where each additional file sets values for missing or null value keys.
- Merge multiple yaml files with overwrite to support overriding previous values.
- Supports multiple documents in a single yaml file
## [Usage](http://mikefarah.github.io/yq/)
@@ -41,15 +62,15 @@ Usage:
yq [command]
Available Commands:
delete yq d [--inplace/-i] [--doc/-d index] sample.yaml a.b.c
help Help about any command
merge yq m [--inplace/-i] [--overwrite/-x] sample.yaml sample2.yaml
new yq n [--script/-s script_file] a.b.c newValueForC
read yq r sample.yaml a.b.c
write yq w [--inplace/-i] [--script/-s script_file] sample.yaml a.b.c newValueForC
merge yq m [--inplace/-i] [--doc/-d index] [--overwrite/-x] sample.yaml sample2.yaml
new yq n [--script/-s script_file] a.b.c newValue
read yq r [--doc/-d index] sample.yaml a.b.c
write yq w [--inplace/-i] [--script/-s script_file] [--doc/-d index] sample.yaml a.b.c newValue
Flags:
-h, --help help for yq
-j, --tojson output as json
-t, --trim trim yaml output (default true)
-v, --verbose verbose mode
-V, --version Print version information and quit
@@ -60,6 +81,10 @@ Use "yq [command] --help" for more information about a command.
## Contribute
1. `make [local] vendor`
2. add unit tests
3. apply changes
3. apply changes (use govendor with a preference to [gopkg](https://gopkg.in/) for package dependencies)
4. `make [local] build`
5. profit
5. If required, update the user documentation
- Update README.md and/or documentation under the mkdocs folder
- `make [local] build-docs`
- browse to docs/index.html and check your changes
6. profit

View File

@@ -5,7 +5,7 @@ import (
"strings"
"testing"
"github.com/spf13/cobra"
"gopkg.in/spf13/cobra.v0"
)
func getRootCommand() *cobra.Command {
@@ -73,37 +73,13 @@ func TestRootCmd_TrimShort(t *testing.T) {
}
}
func TestRootCmd_ToJsonLong(t *testing.T) {
cmd := getRootCommand()
result := runCmd(cmd, "--tojson")
if result.Error != nil {
t.Error(result.Error)
}
if !outputToJSON {
t.Error("Expected outputToJSON to be true")
}
}
func TestRootCmd_ToJsonShort(t *testing.T) {
cmd := getRootCommand()
result := runCmd(cmd, "-j")
if result.Error != nil {
t.Error(result.Error)
}
if !outputToJSON {
t.Error("Expected outputToJSON to be true")
}
}
func TestRootCmd_VersionShort(t *testing.T) {
cmd := getRootCommand()
result := runCmd(cmd, "-V")
if result.Error != nil {
t.Error(result.Error)
}
if !strings.Contains(result.Output, "yaml version") {
if !strings.Contains(result.Output, "yq version") {
t.Error("expected version message to be printed out, but the message was not found.")
}
}
@@ -114,7 +90,7 @@ func TestRootCmd_VersionLong(t *testing.T) {
if result.Error != nil {
t.Error(result.Error)
}
if !strings.Contains(result.Output, "yaml version") {
if !strings.Contains(result.Output, "yq version") {
t.Error("expected version message to be printed out, but the message was not found.")
}
}
@@ -128,6 +104,15 @@ func TestReadCmd(t *testing.T) {
assertResult(t, "2\n", result.Output)
}
func TestReadMultiCmd(t *testing.T) {
cmd := getRootCommand()
result := runCmd(cmd, "read -d 1 examples/multiple_docs.yaml another.document")
if result.Error != nil {
t.Error(result.Error)
}
assertResult(t, "here\n", result.Output)
}
func TestReadCmd_ArrayYaml(t *testing.T) {
cmd := getRootCommand()
result := runCmd(cmd, "read examples/array.yaml [0].gather_facts")
@@ -293,7 +278,16 @@ func TestReadCmd_NoTrim(t *testing.T) {
func TestReadCmd_ToJson(t *testing.T) {
cmd := getRootCommand()
result := runCmd(cmd, "-j read examples/sample.yaml b.c")
result := runCmd(cmd, "read -j examples/sample.yaml b.c")
if result.Error != nil {
t.Error(result.Error)
}
assertResult(t, "2\n", result.Output)
}
func TestReadCmd_ToJsonLong(t *testing.T) {
cmd := getRootCommand()
result := runCmd(cmd, "read --tojson examples/sample.yaml b.c")
if result.Error != nil {
t.Error(result.Error)
}
@@ -334,17 +328,6 @@ func TestNewCmd_Verbose(t *testing.T) {
assertResult(t, expectedOutput, result.Output)
}
func TestNewCmd_ToJson(t *testing.T) {
cmd := getRootCommand()
result := runCmd(cmd, "-j new b.c 3")
if result.Error != nil {
t.Error(result.Error)
}
expectedOutput := `{"b":{"c":3}}
`
assertResult(t, expectedOutput, result.Output)
}
func TestWriteCmd(t *testing.T) {
content := `b:
c: 3
@@ -363,6 +346,50 @@ func TestWriteCmd(t *testing.T) {
assertResult(t, expectedOutput, result.Output)
}
func TestWriteMultiCmd(t *testing.T) {
content := `b:
c: 3
---
apples: great
`
filename := writeTempYamlFile(content)
defer removeTempYamlFile(filename)
cmd := getRootCommand()
result := runCmd(cmd, fmt.Sprintf("write %s -d 1 apples ok", filename))
if result.Error != nil {
t.Error(result.Error)
}
expectedOutput := `b:
c: 3
---
apples: ok
`
assertResult(t, expectedOutput, result.Output)
}
func TestWriteMultiAllCmd(t *testing.T) {
content := `b:
c: 3
---
apples: great
`
filename := writeTempYamlFile(content)
defer removeTempYamlFile(filename)
cmd := getRootCommand()
result := runCmd(cmd, fmt.Sprintf("write %s -d * apples ok", filename))
if result.Error != nil {
t.Error(result.Error)
}
expectedOutput := `b:
c: 3
apples: ok
---
apples: ok`
assertResult(t, expectedOutput, strings.Trim(result.Output, "\n "))
}
func TestWriteCmd_EmptyArray(t *testing.T) {
content := `b: 3`
filename := writeTempYamlFile(content)
@@ -432,7 +459,7 @@ func TestWriteCmd_Inplace(t *testing.T) {
gotOutput := readTempYamlFile(filename)
expectedOutput := `b:
c: 7`
assertResult(t, expectedOutput, gotOutput)
assertResult(t, expectedOutput, strings.Trim(gotOutput, "\n "))
}
func TestWriteCmd_Append(t *testing.T) {
@@ -472,6 +499,95 @@ b:
assertResult(t, expectedOutput, result.Output)
}
func TestDeleteYaml(t *testing.T) {
content := `a: 2
b:
c: things
d: something else
`
filename := writeTempYamlFile(content)
defer removeTempYamlFile(filename)
cmd := getRootCommand()
result := runCmd(cmd, fmt.Sprintf("delete %s b.c", filename))
if result.Error != nil {
t.Error(result.Error)
}
expectedOutput := `a: 2
b:
d: something else
`
assertResult(t, expectedOutput, result.Output)
}
func TestDeleteYamlArray(t *testing.T) {
content := `- 1
- 2
- 3
`
filename := writeTempYamlFile(content)
defer removeTempYamlFile(filename)
cmd := getRootCommand()
result := runCmd(cmd, fmt.Sprintf("delete %s [1]", filename))
if result.Error != nil {
t.Error(result.Error)
}
expectedOutput := `- 1
- 3
`
assertResult(t, expectedOutput, result.Output)
}
func TestDeleteYamlMulti(t *testing.T) {
content := `apples: great
---
- 1
- 2
- 3
`
filename := writeTempYamlFile(content)
defer removeTempYamlFile(filename)
cmd := getRootCommand()
result := runCmd(cmd, fmt.Sprintf("delete -d 1 %s [1]", filename))
if result.Error != nil {
t.Error(result.Error)
}
expectedOutput := `apples: great
---
- 1
- 3
`
assertResult(t, expectedOutput, result.Output)
}
func TestDeleteYamlMultiAllCmd(t *testing.T) {
content := `b:
c: 3
apples: great
---
apples: great
something: else
`
filename := writeTempYamlFile(content)
defer removeTempYamlFile(filename)
cmd := getRootCommand()
result := runCmd(cmd, fmt.Sprintf("delete %s -d * apples", filename))
if result.Error != nil {
t.Error(result.Error)
}
expectedOutput := `b:
c: 3
---
something: else`
assertResult(t, expectedOutput, strings.Trim(result.Output, "\n "))
}
func TestMergeCmd(t *testing.T) {
cmd := getRootCommand()
result := runCmd(cmd, "merge examples/data1.yaml examples/data2.yaml")
@@ -488,6 +604,99 @@ c:
assertResult(t, expectedOutput, result.Output)
}
func TestMergeOverwriteCmd(t *testing.T) {
cmd := getRootCommand()
result := runCmd(cmd, "merge --overwrite examples/data1.yaml examples/data2.yaml")
if result.Error != nil {
t.Error(result.Error)
}
expectedOutput := `a: other
b:
- 1
- 2
c:
test: 1
`
assertResult(t, expectedOutput, result.Output)
}
func TestMergeCmd_Multi(t *testing.T) {
cmd := getRootCommand()
result := runCmd(cmd, "merge -d1 examples/multiple_docs_small.yaml examples/data2.yaml")
if result.Error != nil {
t.Error(result.Error)
}
expectedOutput := `a: Easy! as one two three
---
a: other
another:
document: here
c:
test: 1
---
- 1
- 2`
assertResult(t, expectedOutput, strings.Trim(result.Output, "\n "))
}
func TestMergeYamlMultiAllCmd(t *testing.T) {
content := `b:
c: 3
apples: green
---
something: else`
filename := writeTempYamlFile(content)
defer removeTempYamlFile(filename)
mergeContent := `apples: red
something: good`
mergeFilename := writeTempYamlFile(mergeContent)
defer removeTempYamlFile(mergeFilename)
cmd := getRootCommand()
result := runCmd(cmd, fmt.Sprintf("merge -d* %s %s", filename, mergeFilename))
if result.Error != nil {
t.Error(result.Error)
}
expectedOutput := `apples: green
b:
c: 3
something: good
---
apples: red
something: else`
assertResult(t, expectedOutput, strings.Trim(result.Output, "\n "))
}
func TestMergeYamlMultiAllOverwriteCmd(t *testing.T) {
content := `b:
c: 3
apples: green
---
something: else`
filename := writeTempYamlFile(content)
defer removeTempYamlFile(filename)
mergeContent := `apples: red
something: good`
mergeFilename := writeTempYamlFile(mergeContent)
defer removeTempYamlFile(mergeFilename)
cmd := getRootCommand()
result := runCmd(cmd, fmt.Sprintf("merge --overwrite -d* %s %s", filename, mergeFilename))
if result.Error != nil {
t.Error(result.Error)
}
expectedOutput := `apples: red
b:
c: 3
something: good
---
apples: red
something: good`
assertResult(t, expectedOutput, strings.Trim(result.Output, "\n "))
}
func TestMergeCmd_Error(t *testing.T) {
cmd := getRootCommand()
result := runCmd(cmd, "merge examples/data1.yaml")
@@ -504,7 +713,7 @@ func TestMergeCmd_ErrorUnreadableFile(t *testing.T) {
if result.Error == nil {
t.Error("Expected command to fail due to unknown file")
}
expectedOutput := `open fake-unknown: no such file or directory`
expectedOutput := `Error updating document at index 0: open fake-unknown: no such file or directory`
assertResult(t, expectedOutput, result.Error.Error())
}
@@ -540,5 +749,5 @@ b:
- 2
c:
test: 1`
assertResult(t, expectedOutput, gotOutput)
assertResult(t, expectedOutput, strings.Trim(gotOutput, "\n "))
}

View File

@@ -2,10 +2,9 @@ package main
import (
"fmt"
"sort"
"strconv"
"gopkg.in/yaml.v2"
yaml "gopkg.in/mikefarah/yaml.v2"
)
func entryInSlice(context yaml.MapSlice, key interface{}) *yaml.MapItem {
@@ -18,9 +17,7 @@ func entryInSlice(context yaml.MapSlice, key interface{}) *yaml.MapItem {
return nil
}
func writeMap(context interface{}, paths []string, value interface{}) yaml.MapSlice {
log.Debugf("writeMap for %v for %v with value %v\n", paths, context, value)
func getMapSlice(context interface{}) yaml.MapSlice {
var mapSlice yaml.MapSlice
switch context.(type) {
case yaml.MapSlice:
@@ -28,6 +25,25 @@ func writeMap(context interface{}, paths []string, value interface{}) yaml.MapSl
default:
mapSlice = make(yaml.MapSlice, 0)
}
return mapSlice
}
func getArray(context interface{}) (array []interface{}, ok bool) {
switch context.(type) {
case []interface{}:
array = context.([]interface{})
ok = true
default:
array = make([]interface{}, 0)
ok = false
}
return
}
func writeMap(context interface{}, paths []string, value interface{}) yaml.MapSlice {
log.Debugf("writeMap for %v for %v with value %v\n", paths, context, value)
mapSlice := getMapSlice(context)
if len(paths) == 0 {
return mapSlice
@@ -66,13 +82,7 @@ func updatedChildValue(child interface{}, remainingPaths []string, value interfa
func writeArray(context interface{}, paths []string, value interface{}) []interface{} {
log.Debugf("writeArray for %v for %v with value %v\n", paths, context, value)
var array []interface{}
switch context.(type) {
case []interface{}:
array = context.([]interface{})
default:
array = make([]interface{}, 0)
}
array, _ := getArray(context)
if len(paths) == 0 {
return array
@@ -183,19 +193,92 @@ func calculateValue(value interface{}, tail []string) (interface{}, error) {
return value, nil
}
func mapToMapSlice(data map[interface{}]interface{}) yaml.MapSlice {
var mapSlice yaml.MapSlice
func deleteMap(context interface{}, paths []string) yaml.MapSlice {
log.Debugf("deleteMap for %v for %v\n", paths, context)
for k, v := range data {
if mv, ok := v.(map[interface{}]interface{}); ok {
v = mapToMapSlice(mv)
}
item := yaml.MapItem{Key: k, Value: v}
mapSlice = append(mapSlice, item)
mapSlice := getMapSlice(context)
if len(paths) == 0 {
return mapSlice
}
// because the parsing of the yaml was done via a map the order will be inconsistent
// apply order to allow a consistent output
sort.SliceStable(mapSlice, func(i, j int) bool { return mapSlice[i].Key.(string) < mapSlice[j].Key.(string) })
return mapSlice
var found bool
var index int
var child yaml.MapItem
for index, child = range mapSlice {
if child.Key == paths[0] {
found = true
break
}
}
if !found {
return mapSlice
}
remainingPaths := paths[1:]
var newSlice yaml.MapSlice
if len(remainingPaths) > 0 {
newChild := yaml.MapItem{Key: child.Key}
newChild.Value = deleteChildValue(child.Value, remainingPaths)
newSlice = make(yaml.MapSlice, len(mapSlice))
for i := range mapSlice {
item := mapSlice[i]
if i == index {
item = newChild
}
newSlice[i] = item
}
} else {
// Delete item from slice at index
newSlice = append(mapSlice[:index], mapSlice[index+1:]...)
log.Debugf("\tDeleted item index %d from mapSlice", index)
}
log.Debugf("\t\tlen: %d\tcap: %d\tslice: %v", len(mapSlice), cap(mapSlice), mapSlice)
log.Debugf("\tReturning mapSlice %v\n", mapSlice)
return newSlice
}
func deleteArray(context interface{}, paths []string, index int64) interface{} {
log.Debugf("deleteArray for %v for %v\n", paths, context)
array, ok := getArray(context)
if !ok {
// did not get an array
return context
}
if index >= int64(len(array)) {
return array
}
remainingPaths := paths[1:]
if len(remainingPaths) > 0 {
// Recurse into the array element at index
array[index] = deleteMap(array[index], remainingPaths)
} else {
// Delete the array element at index
array = append(array[:index], array[index+1:]...)
log.Debugf("\tDeleted item index %d from array, leaving %v", index, array)
}
log.Debugf("\tReturning array: %v\n", array)
return array
}
func deleteChildValue(child interface{}, remainingPaths []string) interface{} {
log.Debugf("deleteChildValue for %v for %v\n", remainingPaths, child)
idx, nextIndexErr := strconv.ParseInt(remainingPaths[0], 10, 64)
if nextIndexErr != nil {
// must be a map
log.Debugf("\tdetected a map, invoking deleteMap\n")
return deleteMap(child, remainingPaths)
}
log.Debugf("\tdetected an array, so traversing element with index %d\n", idx)
return deleteArray(child, remainingPaths, idx)
}

View File

@@ -5,7 +5,7 @@ import (
"sort"
"testing"
yaml "gopkg.in/yaml.v2"
yaml "gopkg.in/mikefarah/yaml.v2"
)
func TestReadMap_simple(t *testing.T) {
@@ -308,3 +308,80 @@ func TestWriteArray_no_paths(t *testing.T) {
result := writeArray(data, []string{}, 4)
assertResult(t, fmt.Sprintf("%v", data), fmt.Sprintf("%v", result))
}
func TestDelete_MapItem(t *testing.T) {
var data = parseData(`
a: 123
b: 456
`)
var expected = parseData(`
b: 456
`)
result := deleteMap(data, []string{"a"})
assertResult(t, fmt.Sprintf("%v", expected), fmt.Sprintf("%v", result))
}
// Ensure deleting an index into a string does nothing
func TestDelete_index_to_string(t *testing.T) {
var data = parseData(`
a: mystring
`)
result := deleteMap(data, []string{"a", "0"})
assertResult(t, fmt.Sprintf("%v", data), fmt.Sprintf("%v", result))
}
func TestDelete_list_index(t *testing.T) {
var data = parseData(`
a: [3, 4]
`)
var expected = parseData(`
a: [3]
`)
result := deleteMap(data, []string{"a", "1"})
assertResult(t, fmt.Sprintf("%v", expected), fmt.Sprintf("%v", result))
}
func TestDelete_list_index_beyond_bounds(t *testing.T) {
var data = parseData(`
a: [3, 4]
`)
result := deleteMap(data, []string{"a", "5"})
assertResult(t, fmt.Sprintf("%v", data), fmt.Sprintf("%v", result))
}
func TestDelete_list_index_out_of_bounds_by_1(t *testing.T) {
var data = parseData(`
a: [3, 4]
`)
result := deleteMap(data, []string{"a", "2"})
assertResult(t, fmt.Sprintf("%v", data), fmt.Sprintf("%v", result))
}
func TestDelete_no_paths(t *testing.T) {
var data = parseData(`
a: [3, 4]
b:
- name: test
`)
result := deleteMap(data, []string{})
assertResult(t, fmt.Sprintf("%v", data), fmt.Sprintf("%v", result))
}
func TestDelete_array_map_item(t *testing.T) {
var data = parseData(`
b:
- name: fred
value: blah
- name: john
value: test
`)
var expected = parseData(`
b:
- value: blah
- name: john
value: test
`)
result := deleteMap(data, []string{"b", "0", "name"})
assertResult(t, fmt.Sprintf("%v", expected), fmt.Sprintf("%v", result))
}

12
debian/changelog vendored Normal file
View File

@@ -0,0 +1,12 @@
yq (2.0-0) bionic; urgency=medium
* Release 2.0.0
-- Roberto Mier EscandĂłn <rmescandon@gmail.com> Wed, 20 Jun 2018 10:29:53 +0200
yq (1.15-0) bionic; urgency=medium
* Release 1.15
-- Roberto Mier EscandĂłn <rmescandon@gmail.com> Wed, 06 Jun 2018 11:32:03 +0200

1
debian/compat vendored Normal file
View File

@@ -0,0 +1 @@
9

22
debian/control vendored Normal file
View File

@@ -0,0 +1,22 @@
Source: yq
Section: devel
Priority: extra
Maintainer: Roberto Mier EscandĂłn <rmescandon@gmail.com>
Build-Depends: debhelper (>= 9),
dh-golang,
golang-1.10-go,
rsync
Standards-Version: 3.9.6
Homepage: https://github.com/mikefarah/yq.git
Vcs-Browser: https://github.com/mikefarah/yq.git
Vcs-Git: https://github.com/mikefarah/yq.git
Package: yq
Architecture: all
Built-Using: ${misc:Built-Using}
Depends: ${shlibs:Depends},
${misc:Depends}
Description:
a lightweight and portable command-line YAML processor
.
The aim of the project is to be the [jq](https://github.com/stedolan/jq) or sed of yaml files.

7
debian/copyright vendored Normal file
View File

@@ -0,0 +1,7 @@
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: yq
Source: https://github.com/mikefarah/yq.git
Files: *
Copyright: 2017 Mike Farah Ltd. All rights reserved
License: Proprietary

2
debian/gbp.conf vendored Normal file
View File

@@ -0,0 +1,2 @@
[DEFAULT]
pristine-tar = True

59
debian/rules vendored Executable file
View File

@@ -0,0 +1,59 @@
#!/usr/bin/make -f
#
# Copyright (C) 2018 Roberto Mier EscandĂłn <rmescandon@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 3 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
PROJECT := yq
OWNER := mikefarah
REPO := github.com
GOVERSION := 1.10
export DH_OPTIONS
export DH_GOPKG := ${REPO}/${OWNER}/${PROJECT}
export GOROOT := /usr/lib/go-${GOVERSION}
export GOPATH := ${CURDIR}/_build
export GOBIN := ${GOPATH}/bin
export PATH := ${GOROOT}/bin:${GOBIN}:${PATH}
BLDPATH := $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)
SRCDIR := ${CURDIR}/_build/src/${DH_GOPKG}
DESTDIR := ${CURDIR}/debian/${PROJECT}
BINDIR := /usr/bin
ASSETSDIR := /usr/share/${PROJECT}
%:
dh $@ --buildsystem=golang --with=golang
override_dh_auto_build:
mkdir -p ${SRCDIR}
mkdir -p ${GOBIN}
# copy project to local srcdir to build from there
rsync -avz --progress --exclude=obj-${BLDPATH} --exclude=debian . $(SRCDIR)
# build go code
(cd ${SRCDIR} && go install ./...)
override_dh_auto_test:
(cd ${SRCDIR} && go test -v ./...)
override_dh_auto_install:
mkdir -p ${DESTDIR}/${BINDIR}
mkdir -p ${DESTDIR}/${ASSETSDIR}
cp ${CURDIR}/_build/bin/yq ${DESTDIR}/${BINDIR}
cp -rf ${SRCDIR}/LICENSE ${DESTDIR}/${ASSETSDIR}
cp -rf ${SRCDIR}/README.md ${DESTDIR}/${PLUGINSDIR}
chmod a+x ${DESTDIR}/${BINDIR}/yq
override_dh_auto_clean:
dh_clean
rm -rf ${CURDIR}/obj-${BLDPATH}
rm -rf ${CURDIR}/_build

1
debian/source/format vendored Normal file
View File

@@ -0,0 +1 @@
3.0 (native)

View File

@@ -238,6 +238,18 @@
<li class="md-nav__item">
<a href="/delete/" title="Delete" class="md-nav__link">
Delete
</a>
</li>
<li class="md-nav__item">
<a href="/create/" title="Create" class="md-nav__link">
Create

View File

@@ -238,6 +238,18 @@
<li class="md-nav__item">
<a href="../delete/" title="Delete" class="md-nav__link">
Delete
</a>
</li>
<li class="md-nav__item">
<a href="../create/" title="Create" class="md-nav__link">
Create
@@ -360,7 +372,7 @@
<h1>Convert</h1>
<h3 id="yaml-to-json">Yaml to Json<a class="headerlink" href="#yaml-to-json" title="Permanent link">&para;</a></h3>
<p>To convert output to json, use the --tojson (or -j) flag. This can be used with any command.</p>
<p>To convert output to json, use the --tojson (or -j) flag. This can only be used with the read command.</p>
<p>Given a sample.yaml file of:</p>
<pre><code class="yaml">b:
c: 2

View File

@@ -237,6 +237,18 @@
<li class="md-nav__item">
<a href="../delete/" title="Delete" class="md-nav__link">
Delete
</a>
</li>
@@ -373,7 +385,7 @@
<h1>Create</h1>
<p>Yaml files can be created using the 'new' command. This works in the same way as the write command, but you don't pass in an existing Yaml file.</p>
<p>Yaml files can be created using the 'new' command. This works in the same way as the write command, but you don't pass in an existing Yaml file. Currently this does not support creating multiple documents in a single yaml file.</p>
<pre><code>yq n &lt;path&gt; &lt;new value&gt;
</code></pre>
@@ -440,7 +452,7 @@ b.e[0].name: Howdy Partner
<div class="md-footer-nav">
<nav class="md-footer-nav__inner md-grid">
<a href="../write/" title="Write/Update" class="md-flex md-footer-nav__link md-footer-nav__link--prev" rel="prev">
<a href="../delete/" title="Delete" class="md-flex md-footer-nav__link md-footer-nav__link--prev" rel="prev">
<div class="md-flex__cell md-flex__cell--shrink">
<i class="md-icon md-icon--arrow-back md-footer-nav__button"></i>
</div>
@@ -449,7 +461,7 @@ b.e[0].name: Howdy Partner
<span class="md-footer-nav__direction">
Previous
</span>
Write/Update
Delete
</span>
</div>
</a>

645
docs/delete/index.html Normal file
View File

@@ -0,0 +1,645 @@
<!DOCTYPE html>
<html lang="en" class="no-js">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="lang:clipboard.copy" content="Copy to clipboard">
<meta name="lang:clipboard.copied" content="Copied to clipboard">
<meta name="lang:search.language" content="en">
<meta name="lang:search.result.none" content="No matching documents">
<meta name="lang:search.result.one" content="1 matching document">
<meta name="lang:search.result.other" content="# matching documents">
<meta name="lang:search.tokenizer" content="[\s\-]+">
<link rel="shortcut icon" href="../assets/images/favicon.png">
<meta name="generator" content="mkdocs-0.17.2, mkdocs-material-2.2.5">
<title>Delete - Yq</title>
<link rel="stylesheet" href="../assets/stylesheets/application.bcabdff3.css">
<script src="../assets/javascripts/modernizr.1aa3b519.js"></script>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,400i,700|Roboto+Mono">
<style>body,input{font-family:"Roboto","Helvetica Neue",Helvetica,Arial,sans-serif}code,kbd,pre{font-family:"Roboto Mono","Courier New",Courier,monospace}</style>
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
</head>
<body>
<svg class="md-svg">
<defs>
<svg xmlns="http://www.w3.org/2000/svg" width="416" height="448" viewBox="0 0 416 448" id="github"><path fill="currentColor" d="M160 304q0 10-3.125 20.5t-10.75 19T128 352t-18.125-8.5-10.75-19T96 304t3.125-20.5 10.75-19T128 256t18.125 8.5 10.75 19T160 304zm160 0q0 10-3.125 20.5t-10.75 19T288 352t-18.125-8.5-10.75-19T256 304t3.125-20.5 10.75-19T288 256t18.125 8.5 10.75 19T320 304zm40 0q0-30-17.25-51T296 232q-10.25 0-48.75 5.25Q229.5 240 208 240t-39.25-2.75Q130.75 232 120 232q-29.5 0-46.75 21T56 304q0 22 8 38.375t20.25 25.75 30.5 15 35 7.375 37.25 1.75h42q20.5 0 37.25-1.75t35-7.375 30.5-15 20.25-25.75T360 304zm56-44q0 51.75-15.25 82.75-9.5 19.25-26.375 33.25t-35.25 21.5-42.5 11.875-42.875 5.5T212 416q-19.5 0-35.5-.75t-36.875-3.125-38.125-7.5-34.25-12.875T37 371.5t-21.5-28.75Q0 312 0 260q0-59.25 34-99-6.75-20.5-6.75-42.5 0-29 12.75-54.5 27 0 47.5 9.875t47.25 30.875Q171.5 96 212 96q37 0 70 8 26.25-20.5 46.75-30.25T376 64q12.75 25.5 12.75 54.5 0 21.75-6.75 42 34 40 34 99.5z"/></svg>
</defs>
</svg>
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="drawer">
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="search">
<label class="md-overlay" data-md-component="overlay" for="drawer"></label>
<header class="md-header" data-md-component="header">
<nav class="md-header-nav md-grid">
<div class="md-flex">
<div class="md-flex__cell md-flex__cell--shrink">
<a href=".." title="Yq" class="md-header-nav__button md-logo">
<i class="md-icon">î Ś</i>
</a>
</div>
<div class="md-flex__cell md-flex__cell--shrink">
<label class="md-icon md-icon--menu md-header-nav__button" for="drawer"></label>
</div>
<div class="md-flex__cell md-flex__cell--stretch">
<div class="md-flex__ellipsis md-header-nav__title" data-md-component="title">
<span class="md-header-nav__topic">
Yq
</span>
<span class="md-header-nav__topic">
Delete
</span>
</div>
</div>
<div class="md-flex__cell md-flex__cell--shrink">
<label class="md-icon md-icon--search md-header-nav__button" for="search"></label>
<div class="md-search" data-md-component="search" role="dialog">
<label class="md-search__overlay" for="search"></label>
<div class="md-search__inner">
<form class="md-search__form" name="search">
<input type="text" class="md-search__input" name="query" required placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="query">
<label class="md-icon md-search__icon" for="search"></label>
<button type="reset" class="md-icon md-search__icon" data-md-component="reset">&#xE5CD;</button>
</form>
<div class="md-search__output">
<div class="md-search__scrollwrap" data-md-scrollfix>
<div class="md-search-result" data-md-component="result">
<div class="md-search-result__meta">
Type to start searching
</div>
<ol class="md-search-result__list"></ol>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="md-flex__cell md-flex__cell--shrink">
<div class="md-header-nav__source">
<a href="https://github.com/mikefarah/yq/" title="Go to repository" class="md-source" data-md-source="github">
<div class="md-source__icon">
<svg viewBox="0 0 24 24" width="24" height="24">
<use xlink:href="#github" width="24" height="24"></use>
</svg>
</div>
<div class="md-source__repository">
mikefarah/yq
</div>
</a>
</div>
</div>
</div>
</nav>
</header>
<div class="md-container">
<main class="md-main">
<div class="md-main__inner md-grid" data-md-component="container">
<div class="md-sidebar md-sidebar--primary" data-md-component="navigation">
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--primary" data-md-level="0">
<label class="md-nav__title md-nav__title--site" for="drawer">
<span class="md-nav__button md-logo">
<i class="md-icon">î Ś</i>
</span>
Yq
</label>
<div class="md-nav__source">
<a href="https://github.com/mikefarah/yq/" title="Go to repository" class="md-source" data-md-source="github">
<div class="md-source__icon">
<svg viewBox="0 0 24 24" width="24" height="24">
<use xlink:href="#github" width="24" height="24"></use>
</svg>
</div>
<div class="md-source__repository">
mikefarah/yq
</div>
</a>
</div>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href=".." title="Install" class="md-nav__link">
Install
</a>
</li>
<li class="md-nav__item">
<a href="../read/" title="Read" class="md-nav__link">
Read
</a>
</li>
<li class="md-nav__item">
<a href="../write/" title="Write/Update" class="md-nav__link">
Write/Update
</a>
</li>
<li class="md-nav__item md-nav__item--active">
<input class="md-toggle md-nav__toggle" data-md-toggle="toc" type="checkbox" id="toc">
<label class="md-nav__link md-nav__link--active" for="toc">
Delete
</label>
<a href="./" title="Delete" class="md-nav__link md-nav__link--active">
Delete
</a>
<nav class="md-nav md-nav--secondary">
<label class="md-nav__title" for="toc">Table of contents</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="#to-stdout" title="To Stdout" class="md-nav__link">
To Stdout
</a>
</li>
<li class="md-nav__item">
<a href="#from-stdin" title="From STDIN" class="md-nav__link">
From STDIN
</a>
</li>
<li class="md-nav__item">
<a href="#deleting-array-elements" title="Deleting array elements" class="md-nav__link">
Deleting array elements
</a>
</li>
<li class="md-nav__item">
<a href="#deleting-nodes-in-place" title="Deleting nodes in-place" class="md-nav__link">
Deleting nodes in-place
</a>
</li>
<li class="md-nav__item">
<a href="#multiple-documents-delete-from-single-document" title="Multiple Documents - delete from single document" class="md-nav__link">
Multiple Documents - delete from single document
</a>
</li>
<li class="md-nav__item">
<a href="#multiple-documents-delete-from-all-documents" title="Multiple Documents - delete from all documents" class="md-nav__link">
Multiple Documents - delete from all documents
</a>
</li>
<li class="md-nav__item">
<a href="#keys-with-dots" title="Keys with dots" class="md-nav__link">
Keys with dots
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="../create/" title="Create" class="md-nav__link">
Create
</a>
</li>
<li class="md-nav__item">
<a href="../convert/" title="Convert" class="md-nav__link">
Convert
</a>
</li>
<li class="md-nav__item">
<a href="../merge/" title="Merge" class="md-nav__link">
Merge
</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="md-sidebar md-sidebar--secondary" data-md-component="toc">
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--secondary">
<label class="md-nav__title" for="toc">Table of contents</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="#to-stdout" title="To Stdout" class="md-nav__link">
To Stdout
</a>
</li>
<li class="md-nav__item">
<a href="#from-stdin" title="From STDIN" class="md-nav__link">
From STDIN
</a>
</li>
<li class="md-nav__item">
<a href="#deleting-array-elements" title="Deleting array elements" class="md-nav__link">
Deleting array elements
</a>
</li>
<li class="md-nav__item">
<a href="#deleting-nodes-in-place" title="Deleting nodes in-place" class="md-nav__link">
Deleting nodes in-place
</a>
</li>
<li class="md-nav__item">
<a href="#multiple-documents-delete-from-single-document" title="Multiple Documents - delete from single document" class="md-nav__link">
Multiple Documents - delete from single document
</a>
</li>
<li class="md-nav__item">
<a href="#multiple-documents-delete-from-all-documents" title="Multiple Documents - delete from all documents" class="md-nav__link">
Multiple Documents - delete from all documents
</a>
</li>
<li class="md-nav__item">
<a href="#keys-with-dots" title="Keys with dots" class="md-nav__link">
Keys with dots
</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="md-content">
<article class="md-content__inner md-typeset">
<a href="https://github.com/mikefarah/yq/edit/master/docs/delete.md" title="Edit this page" class="md-icon md-content__icon">&#xE3C9;</a>
<h1>Delete</h1>
<pre><code>yq d &lt;yaml_file&gt; &lt;path_to_delete&gt;
</code></pre>
<h3 id="to-stdout">To Stdout<a class="headerlink" href="#to-stdout" title="Permanent link">&para;</a></h3>
<p>Given a sample.yaml file of:</p>
<pre><code class="yaml">b:
c: 2
apples: green
</code></pre>
<p>then</p>
<pre><code class="bash">yq d sample.yaml b.c
</code></pre>
<p>will output:</p>
<pre><code class="yaml">b:
apples: green
</code></pre>
<h3 id="from-stdin">From STDIN<a class="headerlink" href="#from-stdin" title="Permanent link">&para;</a></h3>
<pre><code class="bash">cat sample.yaml | yq d - b.c
</code></pre>
<h3 id="deleting-array-elements">Deleting array elements<a class="headerlink" href="#deleting-array-elements" title="Permanent link">&para;</a></h3>
<p>Given a sample.yaml file of:</p>
<pre><code class="yaml">b:
c:
- 1
- 2
- 3
</code></pre>
<p>then</p>
<pre><code class="bash">yq d sample.yaml 'b.c[1]'
</code></pre>
<p>will output:</p>
<pre><code class="yaml">b:
c:
- 1
- 3
</code></pre>
<h3 id="deleting-nodes-in-place">Deleting nodes in-place<a class="headerlink" href="#deleting-nodes-in-place" title="Permanent link">&para;</a></h3>
<p>Given a sample.yaml file of:</p>
<pre><code class="yaml">b:
c: 2
apples: green
</code></pre>
<p>then</p>
<pre><code class="bash">yq d -i sample.yaml b.c
</code></pre>
<p>will update the sample.yaml file so that the 'c' node is deleted</p>
<h3 id="multiple-documents-delete-from-single-document">Multiple Documents - delete from single document<a class="headerlink" href="#multiple-documents-delete-from-single-document" title="Permanent link">&para;</a></h3>
<p>Given a sample.yaml file of:</p>
<pre><code class="yaml">something: else
field: leaveMe
---
b:
c: 2
field: deleteMe
</code></pre>
<p>then</p>
<pre><code class="bash">yq w -d1 sample.yaml field
</code></pre>
<p>will output:</p>
<pre><code class="yaml">something: else
field: leaveMe
---
b:
c: 2
</code></pre>
<h3 id="multiple-documents-delete-from-all-documents">Multiple Documents - delete from all documents<a class="headerlink" href="#multiple-documents-delete-from-all-documents" title="Permanent link">&para;</a></h3>
<p>Given a sample.yaml file of:</p>
<pre><code class="yaml">something: else
field: deleteMe
---
b:
c: 2
field: deleteMeToo
</code></pre>
<p>then</p>
<pre><code class="bash">yq w -d'*' sample.yaml field
</code></pre>
<p>will output:</p>
<pre><code class="yaml">something: else
---
b:
c: 2
</code></pre>
<p>Note that '*' is in quotes to avoid being interpreted by your shell.</p>
<h3 id="keys-with-dots">Keys with dots<a class="headerlink" href="#keys-with-dots" title="Permanent link">&para;</a></h3>
<p>When specifying a key that has a dot use key lookup indicator.</p>
<pre><code class="yaml">b:
foo.bar: 7
</code></pre>
<pre><code class="bash">yaml r sample.yaml 'b[foo.bar]'
</code></pre>
<pre><code class="bash">yaml w sample.yaml 'b[foo.bar]' 9
</code></pre>
<p>Any valid yaml key can be specified as part of a key lookup.</p>
<p>Note that the path is in quotes to avoid the square brackets being interpreted by your shell.</p>
</article>
</div>
</div>
</main>
<footer class="md-footer">
<div class="md-footer-nav">
<nav class="md-footer-nav__inner md-grid">
<a href="../write/" title="Write/Update" class="md-flex md-footer-nav__link md-footer-nav__link--prev" rel="prev">
<div class="md-flex__cell md-flex__cell--shrink">
<i class="md-icon md-icon--arrow-back md-footer-nav__button"></i>
</div>
<div class="md-flex__cell md-flex__cell--stretch md-footer-nav__title">
<span class="md-flex__ellipsis">
<span class="md-footer-nav__direction">
Previous
</span>
Write/Update
</span>
</div>
</a>
<a href="../create/" title="Create" class="md-flex md-footer-nav__link md-footer-nav__link--next" rel="next">
<div class="md-flex__cell md-flex__cell--stretch md-footer-nav__title">
<span class="md-flex__ellipsis">
<span class="md-footer-nav__direction">
Next
</span>
Create
</span>
</div>
<div class="md-flex__cell md-flex__cell--shrink">
<i class="md-icon md-icon--arrow-forward md-footer-nav__button"></i>
</div>
</a>
</nav>
</div>
<div class="md-footer-meta md-typeset">
<div class="md-footer-meta__inner md-grid">
<div class="md-footer-copyright">
powered by
<a href="http://www.mkdocs.org">MkDocs</a>
and
<a href="https://squidfunk.github.io/mkdocs-material/">
Material for MkDocs</a>
</div>
<div class="md-footer-social">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<a href="https://github.com/mikefarah" class="md-footer-social__link fa fa-github"></a>
<a href="https://www.linkedin.com/in/mike-farah-b5a75b2/" class="md-footer-social__link fa fa-linkedin"></a>
</div>
</div>
</div>
</footer>
</div>
<script src="../assets/javascripts/application.6cdc17f0.js"></script>
<script>app.initialize({version:"0.17.2",url:{base:".."}})</script>
</body>
</html>

View File

@@ -274,6 +274,18 @@
<li class="md-nav__item">
<a href="delete/" title="Delete" class="md-nav__link">
Delete
</a>
</li>
<li class="md-nav__item">
<a href="create/" title="Create" class="md-nav__link">
Create
@@ -361,8 +373,14 @@
<pre><code>snap install yq
</code></pre>
<p>On Ubuntu 16.04 or higher from Debian package:</p>
<pre><code>sudo add-apt-repository ppa:rmescandon/yq
sudo apt update
sudo apt install yq -y
</code></pre>
<p>or, <a href="https://github.com/mikefarah/yq/releases/latest">Download latest binary</a> or alternatively:</p>
<pre><code>go get github.com/mikefarah/yq
<pre><code>go get gopkg.in/mikefarah/yq.v2
</code></pre>
<p><a href="https://github.com/mikefarah/yq">View on GitHub</a></p>

View File

@@ -238,6 +238,18 @@
<li class="md-nav__item">
<a href="../delete/" title="Delete" class="md-nav__link">
Delete
</a>
</li>
<li class="md-nav__item">
<a href="../create/" title="Create" class="md-nav__link">
Create
@@ -313,6 +325,20 @@
</li>
<li class="md-nav__item">
<a href="#multiple-documents-merge-into-single-document" title="Multiple Documents - merge into single document" class="md-nav__link">
Multiple Documents - merge into single document
</a>
</li>
<li class="md-nav__item">
<a href="#multiple-documents-merge-into-all-documents" title="Multiple Documents - merge into all documents" class="md-nav__link">
Multiple Documents - merge into all documents
</a>
</li>
</ul>
@@ -368,6 +394,20 @@
</li>
<li class="md-nav__item">
<a href="#multiple-documents-merge-into-single-document" title="Multiple Documents - merge into single document" class="md-nav__link">
Multiple Documents - merge into single document
</a>
</li>
<li class="md-nav__item">
<a href="#multiple-documents-merge-into-all-documents" title="Multiple Documents - merge into all documents" class="md-nav__link">
Multiple Documents - merge into all documents
</a>
</li>
</ul>
@@ -389,10 +429,9 @@
<p>Yaml files can be merged using the 'merge' command. Each additional file merged with the first file will
set values for any key not existing already or where the key has no value.</p>
<pre><code>yq m &lt;yaml_file|json_file&gt; &lt;path&gt;...
<pre><code>yq m &lt;yaml_file&gt; &lt;path&gt;...
</code></pre>
<p>This command can take a json file as input too, and will output yaml unless specified to export as json (-j)</p>
<h3 id="to-stdout">To Stdout<a class="headerlink" href="#to-stdout" title="Permanent link">&para;</a></h3>
<p>Given a data1.yaml file of:</p>
<pre><code class="yaml">a: simple
@@ -485,6 +524,54 @@ d: false
<p>Notice that 'b' does not result in the merging of the values within an array. The underlying library does not
currently handle merging values within an array.</p>
<h3 id="multiple-documents-merge-into-single-document">Multiple Documents - merge into single document<a class="headerlink" href="#multiple-documents-merge-into-single-document" title="Permanent link">&para;</a></h3>
<p>Currently yq only has multi-document support for the <em>first</em> document being merged into. The remaining yaml files will have their first document selected.</p>
<p>Given a data1.yaml file of:</p>
<pre><code class="yaml">something: else
---
a: simple
b: cat
</code></pre>
<p>and data3.yaml file of:</p>
<pre><code class="yaml">b: dog
</code></pre>
<p>then</p>
<pre><code class="bash">yq m -x -d1 data1.yaml data3.yaml
</code></pre>
<p>will output:</p>
<pre><code class="yaml">something: else
---
a: simple
b: dog
</code></pre>
<h3 id="multiple-documents-merge-into-all-documents">Multiple Documents - merge into all documents<a class="headerlink" href="#multiple-documents-merge-into-all-documents" title="Permanent link">&para;</a></h3>
<p>Currently yq only has multi-document support for the <em>first</em> document being merged into. The remaining yaml files will have their first document selected.</p>
<p>Given a data1.yaml file of:</p>
<pre><code class="yaml">something: else
---
a: simple
b: cat
</code></pre>
<p>and data3.yaml file of:</p>
<pre><code class="yaml">b: dog
</code></pre>
<p>then</p>
<pre><code class="bash">yq m -x -d'*' data1.yaml data3.yaml
</code></pre>
<p>will output:</p>
<pre><code class="yaml">b: dog
something: else
---
a: simple
b: dog
</code></pre>

View File

@@ -259,8 +259,8 @@
</li>
<li class="md-nav__item">
<a href="#handling-in-the-yaml-key" title="Handling '.' in the yaml key" class="md-nav__link">
Handling '.' in the yaml key
<a href="#multiple-documents" title="Multiple Documents" class="md-nav__link">
Multiple Documents
</a>
</li>
@@ -312,6 +312,18 @@
<li class="md-nav__item">
<a href="../delete/" title="Delete" class="md-nav__link">
Delete
</a>
</li>
<li class="md-nav__item">
<a href="../create/" title="Create" class="md-nav__link">
Create
@@ -383,8 +395,8 @@
</li>
<li class="md-nav__item">
<a href="#handling-in-the-yaml-key" title="Handling '.' in the yaml key" class="md-nav__link">
Handling '.' in the yaml key
<a href="#multiple-documents" title="Multiple Documents" class="md-nav__link">
Multiple Documents
</a>
</li>
@@ -469,14 +481,16 @@ bob:
- apples
</code></pre>
<h3 id="handling-in-the-yaml-key">Handling '.' in the yaml key<a class="headerlink" href="#handling-in-the-yaml-key" title="Permanent link">&para;</a></h3>
<h3 id="multiple-documents">Multiple Documents<a class="headerlink" href="#multiple-documents" title="Permanent link">&para;</a></h3>
<p>Given a sample.yaml file of:</p>
<pre><code class="yaml">b.x:
<pre><code class="yaml">something: else
---
b:
c: 2
</code></pre>
<p>then</p>
<pre><code class="bash">yq r sample.yaml \&quot;b.x\&quot;.c
<pre><code class="bash">yq r -d1 sample.yaml b.c
</code></pre>
<p>will output the value of '2'.</p>

View File

@@ -2,7 +2,7 @@
"docs": [
{
"location": "/",
"text": "yq\n\u00b6\n\n\nyq is a lightweight and portable command-line YAML processor\n\n\nThe aim of the project is to be the \njq\n or sed of yaml files.\n\n\nInstall\n\u00b6\n\n\nOn MacOS:\n\n\nbrew install yq\n\n\n\n\nOn Ubuntu and other Linux distros supporting \nsnap\n packages:\n\n\nsnap install yq\n\n\n\n\nor, \nDownload latest binary\n or alternatively:\n\n\ngo get github.com/mikefarah/yq\n\n\n\n\nView on GitHub",
"text": "yq\n\u00b6\n\n\nyq is a lightweight and portable command-line YAML processor\n\n\nThe aim of the project is to be the \njq\n or sed of yaml files.\n\n\nInstall\n\u00b6\n\n\nOn MacOS:\n\n\nbrew install yq\n\n\n\n\nOn Ubuntu and other Linux distros supporting \nsnap\n packages:\n\n\nsnap install yq\n\n\n\n\nOn Ubuntu 16.04 or higher from Debian package:\n\n\nsudo add-apt-repository ppa:rmescandon/yq\nsudo apt update\nsudo apt install yq -y\n\n\n\n\nor, \nDownload latest binary\n or alternatively:\n\n\ngo get gopkg.in/mikefarah/yq.v2\n\n\n\n\nView on GitHub",
"title": "Install"
},
{
@@ -12,12 +12,12 @@
},
{
"location": "/#install",
"text": "On MacOS: brew install yq On Ubuntu and other Linux distros supporting snap packages: snap install yq or, Download latest binary or alternatively: go get github.com/mikefarah/yq View on GitHub",
"text": "On MacOS: brew install yq On Ubuntu and other Linux distros supporting snap packages: snap install yq On Ubuntu 16.04 or higher from Debian package: sudo add-apt-repository ppa:rmescandon/yq\nsudo apt update\nsudo apt install yq -y or, Download latest binary or alternatively: go get gopkg.in/mikefarah/yq.v2 View on GitHub",
"title": "Install"
},
{
"location": "/read/",
"text": "yq r <yaml_file|json_file> <path>\n\n\n\n\nThis command can take a json file as input too, and will output yaml unless specified to export as json (-j)\n\n\nBasic\n\u00b6\n\n\nGiven a sample.yaml file of:\n\n\nb:\n c: 2\n\n\n\n\nthen\n\n\nyq r sample.yaml b.c\n\n\n\n\nwill output the value of '2'.\n\n\nFrom Stdin\n\u00b6\n\n\nGiven a sample.yaml file of:\n\n\ncat sample.yaml | yq r - b.c\n\n\n\n\nwill output the value of '2'.\n\n\nSplat\n\u00b6\n\n\nGiven a sample.yaml file of:\n\n\n---\nbob:\n item1:\n cats: bananas\n item2:\n cats: apples\n\n\n\n\nthen\n\n\nyq r sample.yaml bob.*.cats\n\n\n\n\nwill output\n\n\n- bananas\n- apples\n\n\n\n\nHandling '.' in the yaml key\n\u00b6\n\n\nGiven a sample.yaml file of:\n\n\nb.x:\n c: 2\n\n\n\n\nthen\n\n\nyq r sample.yaml \\\"b.x\\\".c\n\n\n\n\nwill output the value of '2'.\n\n\nArrays\n\u00b6\n\n\nYou can give an index to access a specific element:\ne.g.: given a sample file of\n\n\nb:\n e:\n - name: fred\n value: 3\n - name: sam\n value: 4\n\n\n\n\nthen\n\n\nyq r sample.yaml 'b.e[1].name'\n\n\n\n\nwill output 'sam'\n\n\nNote that the path is in quotes to avoid the square brackets being interpreted by your shell.\n\n\nArray Splat\n\u00b6\n\n\ne.g.: given a sample file of\n\n\nb:\n e:\n - name: fred\n value: 3\n - name: sam\n value: 4\n\n\n\n\nthen\n\n\nyq r sample.yaml 'b.e[*].name'\n\n\n\n\nwill output:\n\n\n- fred\n- sam\n\n\n\n\nNote that the path is in quotes to avoid the square brackets being interpreted by your shell.\n\n\nKeys with dots\n\u00b6\n\n\nWhen specifying a key that has a dot use key lookup indicator.\n\n\nb:\n foo.bar: 7\n\n\n\n\nyaml r sample.yaml 'b[foo.bar]'\n\n\n\n\nyaml w sample.yaml 'b[foo.bar]' 9\n\n\n\n\nAny valid yaml key can be specified as part of a key lookup.\n\n\nNote that the path is in quotes to avoid the square brackets being interpreted by your shell.",
"text": "yq r <yaml_file|json_file> <path>\n\n\n\n\nThis command can take a json file as input too, and will output yaml unless specified to export as json (-j)\n\n\nBasic\n\u00b6\n\n\nGiven a sample.yaml file of:\n\n\nb:\n c: 2\n\n\n\n\nthen\n\n\nyq r sample.yaml b.c\n\n\n\n\nwill output the value of '2'.\n\n\nFrom Stdin\n\u00b6\n\n\nGiven a sample.yaml file of:\n\n\ncat sample.yaml | yq r - b.c\n\n\n\n\nwill output the value of '2'.\n\n\nSplat\n\u00b6\n\n\nGiven a sample.yaml file of:\n\n\n---\nbob:\n item1:\n cats: bananas\n item2:\n cats: apples\n\n\n\n\nthen\n\n\nyq r sample.yaml bob.*.cats\n\n\n\n\nwill output\n\n\n- bananas\n- apples\n\n\n\n\nMultiple Documents\n\u00b6\n\n\nGiven a sample.yaml file of:\n\n\nsomething: else\n---\nb:\n c: 2\n\n\n\n\nthen\n\n\nyq r -d1 sample.yaml b.c\n\n\n\n\nwill output the value of '2'.\n\n\nArrays\n\u00b6\n\n\nYou can give an index to access a specific element:\ne.g.: given a sample file of\n\n\nb:\n e:\n - name: fred\n value: 3\n - name: sam\n value: 4\n\n\n\n\nthen\n\n\nyq r sample.yaml 'b.e[1].name'\n\n\n\n\nwill output 'sam'\n\n\nNote that the path is in quotes to avoid the square brackets being interpreted by your shell.\n\n\nArray Splat\n\u00b6\n\n\ne.g.: given a sample file of\n\n\nb:\n e:\n - name: fred\n value: 3\n - name: sam\n value: 4\n\n\n\n\nthen\n\n\nyq r sample.yaml 'b.e[*].name'\n\n\n\n\nwill output:\n\n\n- fred\n- sam\n\n\n\n\nNote that the path is in quotes to avoid the square brackets being interpreted by your shell.\n\n\nKeys with dots\n\u00b6\n\n\nWhen specifying a key that has a dot use key lookup indicator.\n\n\nb:\n foo.bar: 7\n\n\n\n\nyaml r sample.yaml 'b[foo.bar]'\n\n\n\n\nyaml w sample.yaml 'b[foo.bar]' 9\n\n\n\n\nAny valid yaml key can be specified as part of a key lookup.\n\n\nNote that the path is in quotes to avoid the square brackets being interpreted by your shell.",
"title": "Read"
},
{
@@ -36,9 +36,9 @@
"title": "Splat"
},
{
"location": "/read/#handling-in-the-yaml-key",
"text": "Given a sample.yaml file of: b.x:\n c: 2 then yq r sample.yaml \\\"b.x\\\".c will output the value of '2'.",
"title": "Handling '.' in the yaml key"
"location": "/read/#multiple-documents",
"text": "Given a sample.yaml file of: something: else\n---\nb:\n c: 2 then yq r -d1 sample.yaml b.c will output the value of '2'.",
"title": "Multiple Documents"
},
{
"location": "/read/#arrays",
@@ -57,7 +57,7 @@
},
{
"location": "/write/",
"text": "yq w <yaml_file|json_file> <path> <new value>\n\n\n\n\nThis command can take a json file as input too, and will output yaml unless specified to export as json (-j)\n\n\nTo Stdout\n\u00b6\n\n\nGiven a sample.yaml file of:\n\n\nb:\n c: 2\n\n\n\n\nthen\n\n\nyq w sample.yaml b.c cat\n\n\n\n\nwill output:\n\n\nb:\n c: cat\n\n\n\n\nFrom STDIN\n\u00b6\n\n\ncat sample.yaml | yq w - b.c blah\n\n\n\n\nAdding new fields\n\u00b6\n\n\nAny missing fields in the path will be created on the fly.\n\n\nGiven a sample.yaml file of:\n\n\nb:\n c: 2\n\n\n\n\nthen\n\n\nyq w sample.yaml b.d[0] \"new thing\"\n\n\n\n\nwill output:\n\n\nb:\n c: cat\n d:\n - new thing\n\n\n\n\nAppending value to an array field\n\u00b6\n\n\nGiven a sample.yaml file of:\n\n\nb:\n c: 2\n d:\n - new thing\n - foo thing\n\n\n\n\nthen\n\n\nyq w sample.yaml \"b.d[+]\" \"bar thing\"\n\n\n\n\nwill output:\n\n\nb:\n c: cat\n d:\n - new thing\n - foo thing\n - bar thing\n\n\n\n\nNote that the path is in quotes to avoid the square brackets being interpreted by your shell.\n\n\nUpdating files in-place\n\u00b6\n\n\nGiven a sample.yaml file of:\n\n\nb:\n c: 2\n\n\n\n\nthen\n\n\nyq w -i sample.yaml b.c cat\n\n\n\n\nwill update the sample.yaml file so that the value of 'c' is cat.\n\n\nUpdating multiple values with a script\n\u00b6\n\n\nGiven a sample.yaml file of:\n\n\nb:\n c: 2\n e:\n - name: Billy Bob\n\n\n\n\nand a script update_instructions.yaml of:\n\n\nb.c: 3\nb.e[0].name: Howdy Partner\n\n\n\n\nthen\n\n\nyq w -s update_instructions.yaml sample.yaml\n\n\n\n\nwill output:\n\n\nb:\n c: 3\n e:\n - name: Howdy Partner\n\n\n\n\nAnd, of course, you can pipe the instructions in using '-':\n\n\ncat update_instructions.yaml | yq w -s - sample.yaml\n\n\n\n\nValues starting with a hyphen (or dash)\n\u00b6\n\n\nThe flag terminator needs to be used to stop the app from attempting to parse the subsequent arguments as flags:\n\n\nyq w -- my.path -3\n\n\n\n\nwill output\n\n\nmy:\n path: -3\n\n\n\n\nKeys with dots\n\u00b6\n\n\nWhen specifying a key that has a dot use key lookup indicator.\n\n\nb:\n foo.bar: 7\n\n\n\n\nyaml r sample.yaml 'b[foo.bar]'\n\n\n\n\nyaml w sample.yaml 'b[foo.bar]' 9\n\n\n\n\nAny valid yaml key can be specified as part of a key lookup.\n\n\nNote that the path is in quotes to avoid the square brackets being interpreted by your shell.",
"text": "yq w <yaml_file> <path> <new value>\n\n\n\n\nTo Stdout\n\u00b6\n\n\nGiven a sample.yaml file of:\n\n\nb:\n c: 2\n\n\n\n\nthen\n\n\nyq w sample.yaml b.c cat\n\n\n\n\nwill output:\n\n\nb:\n c: cat\n\n\n\n\nFrom STDIN\n\u00b6\n\n\ncat sample.yaml | yq w - b.c blah\n\n\n\n\nAdding new fields\n\u00b6\n\n\nAny missing fields in the path will be created on the fly.\n\n\nGiven a sample.yaml file of:\n\n\nb:\n c: 2\n\n\n\n\nthen\n\n\nyq w sample.yaml b.d[0] \"new thing\"\n\n\n\n\nwill output:\n\n\nb:\n c: cat\n d:\n - new thing\n\n\n\n\nAppending value to an array field\n\u00b6\n\n\nGiven a sample.yaml file of:\n\n\nb:\n c: 2\n d:\n - new thing\n - foo thing\n\n\n\n\nthen\n\n\nyq w sample.yaml \"b.d[+]\" \"bar thing\"\n\n\n\n\nwill output:\n\n\nb:\n c: cat\n d:\n - new thing\n - foo thing\n - bar thing\n\n\n\n\nNote that the path is in quotes to avoid the square brackets being interpreted by your shell.\n\n\nMultiple Documents - update a single document\n\u00b6\n\n\nGiven a sample.yaml file of:\n\n\nsomething: else\n---\nb:\n c: 2\n\n\n\n\nthen\n\n\nyq w -d1 sample.yaml b.c 5\n\n\n\n\nwill output:\n\n\nsomething: else\n---\nb:\n c: 5\n\n\n\n\nMultiple Documents - update all documents\n\u00b6\n\n\nGiven a sample.yaml file of:\n\n\nsomething: else\n---\nb:\n c: 2\n\n\n\n\nthen\n\n\nyq w -d'*' sample.yaml b.c 5\n\n\n\n\nwill output:\n\n\nsomething: else\nb:\n c: 5\n---\nb:\n c: 5\n\n\n\n\nNote that '*' is in quotes to avoid being interpreted by your shell.\n\n\nUpdating files in-place\n\u00b6\n\n\nGiven a sample.yaml file of:\n\n\nb:\n c: 2\n\n\n\n\nthen\n\n\nyq w -i sample.yaml b.c cat\n\n\n\n\nwill update the sample.yaml file so that the value of 'c' is cat.\n\n\nUpdating multiple values with a script\n\u00b6\n\n\nGiven a sample.yaml file of:\n\n\nb:\n c: 2\n e:\n - name: Billy Bob\n\n\n\n\nand a script update_instructions.yaml of:\n\n\nb.c: 3\nb.e[0].name: Howdy Partner\n\n\n\n\nthen\n\n\nyq w -s update_instructions.yaml sample.yaml\n\n\n\n\nwill output:\n\n\nb:\n c: 3\n e:\n - name: Howdy Partner\n\n\n\n\nAnd, of course, you can pipe the instructions in using '-':\n\n\ncat update_instructions.yaml | yq w -s - sample.yaml\n\n\n\n\nValues starting with a hyphen (or dash)\n\u00b6\n\n\nThe flag terminator needs to be used to stop the app from attempting to parse the subsequent arguments as flags:\n\n\nyq w -- my.path -3\n\n\n\n\nwill output\n\n\nmy:\n path: -3\n\n\n\n\nKeys with dots\n\u00b6\n\n\nWhen specifying a key that has a dot use key lookup indicator.\n\n\nb:\n foo.bar: 7\n\n\n\n\nyaml r sample.yaml 'b[foo.bar]'\n\n\n\n\nyaml w sample.yaml 'b[foo.bar]' 9\n\n\n\n\nAny valid yaml key can be specified as part of a key lookup.\n\n\nNote that the path is in quotes to avoid the square brackets being interpreted by your shell.",
"title": "Write/Update"
},
{
@@ -80,6 +80,16 @@
"text": "Given a sample.yaml file of: b:\n c: 2\n d:\n - new thing\n - foo thing then yq w sample.yaml \"b.d[+]\" \"bar thing\" will output: b:\n c: cat\n d:\n - new thing\n - foo thing\n - bar thing Note that the path is in quotes to avoid the square brackets being interpreted by your shell.",
"title": "Appending value to an array field"
},
{
"location": "/write/#multiple-documents-update-a-single-document",
"text": "Given a sample.yaml file of: something: else\n---\nb:\n c: 2 then yq w -d1 sample.yaml b.c 5 will output: something: else\n---\nb:\n c: 5",
"title": "Multiple Documents - update a single document"
},
{
"location": "/write/#multiple-documents-update-all-documents",
"text": "Given a sample.yaml file of: something: else\n---\nb:\n c: 2 then yq w -d'*' sample.yaml b.c 5 will output: something: else\nb:\n c: 5\n---\nb:\n c: 5 Note that '*' is in quotes to avoid being interpreted by your shell.",
"title": "Multiple Documents - update all documents"
},
{
"location": "/write/#updating-files-in-place",
"text": "Given a sample.yaml file of: b:\n c: 2 then yq w -i sample.yaml b.c cat will update the sample.yaml file so that the value of 'c' is cat.",
@@ -100,9 +110,49 @@
"text": "When specifying a key that has a dot use key lookup indicator. b:\n foo.bar: 7 yaml r sample.yaml 'b[foo.bar]' yaml w sample.yaml 'b[foo.bar]' 9 Any valid yaml key can be specified as part of a key lookup. Note that the path is in quotes to avoid the square brackets being interpreted by your shell.",
"title": "Keys with dots"
},
{
"location": "/delete/",
"text": "yq d <yaml_file> <path_to_delete>\n\n\n\n\nTo Stdout\n\u00b6\n\n\nGiven a sample.yaml file of:\n\n\nb:\n c: 2\n apples: green\n\n\n\n\nthen\n\n\nyq d sample.yaml b.c\n\n\n\n\nwill output:\n\n\nb:\n apples: green\n\n\n\n\nFrom STDIN\n\u00b6\n\n\ncat sample.yaml | yq d - b.c\n\n\n\n\nDeleting array elements\n\u00b6\n\n\nGiven a sample.yaml file of:\n\n\nb:\n c: \n - 1\n - 2\n - 3\n\n\n\n\nthen\n\n\nyq d sample.yaml 'b.c[1]'\n\n\n\n\nwill output:\n\n\nb:\n c:\n - 1\n - 3\n\n\n\n\nDeleting nodes in-place\n\u00b6\n\n\nGiven a sample.yaml file of:\n\n\nb:\n c: 2\n apples: green\n\n\n\n\nthen\n\n\nyq d -i sample.yaml b.c\n\n\n\n\nwill update the sample.yaml file so that the 'c' node is deleted\n\n\nMultiple Documents - delete from single document\n\u00b6\n\n\nGiven a sample.yaml file of:\n\n\nsomething: else\nfield: leaveMe\n---\nb:\n c: 2\nfield: deleteMe\n\n\n\n\nthen\n\n\nyq w -d1 sample.yaml field\n\n\n\n\nwill output:\n\n\nsomething: else\nfield: leaveMe\n---\nb:\n c: 2\n\n\n\n\nMultiple Documents - delete from all documents\n\u00b6\n\n\nGiven a sample.yaml file of:\n\n\nsomething: else\nfield: deleteMe\n---\nb:\n c: 2\nfield: deleteMeToo\n\n\n\n\nthen\n\n\nyq w -d'*' sample.yaml field\n\n\n\n\nwill output:\n\n\nsomething: else\n---\nb:\n c: 2\n\n\n\n\nNote that '*' is in quotes to avoid being interpreted by your shell.\n\n\nKeys with dots\n\u00b6\n\n\nWhen specifying a key that has a dot use key lookup indicator.\n\n\nb:\n foo.bar: 7\n\n\n\n\nyaml r sample.yaml 'b[foo.bar]'\n\n\n\n\nyaml w sample.yaml 'b[foo.bar]' 9\n\n\n\n\nAny valid yaml key can be specified as part of a key lookup.\n\n\nNote that the path is in quotes to avoid the square brackets being interpreted by your shell.",
"title": "Delete"
},
{
"location": "/delete/#to-stdout",
"text": "Given a sample.yaml file of: b:\n c: 2\n apples: green then yq d sample.yaml b.c will output: b:\n apples: green",
"title": "To Stdout"
},
{
"location": "/delete/#from-stdin",
"text": "cat sample.yaml | yq d - b.c",
"title": "From STDIN"
},
{
"location": "/delete/#deleting-array-elements",
"text": "Given a sample.yaml file of: b:\n c: \n - 1\n - 2\n - 3 then yq d sample.yaml 'b.c[1]' will output: b:\n c:\n - 1\n - 3",
"title": "Deleting array elements"
},
{
"location": "/delete/#deleting-nodes-in-place",
"text": "Given a sample.yaml file of: b:\n c: 2\n apples: green then yq d -i sample.yaml b.c will update the sample.yaml file so that the 'c' node is deleted",
"title": "Deleting nodes in-place"
},
{
"location": "/delete/#multiple-documents-delete-from-single-document",
"text": "Given a sample.yaml file of: something: else\nfield: leaveMe\n---\nb:\n c: 2\nfield: deleteMe then yq w -d1 sample.yaml field will output: something: else\nfield: leaveMe\n---\nb:\n c: 2",
"title": "Multiple Documents - delete from single document"
},
{
"location": "/delete/#multiple-documents-delete-from-all-documents",
"text": "Given a sample.yaml file of: something: else\nfield: deleteMe\n---\nb:\n c: 2\nfield: deleteMeToo then yq w -d'*' sample.yaml field will output: something: else\n---\nb:\n c: 2 Note that '*' is in quotes to avoid being interpreted by your shell.",
"title": "Multiple Documents - delete from all documents"
},
{
"location": "/delete/#keys-with-dots",
"text": "When specifying a key that has a dot use key lookup indicator. b:\n foo.bar: 7 yaml r sample.yaml 'b[foo.bar]' yaml w sample.yaml 'b[foo.bar]' 9 Any valid yaml key can be specified as part of a key lookup. Note that the path is in quotes to avoid the square brackets being interpreted by your shell.",
"title": "Keys with dots"
},
{
"location": "/create/",
"text": "Yaml files can be created using the 'new' command. This works in the same way as the write command, but you don't pass in an existing Yaml file.\n\n\nyq n <path> <new value>\n\n\n\n\nCreating a simple yaml file\n\u00b6\n\n\nyq n b.c cat\n\n\n\n\nwill output:\n\n\nb:\n c: cat\n\n\n\n\nCreating using a create script\n\u00b6\n\n\nCreate scripts follow the same format as the update scripts.\n\n\nGiven a script create_instructions.yaml of:\n\n\nb.c: 3\nb.e[0].name: Howdy Partner\n\n\n\n\nthen\n\n\nyq n -s create_instructions.yaml\n\n\n\n\nwill output:\n\n\nb:\n c: 3\n e:\n - name: Howdy Partner\n\n\n\n\nYou can also pipe the instructions in:\n\n\ncat create_instructions.yaml | yq n -s -\n\n\n\n\nKeys with dots\n\u00b6\n\n\nWhen specifying a key that has a dot use key lookup indicator.\n\n\nb:\n foo.bar: 7\n\n\n\n\nyaml r sample.yaml 'b[foo.bar]'\n\n\n\n\nyaml w sample.yaml 'b[foo.bar]' 9\n\n\n\n\nAny valid yaml key can be specified as part of a key lookup.\n\n\nNote that the path is in quotes to avoid the square brackets being interpreted by your shell.",
"text": "Yaml files can be created using the 'new' command. This works in the same way as the write command, but you don't pass in an existing Yaml file. Currently this does not support creating multiple documents in a single yaml file.\n\n\nyq n <path> <new value>\n\n\n\n\nCreating a simple yaml file\n\u00b6\n\n\nyq n b.c cat\n\n\n\n\nwill output:\n\n\nb:\n c: cat\n\n\n\n\nCreating using a create script\n\u00b6\n\n\nCreate scripts follow the same format as the update scripts.\n\n\nGiven a script create_instructions.yaml of:\n\n\nb.c: 3\nb.e[0].name: Howdy Partner\n\n\n\n\nthen\n\n\nyq n -s create_instructions.yaml\n\n\n\n\nwill output:\n\n\nb:\n c: 3\n e:\n - name: Howdy Partner\n\n\n\n\nYou can also pipe the instructions in:\n\n\ncat create_instructions.yaml | yq n -s -\n\n\n\n\nKeys with dots\n\u00b6\n\n\nWhen specifying a key that has a dot use key lookup indicator.\n\n\nb:\n foo.bar: 7\n\n\n\n\nyaml r sample.yaml 'b[foo.bar]'\n\n\n\n\nyaml w sample.yaml 'b[foo.bar]' 9\n\n\n\n\nAny valid yaml key can be specified as part of a key lookup.\n\n\nNote that the path is in quotes to avoid the square brackets being interpreted by your shell.",
"title": "Create"
},
{
@@ -122,12 +172,12 @@
},
{
"location": "/convert/",
"text": "Yaml to Json\n\u00b6\n\n\nTo convert output to json, use the --tojson (or -j) flag. This can be used with any command.\n\n\nGiven a sample.yaml file of:\n\n\nb:\n c: 2\n\n\n\n\nthen\n\n\nyq r -j sample.yaml b.c\n\n\n\n\nwill output\n\n\n{\"b\":{\"c\":2}}\n\n\n\n\nJson to Yaml\n\u00b6\n\n\nTo read in json, just pass in a json file instead of yaml, it will just work :)\n\n\ne.g given a json file\n\n\n{\"a\":\"Easy! as one two three\",\"b\":{\"c\":2,\"d\":[3,4]}}\n\n\n\n\nthen\n\n\nyq r sample.json\n\n\n\n\nwill output\n\n\na: Easy! as one two three\nb:\n c: 2\n d:\n - 3\n - 4",
"text": "Yaml to Json\n\u00b6\n\n\nTo convert output to json, use the --tojson (or -j) flag. This can only be used with the read command.\n\n\nGiven a sample.yaml file of:\n\n\nb:\n c: 2\n\n\n\n\nthen\n\n\nyq r -j sample.yaml b.c\n\n\n\n\nwill output\n\n\n{\"b\":{\"c\":2}}\n\n\n\n\nJson to Yaml\n\u00b6\n\n\nTo read in json, just pass in a json file instead of yaml, it will just work :)\n\n\ne.g given a json file\n\n\n{\"a\":\"Easy! as one two three\",\"b\":{\"c\":2,\"d\":[3,4]}}\n\n\n\n\nthen\n\n\nyq r sample.json\n\n\n\n\nwill output\n\n\na: Easy! as one two three\nb:\n c: 2\n d:\n - 3\n - 4",
"title": "Convert"
},
{
"location": "/convert/#yaml-to-json",
"text": "To convert output to json, use the --tojson (or -j) flag. This can be used with any command. Given a sample.yaml file of: b:\n c: 2 then yq r -j sample.yaml b.c will output {\"b\":{\"c\":2}}",
"text": "To convert output to json, use the --tojson (or -j) flag. This can only be used with the read command. Given a sample.yaml file of: b:\n c: 2 then yq r -j sample.yaml b.c will output {\"b\":{\"c\":2}}",
"title": "Yaml to Json"
},
{
@@ -137,7 +187,7 @@
},
{
"location": "/merge/",
"text": "Yaml files can be merged using the 'merge' command. Each additional file merged with the first file will\nset values for any key not existing already or where the key has no value.\n\n\nyq m <yaml_file|json_file> <path>...\n\n\n\n\nThis command can take a json file as input too, and will output yaml unless specified to export as json (-j)\n\n\nTo Stdout\n\u00b6\n\n\nGiven a data1.yaml file of:\n\n\na: simple\nb: [1, 2]\n\n\n\n\nand data2.yaml file of:\n\n\na: other\nc:\n test: 1\n\n\n\n\nthen\n\n\nyq m data1.yaml data2.yaml\n\n\n\n\nwill output:\n\n\na: simple\nb: [1, 2]\nc:\n test: 1\n\n\n\n\nUpdating files in-place\n\u00b6\n\n\nGiven a data1.yaml file of:\n\n\na: simple\nb: [1, 2]\n\n\n\n\nand data2.yaml file of:\n\n\na: other\nc:\n test: 1\n\n\n\n\nthen\n\n\nyq m -i data1.yaml data2.yaml\n\n\n\n\nwill update the data1.yaml file so that the value of 'c' is 'test: 1'.\n\n\nOverwrite values\n\u00b6\n\n\nGiven a data1.yaml file of:\n\n\na: simple\nb: [1, 2]\n\n\n\n\nand data2.yaml file of:\n\n\na: other\nc:\n test: 1\n\n\n\n\nthen\n\n\nyq m -x data1.yaml data2.yaml\n\n\n\n\nwill output:\n\n\na: other\nb: [1, 2]\nc:\n test: 1\n\n\n\n\nOverwrite values with arrays\n\u00b6\n\n\nGiven a data1.yaml file of:\n\n\na: simple\nb: [1, 2]\n\n\n\n\nand data3.yaml file of:\n\n\nb: [2, 3, 4]\nc:\n test: 2\n other: true\nd: false\n\n\n\n\nthen\n\n\nyq m -x data1.yaml data3.yaml\n\n\n\n\nwill output:\n\n\na: simple\nb: [2, 3, 4]\nc:\n test: 2\n other: true\nd: false\n\n\n\n\nNotice that 'b' does not result in the merging of the values within an array. The underlying library does not\ncurrently handle merging values within an array.",
"text": "Yaml files can be merged using the 'merge' command. Each additional file merged with the first file will\nset values for any key not existing already or where the key has no value.\n\n\nyq m <yaml_file> <path>...\n\n\n\n\nTo Stdout\n\u00b6\n\n\nGiven a data1.yaml file of:\n\n\na: simple\nb: [1, 2]\n\n\n\n\nand data2.yaml file of:\n\n\na: other\nc:\n test: 1\n\n\n\n\nthen\n\n\nyq m data1.yaml data2.yaml\n\n\n\n\nwill output:\n\n\na: simple\nb: [1, 2]\nc:\n test: 1\n\n\n\n\nUpdating files in-place\n\u00b6\n\n\nGiven a data1.yaml file of:\n\n\na: simple\nb: [1, 2]\n\n\n\n\nand data2.yaml file of:\n\n\na: other\nc:\n test: 1\n\n\n\n\nthen\n\n\nyq m -i data1.yaml data2.yaml\n\n\n\n\nwill update the data1.yaml file so that the value of 'c' is 'test: 1'.\n\n\nOverwrite values\n\u00b6\n\n\nGiven a data1.yaml file of:\n\n\na: simple\nb: [1, 2]\n\n\n\n\nand data2.yaml file of:\n\n\na: other\nc:\n test: 1\n\n\n\n\nthen\n\n\nyq m -x data1.yaml data2.yaml\n\n\n\n\nwill output:\n\n\na: other\nb: [1, 2]\nc:\n test: 1\n\n\n\n\nOverwrite values with arrays\n\u00b6\n\n\nGiven a data1.yaml file of:\n\n\na: simple\nb: [1, 2]\n\n\n\n\nand data3.yaml file of:\n\n\nb: [2, 3, 4]\nc:\n test: 2\n other: true\nd: false\n\n\n\n\nthen\n\n\nyq m -x data1.yaml data3.yaml\n\n\n\n\nwill output:\n\n\na: simple\nb: [2, 3, 4]\nc:\n test: 2\n other: true\nd: false\n\n\n\n\nNotice that 'b' does not result in the merging of the values within an array. The underlying library does not\ncurrently handle merging values within an array.\n\n\nMultiple Documents - merge into single document\n\u00b6\n\n\nCurrently yq only has multi-document support for the \nfirst\n document being merged into. The remaining yaml files will have their first document selected.\n\n\nGiven a data1.yaml file of:\n\n\nsomething: else\n---\na: simple\nb: cat\n\n\n\n\nand data3.yaml file of:\n\n\nb: dog\n\n\n\n\nthen\n\n\nyq m -x -d1 data1.yaml data3.yaml\n\n\n\n\nwill output:\n\n\nsomething: else\n---\na: simple\nb: dog\n\n\n\n\nMultiple Documents - merge into all documents\n\u00b6\n\n\nCurrently yq only has multi-document support for the \nfirst\n document being merged into. The remaining yaml files will have their first document selected.\n\n\nGiven a data1.yaml file of:\n\n\nsomething: else\n---\na: simple\nb: cat\n\n\n\n\nand data3.yaml file of:\n\n\nb: dog\n\n\n\n\nthen\n\n\nyq m -x -d'*' data1.yaml data3.yaml\n\n\n\n\nwill output:\n\n\nb: dog\nsomething: else\n---\na: simple\nb: dog",
"title": "Merge"
},
{
@@ -159,6 +209,16 @@
"location": "/merge/#overwrite-values-with-arrays",
"text": "Given a data1.yaml file of: a: simple\nb: [1, 2] and data3.yaml file of: b: [2, 3, 4]\nc:\n test: 2\n other: true\nd: false then yq m -x data1.yaml data3.yaml will output: a: simple\nb: [2, 3, 4]\nc:\n test: 2\n other: true\nd: false Notice that 'b' does not result in the merging of the values within an array. The underlying library does not\ncurrently handle merging values within an array.",
"title": "Overwrite values with arrays"
},
{
"location": "/merge/#multiple-documents-merge-into-single-document",
"text": "Currently yq only has multi-document support for the first document being merged into. The remaining yaml files will have their first document selected. Given a data1.yaml file of: something: else\n---\na: simple\nb: cat and data3.yaml file of: b: dog then yq m -x -d1 data1.yaml data3.yaml will output: something: else\n---\na: simple\nb: dog",
"title": "Multiple Documents - merge into single document"
},
{
"location": "/merge/#multiple-documents-merge-into-all-documents",
"text": "Currently yq only has multi-document support for the first document being merged into. The remaining yaml files will have their first document selected. Given a data1.yaml file of: something: else\n---\na: simple\nb: cat and data3.yaml file of: b: dog then yq m -x -d'*' data1.yaml data3.yaml will output: b: dog\nsomething: else\n---\na: simple\nb: dog",
"title": "Multiple Documents - merge into all documents"
}
]
}

View File

@@ -4,7 +4,7 @@
<url>
<loc>/</loc>
<lastmod>2018-02-28</lastmod>
<lastmod>2018-06-20</lastmod>
<changefreq>daily</changefreq>
</url>
@@ -12,7 +12,7 @@
<url>
<loc>/read/</loc>
<lastmod>2018-02-28</lastmod>
<lastmod>2018-06-20</lastmod>
<changefreq>daily</changefreq>
</url>
@@ -20,7 +20,15 @@
<url>
<loc>/write/</loc>
<lastmod>2018-02-28</lastmod>
<lastmod>2018-06-20</lastmod>
<changefreq>daily</changefreq>
</url>
<url>
<loc>/delete/</loc>
<lastmod>2018-06-20</lastmod>
<changefreq>daily</changefreq>
</url>
@@ -28,7 +36,7 @@
<url>
<loc>/create/</loc>
<lastmod>2018-02-28</lastmod>
<lastmod>2018-06-20</lastmod>
<changefreq>daily</changefreq>
</url>
@@ -36,7 +44,7 @@
<url>
<loc>/convert/</loc>
<lastmod>2018-02-28</lastmod>
<lastmod>2018-06-20</lastmod>
<changefreq>daily</changefreq>
</url>
@@ -44,7 +52,7 @@
<url>
<loc>/merge/</loc>
<lastmod>2018-02-28</lastmod>
<lastmod>2018-06-20</lastmod>
<changefreq>daily</changefreq>
</url>

View File

@@ -275,6 +275,20 @@
Appending value to an array field
</a>
</li>
<li class="md-nav__item">
<a href="#multiple-documents-update-a-single-document" title="Multiple Documents - update a single document" class="md-nav__link">
Multiple Documents - update a single document
</a>
</li>
<li class="md-nav__item">
<a href="#multiple-documents-update-all-documents" title="Multiple Documents - update all documents" class="md-nav__link">
Multiple Documents - update all documents
</a>
</li>
<li class="md-nav__item">
@@ -319,6 +333,18 @@
<li class="md-nav__item">
<a href="../delete/" title="Delete" class="md-nav__link">
Delete
</a>
</li>
<li class="md-nav__item">
<a href="../create/" title="Create" class="md-nav__link">
Create
@@ -394,6 +420,20 @@
Appending value to an array field
</a>
</li>
<li class="md-nav__item">
<a href="#multiple-documents-update-a-single-document" title="Multiple Documents - update a single document" class="md-nav__link">
Multiple Documents - update a single document
</a>
</li>
<li class="md-nav__item">
<a href="#multiple-documents-update-all-documents" title="Multiple Documents - update all documents" class="md-nav__link">
Multiple Documents - update all documents
</a>
</li>
<li class="md-nav__item">
@@ -443,10 +483,9 @@
<h1>Write/Update</h1>
<pre><code>yq w &lt;yaml_file|json_file&gt; &lt;path&gt; &lt;new value&gt;
<pre><code>yq w &lt;yaml_file&gt; &lt;path&gt; &lt;new value&gt;
</code></pre>
<p>This command can take a json file as input too, and will output yaml unless specified to export as json (-j)</p>
<h3 id="to-stdout">To Stdout<a class="headerlink" href="#to-stdout" title="Permanent link">&para;</a></h3>
<p>Given a sample.yaml file of:</p>
<pre><code class="yaml">b:
@@ -507,6 +546,47 @@
</code></pre>
<p>Note that the path is in quotes to avoid the square brackets being interpreted by your shell.</p>
<h3 id="multiple-documents-update-a-single-document">Multiple Documents - update a single document<a class="headerlink" href="#multiple-documents-update-a-single-document" title="Permanent link">&para;</a></h3>
<p>Given a sample.yaml file of:</p>
<pre><code class="yaml">something: else
---
b:
c: 2
</code></pre>
<p>then</p>
<pre><code class="bash">yq w -d1 sample.yaml b.c 5
</code></pre>
<p>will output:</p>
<pre><code class="yaml">something: else
---
b:
c: 5
</code></pre>
<h3 id="multiple-documents-update-all-documents">Multiple Documents - update all documents<a class="headerlink" href="#multiple-documents-update-all-documents" title="Permanent link">&para;</a></h3>
<p>Given a sample.yaml file of:</p>
<pre><code class="yaml">something: else
---
b:
c: 2
</code></pre>
<p>then</p>
<pre><code class="bash">yq w -d'*' sample.yaml b.c 5
</code></pre>
<p>will output:</p>
<pre><code class="yaml">something: else
b:
c: 5
---
b:
c: 5
</code></pre>
<p>Note that '*' is in quotes to avoid being interpreted by your shell.</p>
<h3 id="updating-files-in-place">Updating files in-place<a class="headerlink" href="#updating-files-in-place" title="Permanent link">&para;</a></h3>
<p>Given a sample.yaml file of:</p>
<pre><code class="yaml">b:
@@ -603,13 +683,13 @@ b.e[0].name: Howdy Partner
</a>
<a href="../create/" title="Create" class="md-flex md-footer-nav__link md-footer-nav__link--next" rel="next">
<a href="../delete/" title="Delete" class="md-flex md-footer-nav__link md-footer-nav__link--next" rel="next">
<div class="md-flex__cell md-flex__cell--stretch md-footer-nav__title">
<span class="md-flex__ellipsis">
<span class="md-footer-nav__direction">
Next
</span>
Create
Delete
</span>
</div>
<div class="md-flex__cell md-flex__cell--shrink">

View File

@@ -1,5 +1 @@
b: [2, 3, 4]
c:
test: 2
other: true
d: false
b: dog

View File

@@ -0,0 +1,22 @@
commonKey: first document
a: Easy! as one two three
b:
c: 2
d: [3, 4]
e:
- name: fred
value: 3
- name: sam
value: 4
---
commonKey: second document
another:
document: here
---
commonKey: third document
wow:
- here is another
---
- 1
- 2
- 3

View File

@@ -0,0 +1,7 @@
a: Easy! as one two three
---
another:
document: here
---
- 1
- 2

View File

@@ -6,4 +6,4 @@ b:
- name: fred
value: 3
- name: sam
value: 4
value: 4

View File

@@ -5,7 +5,7 @@ import (
"fmt"
"strconv"
yaml "gopkg.in/yaml.v2"
yaml "gopkg.in/mikefarah/yaml.v2"
)
func jsonToString(context interface{}) (string, error) {

View File

@@ -1,8 +1,6 @@
package main
import (
"github.com/imdario/mergo"
)
import "gopkg.in/imdario/mergo.v0"
func merge(dst, src interface{}, overwrite bool) error {
if overwrite {

View File

@@ -1,30 +0,0 @@
package main
import (
"testing"
yaml "gopkg.in/yaml.v2"
)
func TestMerge(t *testing.T) {
result, _ := mergeYaml([]string{"examples/data1.yaml", "examples/data2.yaml", "examples/data3.yaml"})
expected := yaml.MapSlice{
yaml.MapItem{Key: "a", Value: "simple"},
yaml.MapItem{Key: "b", Value: []interface{}{1, 2}},
yaml.MapItem{Key: "c", Value: yaml.MapSlice{yaml.MapItem{Key: "other", Value: true}, yaml.MapItem{Key: "test", Value: 1}}},
yaml.MapItem{Key: "d", Value: false},
}
assertResultComplex(t, expected, result)
}
func TestMergeWithOverwrite(t *testing.T) {
overwriteFlag = true
result, _ := mergeYaml([]string{"examples/data1.yaml", "examples/data2.yaml", "examples/data3.yaml"})
expected := yaml.MapSlice{
yaml.MapItem{Key: "a", Value: "other"},
yaml.MapItem{Key: "b", Value: []interface{}{2, 3, 4}},
yaml.MapItem{Key: "c", Value: yaml.MapSlice{yaml.MapItem{Key: "other", Value: true}, yaml.MapItem{Key: "test", Value: 2}}},
yaml.MapItem{Key: "d", Value: false},
}
assertResultComplex(t, expected, result)
}

View File

@@ -6,6 +6,7 @@ pages:
- Install: index.md
- Read: read.md
- Write/Update: write.md
- Delete: delete.md
- Create: create.md
- Convert: convert.md
- Merge: merge.md

View File

@@ -1,5 +1,5 @@
### Yaml to Json
To convert output to json, use the --tojson (or -j) flag. This can be used with any command.
To convert output to json, use the --tojson (or -j) flag. This can only be used with the read command.
Given a sample.yaml file of:
```yaml

View File

@@ -1,4 +1,4 @@
Yaml files can be created using the 'new' command. This works in the same way as the write command, but you don't pass in an existing Yaml file.
Yaml files can be created using the 'new' command. This works in the same way as the write command, but you don't pass in an existing Yaml file. Currently this does not support creating multiple documents in a single yaml file.
```
yq n <path> <new value>

110
mkdocs/delete.md Normal file
View File

@@ -0,0 +1,110 @@
```
yq d <yaml_file> <path_to_delete>
```
### To Stdout
Given a sample.yaml file of:
```yaml
b:
c: 2
apples: green
```
then
```bash
yq d sample.yaml b.c
```
will output:
```yaml
b:
apples: green
```
### From STDIN
```bash
cat sample.yaml | yq d - b.c
```
### Deleting array elements
Given a sample.yaml file of:
```yaml
b:
c:
- 1
- 2
- 3
```
then
```bash
yq d sample.yaml 'b.c[1]'
```
will output:
```yaml
b:
c:
- 1
- 3
```
### Deleting nodes in-place
Given a sample.yaml file of:
```yaml
b:
c: 2
apples: green
```
then
```bash
yq d -i sample.yaml b.c
```
will update the sample.yaml file so that the 'c' node is deleted
### Multiple Documents - delete from single document
Given a sample.yaml file of:
```yaml
something: else
field: leaveMe
---
b:
c: 2
field: deleteMe
```
then
```bash
yq w -d1 sample.yaml field
```
will output:
```yaml
something: else
field: leaveMe
---
b:
c: 2
```
### Multiple Documents - delete from all documents
Given a sample.yaml file of:
```yaml
something: else
field: deleteMe
---
b:
c: 2
field: deleteMeToo
```
then
```bash
yq w -d'*' sample.yaml field
```
will output:
```yaml
something: else
---
b:
c: 2
```
Note that '*' is in quotes to avoid being interpreted by your shell.
{!snippets/keys_with_dots.md!}

View File

@@ -12,9 +12,15 @@ On Ubuntu and other Linux distros supporting `snap` packages:
```
snap install yq
```
On Ubuntu 16.04 or higher from Debian package:
```
sudo add-apt-repository ppa:rmescandon/yq
sudo apt update
sudo apt install yq -y
```
or, [Download latest binary](https://github.com/mikefarah/yq/releases/latest) or alternatively:
```
go get github.com/mikefarah/yq
go get gopkg.in/mikefarah/yq.v2
```
[View on GitHub](https://github.com/mikefarah/yq)

View File

@@ -2,9 +2,9 @@ Yaml files can be merged using the 'merge' command. Each additional file merged
set values for any key not existing already or where the key has no value.
```
yq m <yaml_file|json_file> <path>...
yq m <yaml_file> <path>...
```
{!snippets/works_with_json.md!}
### To Stdout
Given a data1.yaml file of:
@@ -102,3 +102,56 @@ d: false
Notice that 'b' does not result in the merging of the values within an array. The underlying library does not
currently handle merging values within an array.
### Multiple Documents - merge into single document
Currently yq only has multi-document support for the _first_ document being merged into. The remaining yaml files will have their first document selected.
Given a data1.yaml file of:
```yaml
something: else
---
a: simple
b: cat
```
and data3.yaml file of:
```yaml
b: dog
```
then
```bash
yq m -x -d1 data1.yaml data3.yaml
```
will output:
```yaml
something: else
---
a: simple
b: dog
```
### Multiple Documents - merge into all documents
Currently yq only has multi-document support for the _first_ document being merged into. The remaining yaml files will have their first document selected.
Given a data1.yaml file of:
```yaml
something: else
---
a: simple
b: cat
```
and data3.yaml file of:
```yaml
b: dog
```
then
```bash
yq m -x -d'*' data1.yaml data3.yaml
```
will output:
```yaml
b: dog
something: else
---
a: simple
b: dog
```

View File

@@ -43,15 +43,17 @@ will output
- apples
```
### Handling '.' in the yaml key
### Multiple Documents
Given a sample.yaml file of:
```yaml
b.x:
something: else
---
b:
c: 2
```
then
```bash
yq r sample.yaml \"b.x\".c
yq r -d1 sample.yaml b.c
```
will output the value of '2'.

View File

@@ -1,7 +1,6 @@
```
yq w <yaml_file|json_file> <path> <new value>
yq w <yaml_file> <path> <new value>
```
{!snippets/works_with_json.md!}
### To Stdout
Given a sample.yaml file of:
@@ -69,6 +68,50 @@ b:
Note that the path is in quotes to avoid the square brackets being interpreted by your shell.
### Multiple Documents - update a single document
Given a sample.yaml file of:
```yaml
something: else
---
b:
c: 2
```
then
```bash
yq w -d1 sample.yaml b.c 5
```
will output:
```yaml
something: else
---
b:
c: 5
```
### Multiple Documents - update all documents
Given a sample.yaml file of:
```yaml
something: else
---
b:
c: 2
```
then
```bash
yq w -d'*' sample.yaml b.c 5
```
will output:
```yaml
something: else
b:
c: 5
---
b:
c: 5
```
Note that '*' is in quotes to avoid being interpreted by your shell.
### Updating files in-place
Given a sample.yaml file of:
```yaml

37
release_instructions.txt Normal file
View File

@@ -0,0 +1,37 @@
- increment version in version.go
- increment version in snapcraft.yaml
- tag git with same version number
- make sure local build passes
- push tag to git
- make local xcompile (builds binaries for all platforms)
- git release
./scripts/publish.sh
- snapcraft
- will auto create a candidate, test it works then promote
- see https://build.snapcraft.io/user/mikefarah/yq
sudo snap remove yq
sudo snap install --edge yq
then on the mac snapcraft release yq <snap_build_number> stable
- brew
- create pull request pointing to latest git release
- docker
- build and push latest and new version tag
- debian package
- execute
```dch -i```
- fill debian/changelog with changes from last version
- build the package sources
```debuild -i -I -S -sa```
(signing with gpg key is required in order to put it to ppa)
- put to PPA
```dput ppa:<REPOSITORY> ../yq_<VERSION>_source.changes```
(current distro repository is ppa:rmescandon/yq. In case that a new version
is released, please contact rmescandon@gmail.com to bump debian package)

View File

@@ -1,39 +1,32 @@
#!/bin/bash
set -ex
GITHUB_TOKEN="${GITHUB_TOKEN:?missing required input \'GITHUB_TOKEN\'}"
CURRENT="$(git describe --tags --abbrev=0)"
PREVIOUS="$(git describe --tags --abbrev=0 --always "${CURRENT}"^)"
OWNER="mikefarah"
REPO="yaml"
REPO="yq"
release() {
mapfile -t logs < <(git log --pretty=oneline --abbrev-commit "${PREVIOUS}".."${CURRENT}")
description="$(printf '%s\n' "${logs[@]}")"
github-release release \
--user "$OWNER" \
--repo "$REPO" \
--tag "$CURRENT" \
--description "$description" ||
github-release edit \
--user "$OWNER" \
--repo "$REPO" \
--tag "$CURRENT" \
--description "$description"
--tag "$CURRENT"
}
upload() {
mapfile -t files < <(find ./build -mindepth 1 -maxdepth 1)
for file in "${files[@]}"; do
while IFS= read -r -d $'\0'; do
file=$REPLY
BINARY=$(basename "${file}")
echo "--> ${BINARY}"
github-release upload \
--replace \
--user "$OWNER" \
--repo "$REPO" \
--tag "$CURRENT" \
--name "${BINARY}" \
--file "$file"
done
done < <(find ./build -mindepth 1 -maxdepth 1 -print0)
}
release

View File

@@ -1,5 +1,5 @@
name: yq
version: 1.14.1
version: 2.0.0
summary: A lightweight and portable command-line YAML processor
description: |
The aim of the project is to be the jq or sed of yaml files.

View File

@@ -9,8 +9,8 @@ import (
"strings"
"testing"
"github.com/spf13/cobra"
yaml "gopkg.in/yaml.v2"
yaml "gopkg.in/mikefarah/yaml.v2"
"gopkg.in/spf13/cobra.v0"
)
type resulter struct {
@@ -41,19 +41,21 @@ func parseData(rawData string) yaml.MapSlice {
}
func assertResult(t *testing.T, expectedValue interface{}, actualValue interface{}) {
t.Helper()
if expectedValue != actualValue {
t.Error("Expected <", expectedValue, "> but got <", actualValue, ">", fmt.Sprintf("%T", actualValue))
}
}
func assertResultComplex(t *testing.T, expectedValue interface{}, actualValue interface{}) {
t.Helper()
if !reflect.DeepEqual(expectedValue, actualValue) {
t.Error("Expected <", expectedValue, "> but got <", actualValue, ">", fmt.Sprintf("%T", actualValue))
}
}
func assertResultWithContext(t *testing.T, expectedValue interface{}, actualValue interface{}, context interface{}) {
t.Helper()
if expectedValue != actualValue {
t.Error(context)
t.Error(": expected <", expectedValue, "> but got <", actualValue, ">")

52
vendor/vendor.json vendored
View File

@@ -2,12 +2,6 @@
"comment": "",
"ignore": "test",
"package": [
{
"checksumSHA1": "66lykxpWgSmQodnhkADqn6tnroQ=",
"path": "github.com/imdario/mergo",
"revision": "e3000cb3d28c72b837601cac94debd91032d19fe",
"revisionTime": "2017-06-20T10:47:01Z"
},
{
"checksumSHA1": "40vJyUB4ezQSn/NSadsKEOrudMc=",
"path": "github.com/inconshreveable/mousetrap",
@@ -15,28 +9,40 @@
"revisionTime": "2014-10-17T20:07:13Z"
},
{
"checksumSHA1": "BoXdUBWB8UnSlFlbnuTQaPqfCGk=",
"path": "github.com/op/go-logging",
"revision": "970db520ece77730c7e4724c61121037378659d9",
"revisionTime": "2016-03-15T20:05:05Z"
"checksumSHA1": "ljd3FhYRJ91cLZz3wsH9BQQ2JbA=",
"path": "github.com/pkg/errors",
"revision": "816c9085562cd7ee03e7f8188a1cfd942858cded",
"revisionTime": "2018-03-11T21:45:15Z"
},
{
"checksumSHA1": "xPKgXygsORkmXnLdtFaFmipYKaA=",
"path": "github.com/spf13/cobra",
"revision": "b78744579491c1ceeaaa3b40205e56b0591b93a3",
"revisionTime": "2017-09-05T17:20:51Z"
},
{
"checksumSHA1": "Q52Y7t0lEtk/wcDn5q7tS7B+jqs=",
"checksumSHA1": "OJI0OgC5V8gZtfS1e0CDYMhkDNc=",
"path": "github.com/spf13/pflag",
"revision": "7aff26db30c1be810f9de5038ec5ef96ac41fd7c",
"revisionTime": "2017-08-24T17:57:12Z"
"revision": "3ebe029320b2676d667ae88da602a5f854788a8a",
"revisionTime": "2018-06-01T13:25:42Z"
},
{
"checksumSHA1": "RDJpJQwkF012L6m/2BJizyOksNw=",
"path": "gopkg.in/yaml.v2",
"revision": "eb3733d160e74a9c7e442f435eb3bea458e1d19f",
"revisionTime": "2017-08-12T16:00:11Z"
"checksumSHA1": "RwlkCZz8VFXAE4aHQQOSC0hLu5k=",
"path": "gopkg.in/imdario/mergo.v0",
"revision": "9316a62528ac99aaecb4e47eadd6dc8aa6533d58",
"revisionTime": "2018-06-08T14:01:56Z"
},
{
"checksumSHA1": "7wtGubs4v7+RZovtlmyT9KwA/gE=",
"path": "gopkg.in/mikefarah/yaml.v2",
"revision": "e175af14aaa1d0eff2ee04b691e4a4827a111416",
"revisionTime": "2018-06-13T04:05:11Z"
},
{
"checksumSHA1": "rL5r44ASTGubGW88gqQwlvVQshw=",
"path": "gopkg.in/op/go-logging.v1",
"revision": "b2cb9fa56473e98db8caba80237377e83fe44db5",
"revisionTime": "2016-02-11T21:21:56Z"
},
{
"checksumSHA1": "xsZjAbfLrXcMtY6fyQ8QC6EvJD0=",
"path": "gopkg.in/spf13/cobra.v0",
"revision": "ef82de70bb3f60c65fb8eebacbb2d122ef517385",
"revisionTime": "2018-04-27T13:45:50Z"
}
],
"rootPath": "github.com/mikefarah/yq"

View File

@@ -11,7 +11,7 @@ var (
GitDescribe string
// Version is main version number that is being run at the moment.
Version = "1.14.1"
Version = "2.0.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
@@ -20,7 +20,7 @@ var (
)
// ProductName is the name of the product
const ProductName = "yaml"
const ProductName = "yq"
// GetVersionDisplay composes the parts of the version in a way that's suitable
// for displaying to humans.

View File

@@ -3,13 +3,18 @@ package main
import "testing"
func TestGetVersionDisplay(t *testing.T) {
var expectedVersion = ProductName + " version " + Version
if VersionPrerelease != "" {
expectedVersion = expectedVersion + "-" + VersionPrerelease
}
expectedVersion = expectedVersion + "\n"
tests := []struct {
name string
want string
}{
{
name: "Display Version",
want: ProductName + " version " + Version + "\n",
want: expectedVersion,
},
}
for _, tt := range tests {

458
yq.go
View File

@@ -1,16 +1,20 @@
package main
import (
"errors"
"bufio"
"fmt"
"io"
"io/ioutil"
"os"
"reflect"
"strconv"
"strings"
logging "github.com/op/go-logging"
"github.com/spf13/cobra"
yaml "gopkg.in/yaml.v2"
errors "github.com/pkg/errors"
"gopkg.in/spf13/cobra.v0"
yaml "gopkg.in/mikefarah/yaml.v2"
logging "gopkg.in/op/go-logging.v1"
)
var trimOutput = true
@@ -20,17 +24,19 @@ var outputToJSON = false
var overwriteFlag = false
var verbose = false
var version = false
var docIndex = "0"
var log = logging.MustGetLogger("yq")
func main() {
cmd := newCommandCLI()
if err := cmd.Execute(); err != nil {
fmt.Println(err.Error())
log.Error(err.Error())
os.Exit(1)
}
}
func newCommandCLI() *cobra.Command {
yaml.DefaultMapType = reflect.TypeOf(yaml.MapSlice{})
var rootCmd = &cobra.Command{
Use: "yq",
RunE: func(cmd *cobra.Command, args []string) error {
@@ -60,46 +66,54 @@ func newCommandCLI() *cobra.Command {
}
rootCmd.PersistentFlags().BoolVarP(&trimOutput, "trim", "t", true, "trim yaml output")
rootCmd.PersistentFlags().BoolVarP(&outputToJSON, "tojson", "j", false, "output as json")
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "verbose mode")
rootCmd.Flags().BoolVarP(&version, "version", "V", false, "Print version information and quit")
rootCmd.AddCommand(createReadCmd(), createWriteCmd(), createNewCmd(), createMergeCmd())
rootCmd.AddCommand(
createReadCmd(),
createWriteCmd(),
createDeleteCmd(),
createNewCmd(),
createMergeCmd(),
)
rootCmd.SetOutput(os.Stdout)
return rootCmd
}
func createReadCmd() *cobra.Command {
return &cobra.Command{
var cmdRead = &cobra.Command{
Use: "read [yaml_file] [path]",
Aliases: []string{"r"},
Short: "yq r sample.yaml a.b.c",
Short: "yq r [--doc/-d index] sample.yaml a.b.c",
Example: `
yq read things.yaml a.b.c
yq r - a.b.c (reads from stdin)
yq r things.yaml a.*.c
yq r things.yaml a.array[0].blah
yq r -d1 things.yaml a.array[0].blah
yq r things.yaml a.array[*].blah
`,
Long: "Outputs the value of the given path in the yaml file to STDOUT",
RunE: readProperty,
}
cmdRead.PersistentFlags().StringVarP(&docIndex, "doc", "d", "0", "process document index number, 0 based")
cmdRead.PersistentFlags().BoolVarP(&outputToJSON, "tojson", "j", false, "output as json")
return cmdRead
}
func createWriteCmd() *cobra.Command {
var cmdWrite = &cobra.Command{
Use: "write [yaml_file] [path] [value]",
Aliases: []string{"w"},
Short: "yq w [--inplace/-i] [--script/-s script_file] sample.yaml a.b.c newValueForC",
Short: "yq w [--inplace/-i] [--script/-s script_file] [--doc/-d index] sample.yaml a.b.c newValue",
Example: `
yq write things.yaml a.b.c cat
yq write --inplace things.yaml a.b.c cat
yq w -i things.yaml a.b.c cat
yq w --script update_script.yaml things.yaml
yq w -i -s update_script.yaml things.yaml
yq w things.yaml a.b.d[+] foo
yq w things.yaml a.b.d[+] foo
yq w --doc 2 things.yaml a.b.d[+] foo
yq w -d2 things.yaml a.b.d[+] foo
`,
Long: `Updates the yaml file w.r.t the given path and value.
Outputs to STDOUT unless the inplace flag is used, in which case the file is updated instead.
@@ -118,14 +132,36 @@ a.b.e:
}
cmdWrite.PersistentFlags().BoolVarP(&writeInplace, "inplace", "i", false, "update the yaml file inplace")
cmdWrite.PersistentFlags().StringVarP(&writeScript, "script", "s", "", "yaml script for updating yaml")
cmdWrite.PersistentFlags().StringVarP(&docIndex, "doc", "d", "0", "process document index number (0 based, * for all documents)")
return cmdWrite
}
func createDeleteCmd() *cobra.Command {
var cmdDelete = &cobra.Command{
Use: "delete [yaml_file] [path]",
Aliases: []string{"d"},
Short: "yq d [--inplace/-i] [--doc/-d index] sample.yaml a.b.c",
Example: `
yq delete things.yaml a.b.c
yq delete --inplace things.yaml a.b.c
yq d -i things.yaml a.b.c
yq d things.yaml a.b.c
`,
Long: `Deletes the given path from the YAML file.
Outputs to STDOUT unless the inplace flag is used, in which case the file is updated instead.
`,
RunE: deleteProperty,
}
cmdDelete.PersistentFlags().BoolVarP(&writeInplace, "inplace", "i", false, "update the yaml file inplace")
cmdDelete.PersistentFlags().StringVarP(&docIndex, "doc", "d", "0", "process document index number (0 based, * for all documents)")
return cmdDelete
}
func createNewCmd() *cobra.Command {
var cmdNew = &cobra.Command{
Use: "new [path] [value]",
Aliases: []string{"n"},
Short: "yq n [--script/-s script_file] a.b.c newValueForC",
Short: "yq n [--script/-s script_file] a.b.c newValue",
Example: `
yq new a.b.c cat
yq n a.b.c cat
@@ -147,7 +183,7 @@ func createMergeCmd() *cobra.Command {
var cmdMerge = &cobra.Command{
Use: "merge [initial_yaml_file] [additional_yaml_file]...",
Aliases: []string{"m"},
Short: "yq m [--inplace/-i] [--overwrite/-x] sample.yaml sample2.yaml",
Short: "yq m [--inplace/-i] [--doc/-d index] [--overwrite/-x] sample.yaml sample2.yaml",
Example: `
yq merge things.yaml other.yaml
yq merge --inplace things.yaml other.yaml
@@ -164,6 +200,7 @@ If overwrite flag is set then existing values will be overwritten using the valu
}
cmdMerge.PersistentFlags().BoolVarP(&writeInplace, "inplace", "i", false, "update the yaml file inplace")
cmdMerge.PersistentFlags().BoolVarP(&overwriteFlag, "overwrite", "x", false, "update the yaml file by overwriting existing values")
cmdMerge.PersistentFlags().StringVarP(&docIndex, "doc", "d", "0", "process document index number (0 based, * for all documents)")
return cmdMerge
}
@@ -181,7 +218,6 @@ func readProperty(cmd *cobra.Command, args []string) error {
}
func read(args []string) (interface{}, error) {
var parsedData yaml.MapSlice
var path = ""
if len(args) < 1 {
@@ -189,67 +225,20 @@ func read(args []string) (interface{}, error) {
} else if len(args) > 1 {
path = args[1]
}
if err := readData(args[0], &parsedData); err != nil {
var generalData interface{}
if err = readData(args[0], &generalData); err != nil {
return nil, err
}
item := yaml.MapItem{Key: "thing", Value: generalData}
parsedData = yaml.MapSlice{item}
path = "thing." + path
var generalData interface{}
var docIndexInt, errorParsingDocumentIndex = strconv.ParseInt(docIndex, 10, 32)
if errorParsingDocumentIndex != nil {
return nil, errors.Wrapf(errorParsingDocumentIndex, "Document index %v is not a integer", docIndex)
}
if parsedData != nil && parsedData[0].Key == nil {
var interfaceData []map[interface{}]interface{}
if err := readData(args[0], &interfaceData); err == nil {
var listMap []yaml.MapSlice
for _, item := range interfaceData {
listMap = append(listMap, mapToMapSlice(item))
}
return readYamlArray(listMap, path)
}
if err := readData(args[0], int(docIndexInt), &generalData); err != nil {
return nil, err
}
if path == "" {
return parsedData, nil
return generalData, nil
}
var paths = parsePath(path)
return readMap(parsedData, paths[0], paths[1:])
}
func readYamlArray(listMap []yaml.MapSlice, path string) (interface{}, error) {
if path == "" {
return listMap, nil
}
var paths = parsePath(path)
if paths[0] == "*" {
if len(paths[1:]) == 0 {
return listMap, nil
}
var results []interface{}
for _, m := range listMap {
value, err := readMap(m, paths[1], paths[2:])
if err != nil {
return nil, err
}
results = append(results, value)
}
return results, nil
}
index, err := strconv.ParseInt(paths[0], 10, 64)
if err != nil {
return nil, fmt.Errorf("Error accessing array: %v", err)
}
if len(paths[1:]) == 0 {
return listMap[index], nil
}
return readMap(listMap[index], paths[1], paths[2:])
return recurse(generalData, paths[0], paths[1:])
}
func newProperty(cmd *cobra.Command, args []string) error {
@@ -266,127 +255,205 @@ func newProperty(cmd *cobra.Command, args []string) error {
}
func newYaml(args []string) (interface{}, error) {
var writeCommands yaml.MapSlice
if writeScript != "" {
if err := readData(writeScript, &writeCommands); err != nil {
return nil, err
}
} else if len(args) < 2 {
return nil, errors.New("Must provide <path_to_update> <value>")
} else {
writeCommands = make(yaml.MapSlice, 1)
writeCommands[0] = yaml.MapItem{Key: args[0], Value: parseValue(args[1])}
var writeCommands, writeCommandsError = readWriteCommands(args, 2, "Must provide <path_to_update> <value>")
if writeCommandsError != nil {
return nil, writeCommandsError
}
var parsedData yaml.MapSlice
var prependCommand = ""
var dataBucket interface{}
var isArray = strings.HasPrefix(writeCommands[0].Key.(string), "[")
if isArray {
item := yaml.MapItem{Key: "thing", Value: make(yaml.MapSlice, 0)}
parsedData = yaml.MapSlice{item}
prependCommand = "thing"
dataBucket = make([]interface{}, 0)
} else {
parsedData = make(yaml.MapSlice, 0)
dataBucket = make(yaml.MapSlice, 0)
}
return updateParsedData(parsedData, writeCommands, prependCommand)
for _, entry := range writeCommands {
path := entry.Key.(string)
value := entry.Value
log.Debugf("setting %v to %v", path, value)
var paths = parsePath(path)
dataBucket = updatedChildValue(dataBucket, paths, value)
}
return dataBucket, nil
}
func parseDocumentIndex() (bool, int, error) {
if docIndex == "*" {
return true, -1, nil
}
docIndexInt64, err := strconv.ParseInt(docIndex, 10, 32)
if err != nil {
return false, -1, errors.Wrapf(err, "Document index %v is not a integer or *", docIndex)
}
return false, int(docIndexInt64), nil
}
type updateDataFn func(dataBucket interface{}, currentIndex int) (interface{}, error)
func mapYamlDecoder(updateData updateDataFn, encoder *yaml.Encoder) yamlDecoderFn {
return func(decoder *yaml.Decoder) error {
var dataBucket interface{}
var errorReading error
var errorWriting error
var errorUpdating error
var currentIndex = 0
var updateAll, docIndexInt, errorParsingDocIndex = parseDocumentIndex()
if errorParsingDocIndex != nil {
return errorParsingDocIndex
}
for {
log.Debugf("Read doc %v", currentIndex)
errorReading = decoder.Decode(&dataBucket)
if errorReading == io.EOF {
if !updateAll && currentIndex < docIndexInt {
return fmt.Errorf("Asked to process document %v but there are only %v document(s)", docIndex, currentIndex)
}
return nil
} else if errorReading != nil {
return errors.Wrapf(errorReading, "Error reading document at index %v, %v", currentIndex, errorReading)
}
dataBucket, errorUpdating = updateData(dataBucket, currentIndex)
if errorUpdating != nil {
return errors.Wrapf(errorUpdating, "Error updating document at index %v", currentIndex)
}
errorWriting = encoder.Encode(dataBucket)
if errorWriting != nil {
return errors.Wrapf(errorWriting, "Error writing document at index %v, %v", currentIndex, errorWriting)
}
currentIndex = currentIndex + 1
}
}
}
func writeProperty(cmd *cobra.Command, args []string) error {
updatedData, err := updateYaml(args)
if err != nil {
return err
var writeCommands, writeCommandsError = readWriteCommands(args, 3, "Must provide <filename> <path_to_update> <value>")
if writeCommandsError != nil {
return writeCommandsError
}
return write(cmd, args[0], updatedData)
var updateAll, docIndexInt, errorParsingDocIndex = parseDocumentIndex()
if errorParsingDocIndex != nil {
return errorParsingDocIndex
}
var updateData = func(dataBucket interface{}, currentIndex int) (interface{}, error) {
if updateAll || currentIndex == docIndexInt {
log.Debugf("Updating doc %v", currentIndex)
for _, entry := range writeCommands {
path := entry.Key.(string)
value := entry.Value
log.Debugf("setting %v to %v", path, value)
var paths = parsePath(path)
dataBucket = updatedChildValue(dataBucket, paths, value)
}
}
return dataBucket, nil
}
return readAndUpdate(cmd.OutOrStdout(), args[0], updateData)
}
func write(cmd *cobra.Command, filename string, updatedData interface{}) error {
func readAndUpdate(stdOut io.Writer, inputFile string, updateData updateDataFn) error {
var destination io.Writer
var destinationName string
if writeInplace {
dataStr, err := yamlToString(updatedData)
var tempFile, err = ioutil.TempFile("", "temp")
if err != nil {
return err
}
return ioutil.WriteFile(filename, []byte(dataStr), 0644)
destinationName = tempFile.Name()
destination = tempFile
defer func() {
safelyCloseFile(tempFile)
safelyRenameFile(tempFile.Name(), inputFile)
}()
} else {
var writer = bufio.NewWriter(stdOut)
destination = writer
destinationName = "Stdout"
defer safelyFlush(writer)
}
dataStr, err := toString(updatedData)
if err != nil {
return err
var encoder = yaml.NewEncoder(destination)
log.Debugf("Writing to %v from %v", destinationName, inputFile)
return readStream(inputFile, mapYamlDecoder(updateData, encoder))
}
func deleteProperty(cmd *cobra.Command, args []string) error {
if len(args) < 2 {
return errors.New("Must provide <filename> <path_to_delete>")
}
cmd.Println(dataStr)
return nil
var deletePath = args[1]
var paths = parsePath(deletePath)
var updateAll, docIndexInt, errorParsingDocIndex = parseDocumentIndex()
if errorParsingDocIndex != nil {
return errorParsingDocIndex
}
var updateData = func(dataBucket interface{}, currentIndex int) (interface{}, error) {
if updateAll || currentIndex == docIndexInt {
log.Debugf("Deleting path in doc %v", currentIndex)
return deleteChildValue(dataBucket, paths), nil
}
return dataBucket, nil
}
return readAndUpdate(cmd.OutOrStdout(), args[0], updateData)
}
func mergeProperties(cmd *cobra.Command, args []string) error {
if len(args) < 2 {
return errors.New("Must provide at least 2 yaml files")
}
updatedData, err := mergeYaml(args)
if err != nil {
return err
var input = args[0]
var filesToMerge = args[1:]
var updateAll, docIndexInt, errorParsingDocIndex = parseDocumentIndex()
if errorParsingDocIndex != nil {
return errorParsingDocIndex
}
return write(cmd, args[0], updatedData)
}
func mergeYaml(args []string) (interface{}, error) {
var updatedData map[interface{}]interface{}
for _, f := range args {
var parsedData map[interface{}]interface{}
if err := readData(f, &parsedData); err != nil {
return nil, err
}
if err := merge(&updatedData, parsedData, overwriteFlag); err != nil {
return nil, err
var updateData = func(dataBucket interface{}, currentIndex int) (interface{}, error) {
if updateAll || currentIndex == docIndexInt {
log.Debugf("Merging doc %v", currentIndex)
var mergedData map[interface{}]interface{}
if err := merge(&mergedData, dataBucket, overwriteFlag); err != nil {
return nil, err
}
for _, f := range filesToMerge {
var fileToMerge interface{}
if err := readData(f, 0, &fileToMerge); err != nil {
return nil, err
}
if err := merge(&mergedData, fileToMerge, overwriteFlag); err != nil {
return nil, err
}
}
return mergedData, nil
}
return dataBucket, nil
}
return mapToMapSlice(updatedData), nil
yaml.DefaultMapType = reflect.TypeOf(map[interface{}]interface{}{})
defer func() { yaml.DefaultMapType = reflect.TypeOf(yaml.MapSlice{}) }()
return readAndUpdate(cmd.OutOrStdout(), input, updateData)
}
func updateParsedData(parsedData yaml.MapSlice, writeCommands yaml.MapSlice, prependCommand string) (interface{}, error) {
var prefix = ""
if prependCommand != "" {
prefix = prependCommand + "."
}
for _, entry := range writeCommands {
path := prefix + entry.Key.(string)
value := entry.Value
var paths = parsePath(path)
parsedData = writeMap(parsedData, paths, value)
}
if prependCommand != "" {
return readMap(parsedData, prependCommand, make([]string, 0))
}
return parsedData, nil
}
func updateYaml(args []string) (interface{}, error) {
func readWriteCommands(args []string, expectedArgs int, badArgsMessage string) (yaml.MapSlice, error) {
var writeCommands yaml.MapSlice
var prependCommand = ""
if writeScript != "" {
if err := readData(writeScript, &writeCommands); err != nil {
if err := readData(writeScript, 0, &writeCommands); err != nil {
return nil, err
}
} else if len(args) < 3 {
return nil, errors.New("Must provide <filename> <path_to_update> <value>")
} else if len(args) < expectedArgs {
return nil, errors.New(badArgsMessage)
} else {
writeCommands = make(yaml.MapSlice, 1)
writeCommands[0] = yaml.MapItem{Key: args[1], Value: parseValue(args[2])}
writeCommands[0] = yaml.MapItem{Key: args[expectedArgs-2], Value: parseValue(args[expectedArgs-1])}
}
var parsedData yaml.MapSlice
if err := readData(args[0], &parsedData); err != nil {
var generalData interface{}
if err = readData(args[0], &generalData); err != nil {
return nil, err
}
item := yaml.MapItem{Key: "thing", Value: generalData}
parsedData = yaml.MapSlice{item}
prependCommand = "thing"
}
return updateParsedData(parsedData, writeCommands, prependCommand)
return writeCommands, nil
}
func parseValue(argument string) interface{} {
@@ -429,7 +496,7 @@ func marshalContext(context interface{}) (string, error) {
out, err := yaml.Marshal(context)
if err != nil {
return "", fmt.Errorf("error printing yaml: %v", err)
return "", errors.Wrap(err, "error printing yaml")
}
outStr := string(out)
@@ -441,22 +508,81 @@ func marshalContext(context interface{}) (string, error) {
return outStr, nil
}
func readData(filename string, parsedData interface{}) error {
func safelyRenameFile(from string, to string) {
if renameError := os.Rename(from, to); renameError != nil {
log.Warningf("Error renaming from %v to %v, attemting to copy contents", from, to)
log.Warning(renameError.Error())
// can't do this rename when running in docker to a file targeted in a mounted volume,
// so gracefully degrade to copying the entire contents.
if copyError := copyFileContents(from, to); copyError != nil {
log.Errorf("Failed copying from %v to %v", from, to)
log.Errorf(copyError.Error())
}
}
}
// thanks https://stackoverflow.com/questions/21060945/simple-way-to-copy-a-file-in-golang
func copyFileContents(src, dst string) (err error) {
in, err := os.Open(src)
if err != nil {
return err
}
defer safelyCloseFile(in)
out, err := os.Create(dst)
if err != nil {
return err
}
defer safelyCloseFile(out)
if _, err = io.Copy(out, in); err != nil {
return err
}
return out.Sync()
}
func safelyFlush(writer *bufio.Writer) {
if err := writer.Flush(); err != nil {
log.Error("Error flushing writer!")
log.Error(err.Error())
}
}
func safelyCloseFile(file *os.File) {
err := file.Close()
if err != nil {
log.Error("Error closing file!")
log.Error(err.Error())
}
}
type yamlDecoderFn func(*yaml.Decoder) error
func readStream(filename string, yamlDecoder yamlDecoderFn) error {
if filename == "" {
return errors.New("Must provide filename")
}
var rawData []byte
var err error
var stream io.Reader
if filename == "-" {
rawData, err = ioutil.ReadAll(os.Stdin)
stream = bufio.NewReader(os.Stdin)
} else {
rawData, err = ioutil.ReadFile(filename)
file, err := os.Open(filename)
if err != nil {
return err
}
defer safelyCloseFile(file)
stream = file
}
if err != nil {
return err
}
return yaml.Unmarshal(rawData, parsedData)
return yamlDecoder(yaml.NewDecoder(stream))
}
func readData(filename string, indexToRead int, parsedData interface{}) error {
return readStream(filename, func(decoder *yaml.Decoder) error {
for currentIndex := 0; currentIndex < indexToRead; currentIndex++ {
errorSkipping := decoder.Decode(parsedData)
if errorSkipping != nil {
return errors.Wrapf(errorSkipping, "Error processing document at index %v, %v", currentIndex, errorSkipping)
}
}
return decoder.Decode(parsedData)
})
}

View File

@@ -28,6 +28,13 @@ func TestRead(t *testing.T) {
assertResult(t, 2, result)
}
func TestReadMulti(t *testing.T) {
docIndex = "1"
result, _ := read([]string{"examples/multiple_docs.yaml", "another.document"})
assertResult(t, "here", result)
docIndex = "0"
}
func TestReadArray(t *testing.T) {
result, _ := read([]string{"examples/sample_array.yaml", "[1]"})
assertResult(t, 2, result)
@@ -71,37 +78,6 @@ func TestNewYamlArray(t *testing.T) {
formattedResult)
}
func TestUpdateYaml(t *testing.T) {
result, _ := updateYaml([]string{"examples/sample.yaml", "b.c", "3"})
formattedResult := fmt.Sprintf("%v", result)
assertResult(t,
"[{a Easy! as one two three} {b [{c 3} {d [3 4]} {e [[{name fred} {value 3}] [{name sam} {value 4}]]}]}]",
formattedResult)
}
func TestUpdateYamlArray(t *testing.T) {
result, _ := updateYaml([]string{"examples/sample_array.yaml", "[0]", "3"})
formattedResult := fmt.Sprintf("%v", result)
assertResult(t,
"[3 2 3]",
formattedResult)
}
func TestUpdateYaml_WithScript(t *testing.T) {
writeScript = "examples/instruction_sample.yaml"
_, _ = updateYaml([]string{"examples/sample.yaml"})
}
func TestUpdateYaml_WithUnknownScript(t *testing.T) {
writeScript = "fake-unknown"
_, err := updateYaml([]string{"examples/sample.yaml"})
if err == nil {
t.Error("Expected error due to unknown file")
}
expectedOutput := `open fake-unknown: no such file or directory`
assertResult(t, expectedOutput, err.Error())
}
func TestNewYaml_WithScript(t *testing.T) {
writeScript = "examples/instruction_sample.yaml"
expectedResult := `b: