2

How can I append an incremental count to every predefined word of a text file?

Just like this question: How can I append an incremental count to every line of a text file?

I want to add an incremental count to a text file. But instead of adding an incremental count to each line, I would like to add an incremental count to a predefined word.

For instance, if I want to count the word 'cinema' in the text, I would like all occurrences of 'cinema' changed to 'cinemaN', where N is the incremental number, and the maximum value of N is dependent on how many times the word 'cinema' occurs in the text.

So that an input text file containing this text:

He drove his car to the cinema. He then went inside the cinema to purchase tickets, and afterwards discovered that it was more then two years since he last visited the cinema.

Generates an output file with this content:

He drove his car to the cinema1. He then went inside the cinema2 to purchase tickets, and afterwards discovered that it was more then two years since he last visited the cinema3.

Preferably I would also like to be able to number the selected word in backwards order.

I.e. this would generate a second output file with this content:

He drove his car to the cinema3. He then went inside the cinema2 to purchase tickets, and afterwards discovered that it was more then two years since he last visited the cinema1.

1

Taking into account punctuation after the word.
Forward numbering:

word="cinema"
awk -v word="$word" '
    { 
      for (i = 1; i <= NF; i++) 
        if ($i ~ word "([,.;:)]|$)") { 
          gsub(word, word "" ++count,$i) 
        }
      print 
    }' input-file

Backward numbering:

word="cinema"
count="$(awk -v word="$word" '
    { count += gsub(word, "") }
    END { print count }' input-file)"
awk -v word="$word" -v count="$count" '
    { 
      for (i = 1; i <= NF; i++) 
        if ($i ~ word "([,.;:)]|$)") { 
          gsub(word, word "" count--, $i) 
        }
      print 
    }' input-file
  • Briliiant. Thank you so much. This is did the trick. I knew it had to be awk, but I am not that familiar yet with awk. But this is great. Thanks for a fast and detailed answer. – sku2003 Apr 17 '17 at 19:16
  • This won't work in general as it doesn't account for all punctuation or other characters that might follow a word (e.g. ' or " or !) is case-sensitive (presumably undesirable), and doesn't check for the word being present in mid-field (e.g. bar will match foobar). – Ed Morton Apr 18 '17 at 13:48
4

I'd prefer perl for this:

$ cat ip.txt 
He drove his car to the cinema. He then went inside the cinema to purchase tickets, and afterwards discovered that it was more then two years since he last visited the cinema.

$ # forward counting is easy
$ perl -pe 's/\bcinema\b/$&.++$i/ge' ip.txt 
He drove his car to the cinema1. He then went inside the cinema2 to purchase tickets, and afterwards discovered that it was more then two years since he last visited the cinema3.
  • \bcinema\b word to search for, using word boundaries so that it doesn't match as partial part of another word. For example, \bpar\b won't match apart or park or spar
  • ge the g flag is for global replacement. e allows to use Perl code in replacement section
  • $&.++$i is concatenation of matched word and pre-incremented value of $i which has default value of 0


For reverse, we need to get the count first...

$ c=$(grep -ow 'cinema' ip.txt | wc -l) perl -pe 's/\bcinema\b/$&.$ENV{c}--/ge' ip.txt 
He drove his car to the cinema3. He then went inside the cinema2 to purchase tickets, and afterwards discovered that it was more then two years since he last visited the cinema1.
  • c becomes environment variable accessible via the hash %ENV

or, with perl alone by slurping whole file

perl -0777 -pe '$c=()=/\bcinema\b/g; s//$&.$c--/ge' ip.txt 
2

With GNU awk for multi-char RS, case insensitive matching and word boundaries:

$ awk -v RS='^$' -v ORS= -v word='cinema' '
    BEGIN { IGNORECASE=1 }
    { cnt=gsub("\\<"word"\\>","&"); while (sub("\\<"word"\\>","&"cnt--)); print }
' file
He drove his car to the cinema3. He then went inside the cinema2 to purchase tickets, and afterwards discovered that it was more then two years since he last visited the cinema1.
0

For tagging the word in decreasing order we invert the regex AND invert the data and finally invert the date once again to effect the transformation:

perl -l -0777pe '$_ = reverse reverse =~ s/(?=\bamenic\b)/++$a/gre' input.data

Result

He drove his car to the cinema3. He then went inside the cinema2 to purchase tickets, and
afterwards discovered that it was more then two years since he last visited the cinema1.

For tagging the word in increasing order we do a lookbehind search of the word:

perl -lpe 's/\bcinema\b\K/++$a/eg' input.data

Result

He drove his car to the cinema1. He then went inside the cinema2 to purchase tickets, and
afterwards discovered that it was more then two years since he last visited the cinema3.

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.