简体   繁体   中英

Delete two lines before a specific pattern using sed

Not much familiar with sed, I'm trying to delete 2 lines before a pattern (a timeout value and an empty line) and then reinsert both lines with updated timeout.

Here's a part of the yaml file that I've:

- id: phase1
  blahblahbal
  timeout: 720

- id: phase2
  blahblahbalh
  timeout: 1800

I'm trying to update first timeout to '900'.

Here's what I've with grep:

grep -v "$(grep -B 2 'id: phase2' test.yaml | grep -v 'id: phase2')" test.yaml > test.yaml

and then inserting updated value with sed. This is working but the grep doesn't look nice. Is there a way to delete the two lines with sed before a pattern?

Expected output after first sed/grep:

- id: phase1
  blahblahbal
- id: phase2
  blahblahbalh
  timeout: 1800

Expected output final:

- id: phase1
  blahblahbal
  timeout: 900

- id: phase2
  blahblahbalh
  timeout: 1800

This is how it could be done using awk ( back-replace2.awk ):

$1 ~ /timeout:/ { lineTimeOut = NR }
/^[ \t\r]*$/ { lineEmpty = NR }
/- id: phase2/ {
  if (lineTimeOut == NR - 2 && lineEmpty == NR - 1) {
    buf1 = "  timeout: 900"
  }
}
{
  if (NR > 2) { print buf1 }
  buf1 = buf2 ; buf2 = $0
}
END {
  if (NR >= 2) { print buf1 }
  if (NR >= 1) { print buf2 }
}

The line numbers of a timeout: line and an empty line are remembered. Thus, it can be checked whether these lines appeared exactly two lines before / one line before the line where the remarked pattern (here - id: phase2 ) matches.

The variables buf1 and buf2 are used to make some kind of cycle buffering (ie the third last line is echoed for every line).

Therefore, the END rule is necessary to echo the rest of input (the contents of cycle buffers).

Test:

$ cat >back-replace2.txt <<EOF
- id: phase1
  blahblahbal
  timeout: 720

- id: phase2
  blahblahbalh
  timeout: 1800
EOF

$ awk -f back-replace2.awk back-replace2.txt 
- id: phase1
  blahblahbal
  timeout: 900

- id: phase2
  blahblahbalh
  timeout: 1800

$

Notes:

  1. I didn't check the edge cases (eg whether files with less than 3 lines are handled correctly).

  2. The pattern matching and replacement may need additional logic. I'm sure the questioner will be able to adjust the script appropriately.

Here is my solution with sed:

# Remove above two lines before phase2 id
sed -i ':a;N;s/\n/&/2;Ta;/\n- id\: phase2$/s/.*\n//;P;D' test.yaml

# Add updated timeout
sed -i "/- id\: phase2/ i\\
    timeout: 900\\
" test.yaml

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM