diff --git a/commands_test.go b/commands_test.go
index 420064b6e67..fb80813d112 100644
--- a/commands_test.go
+++ b/commands_test.go
@@ -435,6 +435,25 @@ func TestWriteCmd_Inplace(t *testing.T) {
assertResult(t, expectedOutput, gotOutput)
}
+func TestWriteCmd_Append(t *testing.T) {
+ content := `b:
+ - foo
+`
+ filename := writeTempYamlFile(content)
+ defer removeTempYamlFile(filename)
+
+ cmd := getRootCommand()
+ result := runCmd(cmd, fmt.Sprintf("write %s b[+] 7", filename))
+ if result.Error != nil {
+ t.Error(result.Error)
+ }
+ expectedOutput := `b:
+- foo
+- 7
+`
+ assertResult(t, expectedOutput, result.Output)
+}
+
func TestMergeCmd(t *testing.T) {
cmd := getRootCommand()
result := runCmd(cmd, "merge examples/data1.yaml examples/data2.yaml")
diff --git a/data_navigator.go b/data_navigator.go
index 274071fc1dc..5f9469949ab 100644
--- a/data_navigator.go
+++ b/data_navigator.go
@@ -55,7 +55,7 @@ func updatedChildValue(child interface{}, remainingPaths []string, value interfa
}
_, nextIndexErr := strconv.ParseInt(remainingPaths[0], 10, 64)
- if nextIndexErr != nil {
+ if nextIndexErr != nil && remainingPaths[0] != "+" {
// must be a map
return writeMap(child, remainingPaths, value)
}
@@ -81,7 +81,13 @@ func writeArray(context interface{}, paths []string, value interface{}) []interf
log.Debugf("\tarray %v\n", array)
rawIndex := paths[0]
- index, _ := strconv.ParseInt(rawIndex, 10, 64)
+ var index int64
+ // the append array indicator
+ if rawIndex == "+" {
+ index = int64(len(array))
+ } else {
+ index, _ = strconv.ParseInt(rawIndex, 10, 64)
+ }
// writeArray is only called by updatedChildValue which handles parsing the
// index, as such this renders this dead code.
// if err != nil {
diff --git a/docs/mkdocs/search_index.json b/docs/mkdocs/search_index.json
index c7d3989f9fe..533d377396a 100644
--- a/docs/mkdocs/search_index.json
+++ b/docs/mkdocs/search_index.json
@@ -57,7 +57,7 @@
},
{
"location": "/write/",
- "text": "yaml w \nyaml_file|json_file\n \npath\n \nnew value\n\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\n\nGiven a sample.yaml file of:\n\n\nb:\n c: 2\n\n\n\n\nthen\n\n\nyaml 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\n\ncat sample.yaml | yaml w - b.c blah\n\n\n\n\nAdding new fields\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\nyaml w sample.yaml b.d[0] \nnew thing\n\n\n\n\n\nwill output:\n\n\nb:\n c: cat\n d:\n - new thing\n\n\n\n\nUpdating files in-place\n\n\nGiven a sample.yaml file of:\n\n\nb:\n c: 2\n\n\n\n\nthen\n\n\nyaml 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\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\nyaml 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 | yaml w -s - sample.yaml\n\n\n\n\nValues starting with a hyphen (or dash)\n\n\nThe flag terminator needs to be used to stop the app from attempting to parse the subsequent arguments as flags:\n\n\nyaml w -- my.path -3\n\n\n\n\nwill output\n\n\nmy:\n path: -3",
+ "text": "yaml w \nyaml_file|json_file\n \npath\n \nnew value\n\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\n\nGiven a sample.yaml file of:\n\n\nb:\n c: 2\n\n\n\n\nthen\n\n\nyaml 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\n\ncat sample.yaml | yaml w - b.c blah\n\n\n\n\nAdding new fields\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\nyaml w sample.yaml b.d[0] \nnew thing\n\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\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\nyaml w sample.yaml b.d[+] \nbar thing\n\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\nUpdating files in-place\n\n\nGiven a sample.yaml file of:\n\n\nb:\n c: 2\n\n\n\n\nthen\n\n\nyaml 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\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\nyaml 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 | yaml w -s - sample.yaml\n\n\n\n\nValues starting with a hyphen (or dash)\n\n\nThe flag terminator needs to be used to stop the app from attempting to parse the subsequent arguments as flags:\n\n\nyaml w -- my.path -3\n\n\n\n\nwill output\n\n\nmy:\n path: -3",
"title": "Write/Update"
},
{
@@ -75,6 +75,11 @@
"text": "Any missing fields in the path will be created on the fly. Given a sample.yaml file of: b:\n c: 2 then yaml w sample.yaml b.d[0] new thing will output: b:\n c: cat\n d:\n - new thing",
"title": "Adding new fields"
},
+ {
+ "location": "/write/#appending-value-to-an-array-field",
+ "text": "Given a sample.yaml file of: b:\n c: 2\n d:\n - new thing\n - foo thing then yaml w sample.yaml b.d[+] bar thing will output: b:\n c: cat\n d:\n - new thing\n - foo thing\n - bar thing",
+ "title": "Appending value to an array field"
+ },
{
"location": "/write/#updating-files-in-place",
"text": "Given a sample.yaml file of: b:\n c: 2 then yaml w -i sample.yaml b.c cat will update the sample.yaml file so that the value of 'c' is cat.",
diff --git a/docs/sitemap.xml b/docs/sitemap.xml
index 21e7ee5e3ab..222fdf84e15 100644
--- a/docs/sitemap.xml
+++ b/docs/sitemap.xml
@@ -4,7 +4,7 @@
Given a sample.yaml file of:
+b:
+ c: 2
+ d:
+ - new thing
+ - foo thing
+
+
+then
+yaml w sample.yaml b.d[+] "bar thing"
+
+
+will output:
+b:
+ c: cat
+ d:
+ - new thing
+ - foo thing
+ - bar thing
+
+
Given a sample.yaml file of:
b:
diff --git a/mkdocs/write.md b/mkdocs/write.md
index 209a004836a..460c74e8f33 100644
--- a/mkdocs/write.md
+++ b/mkdocs/write.md
@@ -44,6 +44,29 @@ b:
- new thing
```
+### Appending value to an array field
+Given a sample.yaml file of:
+```yaml
+b:
+ c: 2
+ d:
+ - new thing
+ - foo thing
+```
+then
+```bash
+yaml w sample.yaml b.d[+] "bar thing"
+```
+will output:
+```yaml
+b:
+ c: cat
+ d:
+ - new thing
+ - foo thing
+ - bar thing
+```
+
### Updating files in-place
Given a sample.yaml file of:
```yaml
diff --git a/path_parser_test.go b/path_parser_test.go
index e733f58b846..a8088d379ef 100644
--- a/path_parser_test.go
+++ b/path_parser_test.go
@@ -10,6 +10,7 @@ var parsePathsTests = []struct {
}{
{"a.b", []string{"a", "b"}},
{"a.b[0]", []string{"a", "b", "0"}},
+ {"a.b.d[+]", []string{"a", "b", "d", "+"}},
}
func TestParsePath(t *testing.T) {
diff --git a/yaml.go b/yaml.go
index bff7eeee973..d1795579955 100644
--- a/yaml.go
+++ b/yaml.go
@@ -98,10 +98,14 @@ yaml write --inplace things.yaml a.b.c cat
yaml w -i things.yaml a.b.c cat
yaml w --script update_script.yaml things.yaml
yaml w -i -s update_script.yaml things.yaml
+yaml w things.yaml a.b.d[+] foo
+yaml w 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.
+Append value to array adds the value to the end of array.
+
Update Scripts:
Note that you can give an update script to perform more sophisticated updated. Update script
format is a yaml map where the key is the path and the value is..well the value. e.g.: