Home > Software engineering >  Use sed to append text after start maker with text block until end marker
Use sed to append text after start maker with text block until end marker

Time:05-30

I have a report (simple text file) that needs to be updated (via bash) for service consumption.

The report consists of lists of categories each with a list of items, something like the following example:

vehicle:
car
moto
done

fruit:
banana
apple
done

tree:
pine
oak
done

The purpose of the update is to add new items after the last of a specific category:

Add orange to fruit after apple

For this I've created a small sample script to test/debug this procedure/update using sed:

#!/bin/bash

file=report.txt

cat > "$file" << EOT
vehicle:
car
moto
done

fruit:
banana
apple
done

tree:
pine
oak
done
EOT

category="fruit"
append="orange"

## only $category has $append - but at start:
#find_lhs="^$category:"
#replace_rhs="$category:\n$append"

## all categories have $append at end:
#find_lhs="([\S\s]*?)done"
#replace_rhs="\1$append\ndone"

# add $append at the end of $category - nothing happens:
find_lhs="^$category:([\S\s]*?)done"
replace_rhs="$category:\n\1$append\ndone"

sed -i -E "s/$find_lhs/$replace_rhs/g" "$file"

cat "$file"

Question:

What is missing/failing with:

sed -i -E "s/^$category:([\S\s]*?)done/$category:\n\1\n$append\ndone/g" "$file"

to produce the following report content:

vehicle:
car
moto
done

fruit:
banana
apple
orange #<-- inserted
done

tree:
pine
oak
done

??


Disclaimers:

CodePudding user response:

Using sed

$ sed "/$category/,/done/{/done/s/^/$append\n/}" <<< "$file"
$ sed "/$category/{:a;/done/s/^/$append\n/;n;/^$/!ba}" file
$ sed -e "/$category/{:a;/done/i$append" -e 'n;/^$/!ba}' file

Output

vehicle:
car
moto
done

fruit:
banana
apple
orange
done

tree:
pine
oak
done

CodePudding user response:

This might work for you (GNU sed):

sed -e '/fruit:/,/done/{/done/i\orange' -e '}' file

Focus on the fruit category and insert orange before the category delimiter i.e. done.

N.B. The solution is in two parts as the i command expects a newline or another command set.

Alternative:

sed '/fruit:/,/done/!b;/done/i\orange' file

CodePudding user response:

As an alternative, here is a gnu-awk that does the job using custom RS:

awk -v append='\norange' -v category='fruit:' -v RS='\ndone\n' '
{ORS=RT} $1 == category {$0 = $0 append} 1' file

vehicle:
car
moto
done

fruit:
banana
apple
orange
done

tree:
pine
oak
done
EOT

To save change into the same file use:

awk -i inplace -v append='\norange' -v category='fruit:' -v RS='\ndone\n' '{ORS=RT} $1 == category {$0 = $0 append} 1' file
  • Related