1

I have a text file name class.txt with follow contents.

[serverClass:MAIL]
whitelist.0=LATE
whitelist.1=ONTIME

[serverClass:LETTER]
whitelist.0=FIRST
whitelist.1=SECOND
whitelist.2=THIRD
whitelist.3=FOURTH

[serverClass:NOTES]
whitelist.0=TEST
whitelist.1=CAR
whitelist.2=SPOON
whitelist.3=GAME

Let's say I want to add a new line to one of these blocks e.g. a new entry SAMPLE in block LETTER so whitelist number should auto increment when the new entry is added. Desired output

[serverClass:MAIL]
whitelist.0=LATE
whitelist.1=ONTIME

[serverClass:LETTER]
whitelist.0=FIRST
whitelist.1=OLD
whitelist.2=NEW
whitelist.3=FOURTH
whitelist.4=SAMPLE

[serverClass:NOTES]
whitelist.0=TEST
whitelist.1=CAR
whitelist.2=SPOON
whitelist.3=GAME

is there a way to do this with sed ?

2
  • What do you mean by "when new line is added"? Who adds the line? What does it add?
    – techraf
    Feb 23, 2016 at 22:04
  • I don't know much about sed, but this would be pretty easy to do in C
    – Liam
    Feb 23, 2016 at 23:21

2 Answers 2

1

As I said in my comment on Ralph's answer, there are better tools for this job e.g. with awk you could use paragraph mode, append whitelist.0=SAMPLE if the block is empty otherwise extract the no. from the last field (in this case the field is a line) and append whitelist.NR+1=SAMPLE to that block:

awk -vRS= -vORS='\n\n' 'BEGIN{z="whitelist.0=SAMPLE";FS="\n"}
/LETTER/{if (/[0-9]=/){split($NF, a, /[.=]/);sub(/0/, a[2]+1, z)}
sub (/$/,"\n"z ,$0)};1' infile
0
1

Great! Now, I've learnt something new. I've used sed for all sorts of (minor) substitutions, and not realized you can actually program in it. Obviously a quite weak "machine", since it has only two registers, and a rather obscure language.

Since sed doesn't take input variables, I've mad it an sh script that invokes sed with a dynamic program, which is prepared by replacing BLOCK and ENTRY with the actual tags from the command line. That replacement is done with a separate simple sed substitution to prepare the specific program to achieve the entry insertion.

The following script seems to do the work. If called addentry, it would be invoked as

$ addentry LETTER SAMPLE < data

to reproduce the input data with the entry inserted as its output. I suppose there's an -i option to sed for "in-place editing", if needs be.

#!/bin/sh

/bin/sed -n "$(cat << EOF | sed -e "s/BLOCK/$1/g;s/ENTRY/$2/g"
# Initialize hold space with 0
1 { x ; /^$/ s/^$/0/; x }

# Lines outside the interesting block are just printed
/\s*serverClass:BLOCK/,/^$/! { p }

# Lines of the interesting block are considered more in detail
/\s*serverClass:BLOCK/,/^$/ {

  # The final empty line is replaced by the new entry, using the line
  # counter from the "hold buffer"
  /^$/ { g; s/\(.*\)/whitelist.\1=ENTRY/p; s/.*//p; b xx }

  # print the line
  p

  # Jump forward for the block leader (note xx is a label)
  /serverClass:/ { b xx }

  # Increment hold space counter
  # (Only handles 0-9 here; room for improvement)
  x; y/0123456789/1234567890/; h

  # If the block ends the file without blank line, then add the
  # new entry at end.
  $ { g; s/\(.*\)/whitelist.\1=ENTRY/p; b xx }

  # Label xx is here
  :xx
}
EOF
)"

Many thanks for raising this (for me) interesting challenge.

4
  • Not bad... Couple of things: no need to check for empty pattern space on line 1, the hold space is always empty when you start sed. Also, you could do without the branches, drop -n and golf the remaining script shorter, e.g. with gnu sed syntax: sed '1{x;s/^$/0/;x};/serverClass:BLOCK/,/^$/{//!{x;y/0123456789/1234567890/;x};/^$/{G;s/\(\n\)\(.*\)/whitelist.\2=ENTRY\1/p;d};${G;s/\(.*\n\)\(.*\)/\1whitelist.\2=ENTRY/}}' But as you said, incrementing the counter for N>9 gets more complicated... I would avoid a sed-only solution, awk/perl are the ideal candidates. Feb 24, 2016 at 12:25
  • Thanks so much for the help guys. If there is a way to better this with awk or perl i would love to have it too. I asked for sed cuz I usually only work with sed, so I default to it when I asked the question. Many thanks
    – Hai Le
    Feb 24, 2016 at 13:59
  • Thanks Ralph, I have tested this and it works really well, only problem is we can't get pass after 9th increment, everything after 9 with be "whitlist.9=xxx"
    – Hai Le
    Feb 24, 2016 at 14:58
  • @don_crissti: yes, thanks; more compact and perhaps easier to grasp without the multiple "pathways". @HaiLe: awk or perl would/could look pretty much the same except for using slightly different tokens, but would handle increment (beyond 9) better. Maybe, to raise the bar rather than lowering it, you could ask for a gforth or cobol solution, and to enumerate in roman numerals. :-) Feb 24, 2016 at 21:32

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .