substitute a string on a specific position for specific lines

I woud like to substitue a string on a specific position for specific lines

I've got a file and I would like to change a specific string from "TOCHANGE" to "ABCABCAB"

For every line (except 1,2, 3 and the last one) , I need to check between the 9th and the 16th digits.
For the 3rd line, I need to check between the 12th and the 19th digits.
No checks for line 1,3 and the last one.

------------------------
------------------------
example 1:

line 3 
12345678901TOCHANGE12345
Line 25,.... 
12345678TOCHANGE12345678
123456789TOCHANGE1234567
TOCHANGE1234567890123456

Only the line 3 and 25 need to be amended

So I would like to get

line 3 
12345678901ABCABCAB12345
Line 25,...
12345678ABCABCAB12345678
123456789TOCHANGE1234567
TOCHANGE1234567890123456

-------------------------
-------------------------
example 2:

line 3 
123456789TOCHANGE1234567
Line 25,.... 
12345678TOCHANGE12345678
123456789TOCHANGE1234567
TOCHANGE1234567890123456

Only the line 25 needs to be amended

So I would like to get

Line 3
123456789TOCHANGE1234567 
Line 25,...
12345678ABCABCAB12345678
123456789TOCHANGE1234567
TOCHANGE1234567890123456

Do you have any idea about how I could do it?

I tried with the sed command but it works on the number of occurence.
I think it could be done via the awk command

Many thanks

I'm editing my post because I just re-read your post and saw that you are only looking at specific locations on each line. Also, I see that you already tried sed. I believe awk will accept a period for a single character. You could try using a while loop to read each line (while line=$(line)) and checking for a successful status on an awk command like: awk '/^........TOCHANGE/' $line. If that is successful, you could then use a sed (newline = `sed -e 's/TOCHANGE/ABCABCAB' $line`).

My apologies for the confusion, but I hope it helps. By the way, I didn't have a file like this to try. So, these are untried suggestions.

Hi

Thanks for this.
But I was wondering whether or not it could be possible to do it without a loop.
I'd like to use an 'awk' or a 'sed'.
My first thought was to use a sed but it works by occurence.

Do you have any other idea?

Cheers,

Bernard

# cat tst
12345678901TOCHANGE12345
12345678901TOCHANGE12345
12345678901TOCHANGE12345
12345678901TOCHANGE12345
12345678901TOCHANGE12345
12345678901TOCHANGE12345
12345678901TOCHANGE12345
12345678901TOCHANGE12345
12345678901TOCHANGE12345
12345678901TOCHANGE12345
# sed '4,'"$(( $(wc -l <tst)-1 ))"'s/TOCHANGE/XXXXXXXX/' tst
12345678901TOCHANGE12345
12345678901TOCHANGE12345
12345678901TOCHANGE12345
12345678901XXXXXXXX12345
12345678901XXXXXXXX12345
12345678901XXXXXXXX12345
12345678901XXXXXXXX12345
12345678901XXXXXXXX12345
12345678901XXXXXXXX12345
12345678901TOCHANGE12345

@ctsgnb
Good way to get 2nd last line no but I guess above script needs to be modified as below:

Ignore line 1,2 and the last one (No change)
For the 3rd line, Replace only if TOCHANGE is in 12th to 19th position.
For other lines (4th - 2nd last), Replace only if TOCHANGE is in 9th to 16th position.

 
sed -e '3s/\(...........\)TOCHANGE\(.*\)/\1ABCABCAB\2/' -e '4,'"$(( $(wc -l < inputFile)-1 ))"'s/\(........\)TOCHANGE\(.*\)/\1ABCABCAB\2/' inputFile
 

@anurag

Ooops, you are true, i totally forgot that position case ... here is an even better

  • need caret ^ to be exact regarding the position otherwise the \(.....<n_times>\)TOCHANGE could also be match at a position greater than the expected one
  • use the $! tip to avoid last line)
  • therefore avoid the use of additionnal file opening & line counting of the wc -l
  • no need of sed -e option
sed '3s/^\(...........\)TOCHANGE\(.*\)/\1ABCABCAB\2/;$!{4,$s/^\(........\)TOCHANGE\(.*\)/\1ABCABCAB\2/}' infile
# cat tst1
12345678901TOCHANGE12345
12345678901TOCHANGE12345
12345678901TOCHANGE12345
12345678901TOCHANGE12345
12345678901TOCHANGE12345
12345678901TOCHANGE12345
12345678901TOCHANGE12345
# cat tst2
12345678TOCHANGE12345
12345678TOCHANGE12345
12345678TOCHANGE12345
12345678TOCHANGE12345
12345678TOCHANGE12345
12345678TOCHANGE12345
12345678TOCHANGE12345
# sed '3s/^\(...........\)TOCHANGE\(.*\)/\1ABCABCAB\2/;$!{4,$s/^\(........\)TOCHANGE\(.*\)/\1ABCABCAB\2/}' tst1
12345678901TOCHANGE12345
12345678901TOCHANGE12345
12345678901ABCABCAB12345
12345678901TOCHANGE12345
12345678901TOCHANGE12345
12345678901TOCHANGE12345
12345678901TOCHANGE12345
# sed '3s/^\(...........\)TOCHANGE\(.*\)/\1ABCABCAB\2/;$!{4,$s/^\(........\)TOCHANGE\(.*\)/\1ABCABCAB\2/}' tst2
12345678TOCHANGE12345
12345678TOCHANGE12345
12345678TOCHANGE12345
12345678ABCABCAB12345
12345678ABCABCAB12345
12345678ABCABCAB12345
12345678TOCHANGE12345

@ctsgnb, Yes. I missed caret ^ and last one is much better. Tks

Hi all,

Many thanks for this. It helped me a lot.
I had an error message because the command couldn't parse something

So I slightly changed it and it worked out

sed '3s/^\(...........................\)TOCHANGE\(.\)/\1ABCABCAB\2/;4,'"$(( $(wc -l < filename )-2 ))"'s/^\(.................\)TOCHANGE\(.\)/\1ABCABCAB\2/' filename

Again , many thanks for your help