0

I want to add a symbol " >>" at the end of 1st line and then 5th line and then so on. 1,5,9,13,17,.... I was searching the web and went through below article but I'm unable to achieve it. Please help.

How can I append text below the specific number of lines in sed?

retentive
good at remembering
The child was very sharp, and her memory was extremely retentive. 
— Rowlands, Effie Adelaide

unconscionable
greatly exceeding bounds of reason or moderation
For generations in the New York City public schools, this has become the norm with devastating consequences rooted in unconscionable levels of student failure. 
— New York Times (Nov 4, 2011)

Output should be like-

retentive >>
good at remembering
The child was very sharp, and her memory was extremely retentive. 
— Rowlands, Effie Adelaide

unconscionable >>
greatly exceeding bounds of reason or moderation
For generations in the New York City public schools, this has become the norm with devastating consequences rooted in unconscionable levels of student failure. 
— New York Times (Nov 4, 2011)
Community
  • 1
  • 1

6 Answers6

1

You can do it with awk:

awk '{if ((NR-1) % 5) {print $0} else {print $0 " >>"}}'

We check if line number minus 1 is a multiple of 5 and if it is we output the line followed by a >>, otherwise, we just output the line.

Note: The above code outputs the suffix every 5 lines, because that's what is needed for your example to work.

redneb
  • 21,794
  • 6
  • 42
  • 54
0

You can do it multiple ways. sed is kind of odd when it comes to selecting lines but it's doable. E.g.:

sed:

sed -i -e 's/$/ >>/;n;n;n;n' file

You can do it also as perl one-liner:

perl -pi.bak -e 's/(.*)/$1 >>/ if not (( $. - 1 ) % 5)' file
Dave Grabowski
  • 1,619
  • 10
  • 19
0

You're thinking about this wrong. You should append to the end of the first line of every paragraph, don't worry about how many lines there happen to be in any given paragraph. That's just:

$ awk -v RS= -v ORS='\n\n' '{sub(/\n/," >>&")}1' file
retentive >>
good at remembering
The child was very sharp, and her memory was extremely retentive.
— Rowlands, Effie Adelaide

unconscionable >>
greatly exceeding bounds of reason or moderation
For generations in the New York City public schools, this has become the norm with devastating consequences rooted in unconscionable levels of student failure.
— New York Times (Nov 4, 2011)
Ed Morton
  • 188,023
  • 17
  • 78
  • 185
  • It looks like the data doesn't use free paragraphs. It looks like the third item in each record is a quote, and that quote is unwrapped to be a single line to stick to the four line format: *word*, *short definition*, *example quote*, *source*, `blank line`. – Kaz Sep 13 '16 at 19:01
0

This might work for you (GNU sed):

sed -i '1~4s/$/ >>/' file
potong
  • 55,640
  • 6
  • 51
  • 83
0

Here is a non-numeric way in Awk. This works if we have an Awk that supports the RS variable being more than one character long. We break the data into records based on the blank line separation: "\n\n". Inside these records, we break fields on newlines. Thus $1 is the word, $2 is the definition, $3 is the quote and $4 is the source:

awk 'BEGIN {OFS=FS="\n";ORS=RS="\n\n"} $1=$1" >>"'

We use the same output separators as input separators. Our only pattern/action step is then to edit $1 so that it has >> on it. The default action is { print }, which is what we want: print each record. So we can omit it.

Shorter: Initialize RS from catenation of FS.

awk 'BEGIN {OFS=FS="\n";ORS=RS=FS FS} $1=$1" >>"'

This is nicely expressive: it says that the format uses two consecutive field separators to separate records.

What if we use a flag, initially reset, which is reset on every blank line? This solution still doesn't depend on a hard-coded number, just the blank line separation. The rule fires on the first line, because C evaluates to zero, and then after every blank line, because we reset C to zero:

awk 'C++?1:$0=$0" >>";!NF{C=0}'

Shorter version of accepted Awk solution:

awk '(NR-1)%5?1:$0=$0" >>"'

We can use a ternary conditional expression cond ? then : else as a pattern, leaving the action empty so that it defaults to {print} which of course means {print $0}. If the zero-based record number is is not congruent to 0, modulo 5, then we produce 1 to trigger the print action. Otherwise we evaluate `$0=$0" >>" to add the required suffix to the record. The result of this expression is also a Boolean true, which triggers the print action.

Shave off one more character: we don't have to subtract 1 from NR and then test for congruence to zero. Basically whenever the 1-based record number is congruent to 1, modulo 5, then we want to add the >> suffix:

awk 'NR%5==1?$0=$0" >>":1'

Though we have to add ==1 (+3 chars), we win because we can drop two parentheses and -1 (-4 chars).

We can do better (with some assumptions): Instead of editing $0, what we can do is create a second field which contains >> by assigning to the parameter $2. The implicit print action will print this, offset by a space:

awk 'NR%5==1?$2=">>":1'

But this only works when the definition line contains one word. If any of the words in this dictionary are compound nouns (separated by space, not hyphenated), this fails. If we try to repair this flaw, we are sadly brought back to the same length:

awk 'NR%5==1?$++NF=">>":1'

Slight variation on the approach: Instead of trying to tack >> onto the record or last field, why don't we conditionally install >>\n as ORS, the output record separator?

awk 'ORS=(NR%5==1?" >>\n":"\n")'

Not the tersest, but worth mentioning. It shows how we can dynamically play with some of these variables from record to record.

Different way for testing NR == 1 (mod 5): namely, regexp!

awk 'NR~/[16]$/?$0=$0" >>":1'

Again, not tersest, but seems worth mentioning. We can treat NR as a string representing the integer as decimal digits. If it ends with 1 or 6 then it is congruent to 1, mod 5. Obviously, not easy to modify to other moduli, not to mention computationally disgusting.

Kaz
  • 55,781
  • 9
  • 100
  • 149
0

There's a couple more:

$ awk 'NR%5==1 && sub(/$/,">>>") || 1 ' foo

$ awk '$0=$0(NR%5==1?">>>":"")' foo

James Brown
  • 36,089
  • 7
  • 43
  • 59