2

I have a file with a character I would like to replace, but I also want to have each instance of the character(s) to be replaced, be iterated with a number inside the sting replacement.

SomeFile.txt

[H
A
[H
B
[H
C

I want to replace "[H" with "</pre></div><div id=cat$n><pre>" where $n is an interator for the number of replacements. So the first replacement would be 1, 2, 3, 4, n++ and so on.

Is there a way to to do this with sed?

5
  • 1
    It can be done with sed using a loop, but do you absolutely need sed? Perl would suit to this problem better.
    – kos
    Apr 6, 2016 at 17:28
  • I'm just looking for a solution. Im not biased, but i dont know how to solve it with perl either.
    – j0h
    Apr 6, 2016 at 17:30
  • My answer turned out to be almost identical to choroba's, so here's a slight variation (which does exactly the same thing): perl -pe 's/^\[H$/"<\/pre><\/div><div id=cat".++$n."><pre>"/e' file.
    – kos
    Apr 6, 2016 at 17:47
  • @j0h may I ask just for a small clarification ? You want to count number of replacements, right ? i.e. , you want to know how many times that string was replaced, correct ? Apr 6, 2016 at 18:39
  • I only care that the nth number is in the string that is in the replacement. I actualy know it needs to happen 3413 times. I think I found that with cat filename | grep "\[H" | wc -l Im not saying it wouldnt be useful, but I already have that info. Heres what the file was editing: nanocluster.umeche.maine.edu/ascii/star/starWarz.html
    – j0h
    Apr 6, 2016 at 18:47

3 Answers 3

5

Perl to the rescue!

perl -pe 'BEGIN { $n = 1 } s%\[H%</pre></div><div id=cat$n><pre>% && $n++' filename
  • -p reads the input line by line and prints whatever the code does with it
  • BEGIN { ... } is run before the program is entered
  • s%pattern%replacement% replaces pattern with replacement.
  • \[ needs a backslash, because [ is special in regular expressions
  • && $n++ If the substitution is successful, it returns true, so the next command is run: and it increments the counter $n.
4

Another awk way, treating [H as the record separator:

awk -v RS='\\[H' 'NR>1{printf "</pre></div><div id=cat%d><pre>",NR-1}1'

All but the first record will have [H before it, so, for these records, I print a </pre></div><div id=catN><pre>, where N = NR - 1 (NR being the record number) before the record itself.

$ awk -v RS='\\[H' 'NR>1{printf "</pre></div><div id=cat%d><pre>",NR-1}1' foo.txt

</pre></div><div id=cat1><pre>
A

</pre></div><div id=cat2><pre>
B

</pre></div><div id=cat3><pre>
C

WIth GNU awk, you can use the new inplace modification module:

gawk -i inplace  -v RS='\\[H' 'NR>1{printf "</pre></div><div id=cat%d><pre>",NR-1}1' foo.txt

This is similar to sed -i.

2

AWK version. Notice how [ is escaped and how quotes are organized to allow counter variable to expand in "</pre></div><div id=cat"counter"><pre>" :

$ awk '{ if ( $0~/\[H/ ){\                                                     
>    counter++; sub(/\[H/,"</pre></div><div id=cat"counter"><pre>")\           
> }; print }' test_input.txt                                                   
</pre></div><div id=cat1><pre>
A
</pre></div><div id=cat2><pre>
B
</pre></div><div id=cat3><pre>
C
</pre></div><div id=cat4><pre>

Unless you are using gawk, use gawk '...' input_file.txt > /tmp/tmp.txt && mv /tmp/tmp.txt input_file.txt approach to replace original file with edited version

Addition

As muru have suggested, the code can be simplified to

awk '/\[H/{counter++; sub(/\[H/,"</pre></div><div id=cat"counter"><pre>")}1' test_input.txt
4
  • 1
    { if ($0 ~/\[H/{...} } is somewhat verbose. I think your expression boils down to : awk '/\[H/{counter++, sub(...)}1'.
    – muru
    Apr 6, 2016 at 19:15
  • @muru I get dual lines if I write it as awk '/\[H/;1' test_input.txt Apr 6, 2016 at 19:17
  • Of course you will. Note that I elided only the sub function in my comment. The print was ommitted. If you want me to be verbose: awk '/\[H/{counter++, sub(/\[H/,"</pre></div><div id=cat"counter"><pre>")}1'
    – muru
    Apr 6, 2016 at 19:17
  • I could use awk '/\[H/ {print $0};!/\[H/' test_input.txt form though Apr 6, 2016 at 19:17

You must log in to answer this question.

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