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.

Not the answer you're looking for? Browse other questions tagged .