0

I am to replace using sed the 3th (or nth to be more general) occurrence in respective line with specified match regex. so far as i can is only to do the first:

$cat file   
# Golden dictionary is a versatile multi purpose reference  
# Copyright (C) 2004-2008 A  
# Copyright (C) 2008-2015 B  
# Copyright (C) 2015-2016 C   

so far as i can is only

$ cat file| sed -E '0,/copy/I s//No-&/'

# Golden dictionary is a versatile multi purpose reference  
# No-Copyright (C) 2004-2008 A  
# Copyright (C) 2008-2015 B  
# Copyright (C) 2015-2016 C 

How to do so by such way for the 3th match only?

3
  • What is the expected output? Jan 27, 2019 at 1:15
  • You want the third line that contains the text to be prefixed with No-? Or the third occurrence no matter where it is?
    – Jeff Schaller
    Jan 27, 2019 at 1:32
  • the third occurrence of respective line, no matter to many occurrences in a line, if 2nd line is # Copyright (C) 2004-2008 Copyright (C) 2009 Copyright (C) 2018 # Copyright (C) 2019 A ,still must find 4th line Copyright (C) 2015-2016 C to be replace then
    – user276072
    Jan 27, 2019 at 2:15

2 Answers 2

1

With ed, prefixing the 3rd case-insensitive "copy" with "No-" would be:

ed -s file <<< $'/[Cc][Oo][Pp][Yy]/\n//\n//\ns//No-&/\nw\nq' > /dev/null

The commands are:

  • /[Cc][Oo][Pp][Yy]/ -- search, in a manually case-insensitive way for "copy:
  • // -- repeat the search, twice
  • s//No-&/ -- replace the last match with the "No-" prefix
  • w -- write the change file to disk
  • q -- quit ed

With sed, you could do some pre-work to find the line number to change:

sed -i $(grep -in copy file |awk -F: 'NR==3 { print $1 }')'s/copy/No-&/i' input

Working from left to right,

  • -i -- GNU sed's in-place edit option
  • $( ... ) -- find the line number of the 3rd match of "copy"
    • grep -in copy file -- find the word "copy", case-insensitively in file and report the line numbers of the matches
    • awk -F: 'NR==3 { print $1 }' -- on line 3 of grep's output, split the line on colons and report back column 1 (the line number)
  • s/copy/No-&/i -- replace "copy" with "No-copy", case-insensitively
1
  • 1
    Or newish gawk could do the change itself: awk -i inplace -vIGNORECASE=1 '/copy/&&++n==3 {sub(/copy/,"No-&")} 1' file (for other awk you could do [Cc] etc and the awk ... file >temp && mv temp file dance) Jan 27, 2019 at 7:41
0

Using awk, counting how many lines contain the string Copyright, and when we reach the third such line, perform a substitution of the first such occurrence on that line:

awk '/Copyright/ && ++n == 3 { sub("Copyright", "No-&") } { print }' file

Using & in the replacement means using whatever the regular expression matched. In this case it will be the string Copyright that will be inserted. This (using &) also works in e.g. sed.

The condition before the first block,

/Copyright/ && ++n == 3

would match the string Copyright against the current line. If it matches, n would be incremented. If the incremented value of n is equal to 3, the block is executed.

Testing:

$ cat file
# Golden dictionary is a versatile multi purpose reference
# Copyright (C) 2004-2008 A
# Copyright (C) 2008-2015 B
# Copyright (C) 2015-2016 C
# Golden dictionary is a versatile multi purpose reference
# Copyright (C) 2004-2008 A
# Copyright (C) 2008-2015 B
# Copyright (C) 2015-2016 C

(the file is the same as yours, but with the contents duplicated once)

$ awk '/Copyright/ && ++n == 3 { sub("Copyright", "No-&") } { print }' file
# Golden dictionary is a versatile multi purpose reference
# Copyright (C) 2004-2008 A
# Copyright (C) 2008-2015 B
# No-Copyright (C) 2015-2016 C
# Golden dictionary is a versatile multi purpose reference
# Copyright (C) 2004-2008 A
# Copyright (C) 2008-2015 B
# Copyright (C) 2015-2016 C

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy