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
tofruit
afterapple
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:
- already tried:
- sed: Appending after a block
- use sed to insert a line after matching a block of text
- regex for appending text using sed
- ... and others...
- but probably not in the correct way...
- aware that using
awk
,perl
,python
or other tools/languages would be much simpler- but why not just use
sed
?!
- but why not just use
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