5

I'm trying to do some text processing on a file using a bash script. The goal is to take all of the lines starting with "field:" indented under an 'attribute:' label and swap them with the associated line starting with "- attr:" that follows.

So far I think I have regex patterns that should match the labels:

/ *field:(.*)/g

/ *- attr:(.*)/g

But I haven't had any success with the logic to parse through the desired fields and get them to swap correctly.

Example Input Text

- metric: 'example.metric.1'
  attributes:
      field: 'example 1'
    - attr: 'example1'
      field: 'example 2'
    - attr: 'example2'
      field: 'example 3'
    - attr: 'example3'
      field: 'example 4'
    - attr: 'example4'
- metric: 'example.metric.2'
  attributes:
      field: 'example 5'
    - attr: 'example5'
      field: 'example 6'
    - attr: 'example6'
      field: 'example 7'
    - attr: 'example7'
- metric: 'example.metric.3'
...

Desired Output

- metric: 'example.metric.1'
  attributes:
    - attr: 'example1'
      field: 'example 1'
    - attr: 'example2'
      field: 'example 2'
    - attr: 'example3'
      field: 'example 3'
    - attr: 'example4'
      field: 'example 4'
- metric: 'example.metric.2'
  attributes:
    - attr: 'example5'
      field: 'example 5'
    - attr: 'example6'
      field: 'example 6'
    - attr: 'example7'
      field: 'example 7'
- metric: 'example.metric.3'
... 

How would I go about accomplishing this?

1
  • 2
    Probably better to make the upstream process emit valid yaml rather than trying to fix it downstream. Commented Jun 4, 2021 at 17:38

4 Answers 4

6

Using any awk in any shell on every Unix box:

$ awk '$1=="field:"{s=ORS $0; next} {print $0 s; s=""}' file
- metric: 'example.metric.1'
  attributes:
    - attr: 'example1'
      field: 'example 1'
    - attr: 'example2'
      field: 'example 2'
    - attr: 'example3'
      field: 'example 3'
    - attr: 'example4'
      field: 'example 4'
- metric: 'example.metric.2'
  attributes:
    - attr: 'example5'
      field: 'example 5'
    - attr: 'example6'
      field: 'example 6'
    - attr: 'example7'
      field: 'example 7'
- metric: 'example.metric.3'

if you might not have a space after field: on some lines or just have a burning desire to use a regexp for some reason then change $1=="field:" to $1~/^field:/ or /^[[:space:]]*field:/, whichever you prefer.

0
3

With sed:

sed -n '/^ *field: /{h;n;G};p' data

If we match a field keyword then:

  • save the current line in the hold space (h)
  • get the next line from the file in the pattern space (n)
  • swap the pattern space with the hold space (G) (Equals to line swap)

print each line you encounter: p

2

Using awk:

awk '{if ($1 == "field:") {a=$0;x=0} 
else if (/- attr:/) {$0 = $0 ORS a; x=1} else {x=1}}x' input

In this command, if field: is found then current input record($0) is saved to variable a and x is set to zero. And if attr: is found then $0 is change d to old $0 followed by ORS(newline) followed by variable a.

0
1

We can use POSIX sed constructs to flip the said lines.

sed '/attr:/!x;$G' file

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.