1

I have a file with a yaml data as below:

cat x.yml
foo:
 - bar: 1
 - zoo: 2

I am able to insert the text but this is messing the indentation(see 2nd line):

sed -r '/^[ ]+- bar:/a- hoo: 3' x.yml
foo:
 - bar: 1
- hoo: 3
 - zoo: 2

Then, I tried to backreference the leading spaces but seems like it is not working with /a flag.

sed -r '/^([ ]+)- bar:/a\1- hoo: 3' x.yml
foo:
 - bar: 1
1- hoo: 3
 - zoo: 2

Any help to get the following using a one-liner ?

foo:
 - bar: 1
 - hoo: 3
 - zoo: 2
brian d foy
  • 129,424
  • 31
  • 207
  • 592
monk
  • 1,953
  • 3
  • 21
  • 41

3 Answers3

3

I suggest to switch to GNU sed's s command:

sed -E 's/( *)- bar:.*/&\n\1- hoo: 3/' file

Output:

foo:
 - bar: 1
 - hoo: 3
 - zoo: 2

See: man sed and The Stack Overflow Regular Expressions FAQ

Cyrus
  • 84,225
  • 14
  • 89
  • 153
  • @Kureteiyu: I can't follow that. When `bar` is `42`, it also works. I catch the space and number with `.*`. – Cyrus Jan 28 '22 at 18:22
3

Best option is probably to use a parser. If you know exactly where the values should be, you can just pop them in there. Otherwise you'd have to loop and look for the "bar" key. This is using the YAML module.

use strict;
use warnings;
use YAML;

my $yaml = do { local $/; <> };
my $href = Load($yaml);
for (0 .. $#{ $href->{foo} }) {
    if (grep { $_ eq "bar" } keys %{ $href->{foo}[$_] }) {
        splice @{ $href->{foo} }, $_+1, 0, { hoo => 1 };
    }
}
print Dump $href;

It outputs:

foo:
  - bar: 1
  - hoo: 1
  - zoo: 2

Otherwise you can use Perl like so:

$ perl -pe's/^( *- *)bar.*\K/$1hoo: 1\n/s' x.yml
foo:
 - bar: 1
 - hoo: 1
 - zoo: 2

Capture from beginning of line ^ a dash surrounded by dashes. Expect "bar", then absorb everything after it into the regex match, including the newline at the end (hence the /s modifier). Keep (\K) everything that was matched, and after it, add on the captured dash-string, plus your new content and a newline. Done.

TLP
  • 66,756
  • 10
  • 92
  • 149
1

First off, I agree with Inian saying a YML parser would be more appropriate here.

Nevertheless, you could use the s command and capture groups instead like

$ sed -r 's/^([ ]+)- bar:(.+)$/\1- bar:\2\n\1- hoo: 3/' x.yml

which gives

foo:
 - bar: 1
 - hoo: 3
 - zoo: 2
Kureteiyu
  • 529
  • 4
  • 10