How to copy one key:value one block underneath and repeat this for each following blocks?

Question:

I have a yaml file where i need to copy a key:value (when: var) to the task underneath and need to repeat that for each block.

How it looks like now:

- set_fact: something
- set_fact: var_a
- task: do the foo
  when: var_a
- task: do the goo

- set_fact: something
- set_fact: var_b
- task: do the fofo
  when: var_b
- task: do the gogp

- set_fact: something
- set_fact: var_c
- task: do the fooolo
  when: var_c
- task: do the gooolo

How i need it to look like:

- set_fact: something
- set_fact: var_a
- task: do the foo
  when: var_a
- task: do the goo
  when: var_a

- set_fact: something
- set_fact: var_b
- task: do the fofo
  when: var_b
- task: do the gogp
  when: var_b

- set_fact: something
- set_fact: var_c
- task: do the fooolo
  when: var_c
- task: do the gooolo
  when: var_c

From researching how to do this, i think a vim loop could do that but i have no clue how to do that.
In case anybody has any hints, i’d be very grateful.

Edit: What i tried.
With this i captured the values i needed. (the var_ values)

grep -e set_fact -B1 filterlist_playbook.yml|grep -e "^.*:"|grep -v "set_fact:"|awk -F":" '{print $1}'

I tried this in two different ways.

1 first add the "when:" field and then copy/paste the var_ value
Adding the when: field was ok with sed. Pasting the right value sequentially… i do not find info how to do that, my colleagues also do not know.

2 copy/paste the "when: var_" value to the block underneath.
Again capturing the right value is ok with sed, pasting it sequentially leaves me scratching my head and googling into oblivion.

Note:
I will definitely select a working answer but its taking me time to try each one of them.

Asked By: trainin99

||

Answers:

Using awk you can do this:

awk -F ': ' '
$1 ~ / when$/ {var = $0}
NR > 1 {print prev}
!NF && prev ~ /^- task/ {print var}
{prev = $0}
END {
   print prev
   if (prev ~ /^- task/)
      print var
}' file

Output:

- set_fact: something
- set_fact: somethingelse
- task: do the foo
  when: var_a
- task: do the goo
  when: var_a

- set_fact: something
- set_fact: somethingelse
- task: do the fofo
  when: var_b
- task: do the gogp
  when: var_b

- set_fact: something
- set_fact: somethingelse
- task: do the fooolo
  when: var_c
- task: do the gooolo
  when: var_c
Answered By: anubhava

In vim you could do this:

  • qq start recording a macro called q
  • /when:Enter find the next occurrence of when:
  • yy yank the current line
  • j move the cursor down
  • p paste the line underneath
  • q terminate the macro recording

At this point you can call the macro as many times as you need typing @q.

You may also get to know in advance how many times you need to perform this finding the occurrences of when: with :%s/when://ng, which will print out something like 3 matches on 3 lines. Then you may just type e.g. 3@q to call the macro 3 times.

Answered By: etuardu

If ed is available/acceptable.

printf '%sn' 'g/^[[:space:]]{1,}when: var_.*/t.1' ,p Q | ed -s file.yaml

If the output is correct, and in-place editing is needed, change

,p Q

to

w q

I only have GNU ed at hand.


  • g

    Globally substitute for all non-overlapping instances of the RE rather than just the first one. If both g and count are specified, the results are unspecified.

  • (1,$)g/RE/command list

  • (.,.)taddress

    The t command shall be equivalent to the m command, except that a copy of the addressed lines shall be placed after address address (which can be 0); the current line number shall be set to the address of the last line added.

  • .

    The period character ( . ) shall address the current line.

  • The positive decimal number n shall address the nth line of the edit buffer.

  • , or %

    Depending on the variant shall address the whole buffer.
    ,p or %p

  • (.,.)p

    The p command shall write to standard output the addressed lines; the current line number shall be set to the address of the last line written. The p command can be appended to any command other than e, E, f, q, Q, r, w, or !.

  • w
    The w command shall write the addressed lines into the file named by the pathname file.

  • q

    The q command shall cause ed to exit. If the buffer has changed since the last time the entire buffer was written, the user shall be warned, as described previously.

  • Q

    The Q command shall cause ed to exit without checking whether changes have been made in the buffer since the last w command.

  • -s
    Suppress the writing of byte counts by e, E, r, and w commands and of the ‘!’ prompt after a !command.


See


In vim something like.

:g/^[[:space:]]*when: var_.*/t.1
Answered By: Jetchisel

Here is how I would do it in Vim:

:g/when/normal mzvip^[:'zt'>^M

with ^[ obtained with <C-v><Esc> and ^M obtained with <C-v><CR>.

Breakdown:

  • :g/<pattern>/<command> executes <command> on every line that matches <pattern>.
  • :normal xxx executes normal commands from command-line mode.
  • mz puts mark 'z on the matched line.
  • vip visually selects the current paragraph, placing mark '> on its last line.
  • <Esc> leaves visual mode.
  • :'zt'> copies line with mark 'z below line with mark '>.
  • <CR> executes that last command.

See :help :global, :help :normal, :help mark-motions, :h :t, and :h :range.

Note that, intuitively, using mark '} like this:

:g/when/t'}

looks like it would solve the problem neatly, but Vim puts mark '} on the first blank line after an actual paragraph, which doesn’t help us since our anchor is the last line of the actual paragraph.

That is one of the reasons why the vip dance is necessary: we need a mark on the last line of the paragraph, not after it, and visually selecting the "inner paragraph" does just that with mark '>.

Answered By: romainl
{m,g}awk '
BEGIN { _*= FS = "^" (OFS = "  when: ") (___="")

} !_<NF ? (__=$_)*(NF=_) : ___<__ ? $!NF=__ RS $_ RS __ (__=___) : !_'  
- set_fact: something
- set_fact: var_a
- task: do the foo
  when: var_a
- task: do the goo
  when: var_a

- set_fact: something
- set_fact: var_b
- task: do the fofo
  when: var_b
- task: do the gogp
  when: var_b

- set_fact: something
- set_fact: var_c
- task: do the fooolo
  when: var_c
- task: do the gooolo
  when: var_c
Answered By: RARE Kpop Manifesto

To get the output you show from the input you show all you need is this, using any awk:

$ awk -v RS= -F'n' '{print $0 ORS $(NF-1) ORS}' file
- set_fact: something
- set_fact: var_a
- task: do the foo
  when: var_a
- task: do the goo
  when: var_a

- set_fact: something
- set_fact: var_b
- task: do the fofo
  when: var_b
- task: do the gogp
  when: var_b

- set_fact: something
- set_fact: var_c
- task: do the fooolo
  when: var_c
- task: do the gooolo
  when: var_c

or if your real input is more complicated than you’ve shown then, also using any awk:

$ awk '/when:/{w=$0} {print} /task:/ && w{print w; w=""}' file
- set_fact: something
- set_fact: var_a
- task: do the foo
  when: var_a
- task: do the goo
  when: var_a

- set_fact: something
- set_fact: var_b
- task: do the fofo
  when: var_b
- task: do the gogp
  when: var_b

- set_fact: something
- set_fact: var_c
- task: do the fooolo
  when: var_c
- task: do the gooolo
  when: var_c
Answered By: Ed Morton
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.