2

I am trying to add a string called "SPEC1" to the end of a line which starts with BEGIN, but only if that string doesn't already exist. Some of the matched lines may or may not already have the string.

My example file looks like this:

BEGIN value1 value2 value3 SPEC1
some_other_line1
BEGIN value1 value2 value3 value4
some_other_line2
BEGIN value1 value2

And the result should look like this:

BEGIN value1 value2 value3 SPEC1
some_other_line1
BEGIN value1 value2 value3 value4 SPEC1
some_other_line2
BEGIN value1 value2 SPEC1

I am using sed to try and achieve this. My current sed solution is like this:

sed '/BEGIN/ {
s/ \+SPEC1//g
s|\(.*\)|\1 SPEC1|
}' file

Explanation of above:

  1. Search for lines that start with "BEGIN"
  2. Look for existing string "SPEC1" (may also have one or more spaces in front) and delete it globally to clean up the file
  3. Capture the whole matched line (sed BRE style) and append line with "SPEC1"

Is there a better way to achieve this either with sed, awk, perl or bash?

Thanks.

solaris
  • 311
  • 1
  • 4
  • 13

4 Answers4

2

Awk solution:

awk '$1=="BEGIN" && $NF!="SPEC1"{ $(NF+1)="SPEC1" }1' file
  • $1 - the 1st field of the current record
  • $NF - the number of fields in the current input record(points to the last field)
  • $(NF+1) - generates value for the next field after the last

The output:

BEGIN value1 value2 value3 SPEC1
some_other_line1
BEGIN value1 value2 value3 value4 SPEC1
some_other_line2
BEGIN value1 value2 SPEC1
RomanPerekhrest
  • 88,541
  • 4
  • 65
  • 105
1
awk '/^BEGIN/&& !/SPEC1$/{$0=$0 FS "SPEC1"}1' inputfile
BEGIN value1 value2 value3 SPEC1
some_other_line1
BEGIN value1 value2 value3 value4 SPEC1
some_other_line2
BEGIN value1 value2 SPEC1

If you wish to have SPEC1 as variable:

awk -v string="SPEC1"$"" '/^BEGIN/&&  $0 !~ string{$0=$0 FS string}1' input
P....
  • 17,421
  • 2
  • 32
  • 52
  • 1
    That 2nd script doesn't append `$` to the end of `string` and if it did it'd still be present when you add it. It'd also falsely match `FOOSPEC1`, just do a string comparison on $NF (or add anchors to both sides if you feel a burning desire to use a regexp). – Ed Morton Jan 11 '18 at 16:03
1

Improving the sed version mentioned in OP

$ sed '/BEGIN/{/ SPEC1/! s/$/ SPEC1/}' ip.txt
BEGIN value1 value2 value3 SPEC1
some_other_line1
BEGIN value1 value2 value3 value4 SPEC1
some_other_line2
BEGIN value1 value2 SPEC1
  • /BEGIN/ if line contains BEGIN (add start of line qualifier if needed)
    • / SPEC1/! if line doesn't contain SPEC1 (add end of line qualifier if needed)
    • s/$/ SPEC1/ add SPEC1 to end of line
Sundeep
  • 23,246
  • 2
  • 28
  • 103
0

Another possible sed solution:

$ sed -r '/^BEGIN/s/( SPEC1)?$/ SPEC1/' data
BEGIN value1 value2 value3 SPEC1
some_other_line1
BEGIN value1 value2 value3 value4 SPEC1
some_other_line2
BEGIN value1 value2 SPEC1
PesaThe
  • 7,259
  • 1
  • 19
  • 43