'Replace multiple patterns, but not with the same string

is it possible to change multiply patterns to different values at the same command? lets say I have

A B C D ABC

and I want to change every A to 1 every B to 2 and every C to 3

so the output will be

1 2 3 D 123

since I have 3 patterns to change I would like to avoid substitute them separately. I thought there would be something like

sed -r s/'(A|B|C)'/(1|2|3)/ 

but of course this just replace A or B or C to (1|2|3). I should just mention that my real patterns are more complicated than that...

thank you!



Solution 1:[1]

Easy in sed:

sed 's/WORD1/NEW_WORD1/g;s/WORD2/NEW_WORD2/g;s/WORD3/NEW_WORD3/g'

You can separate multiple commands on the same line by a ;


Update

Probably this was too easy. NeronLeVelu pointed out that the above command can lead to unwanted results because the second substitution might even touch results of the first substitution (and so on).

If you care about this you can avoid this side effect with the t command. The t command branches to the end of the script, but only if a substitution did happen:

sed 's/WORD1/NEW_WORD1/g;t;s/WORD2/NEW_WORD2/g;t;s/WORD3/NEW_WORD3/g'  

Solution 2:[2]

This will work if your "words" don't contain RE metachars (. * ? etc.):

$ cat file
there is the problem when the foo is closed

$ cat tst.awk
BEGIN {
    split("the a foo bar",tmp)
    for (i=1;i in tmp;i+=2) {
        old = (i>1 ? old "|" : "\\<(") tmp[i]
        map[tmp[i]] = tmp[i+1]
    }
    old = old ")\\>"
}
{
    head = ""
    tail = $0
    while ( match(tail,old) ) {
        head = head substr(tail,1,RSTART-1) map[substr(tail,RSTART,RLENGTH)]
        tail = substr(tail,RSTART+RLENGTH)
    }
    print head tail
}

$ awk -f tst.awk file
there is a problem when a bar is closed

The above obviously maps "the" to "a" and "foo" to "bar" and uses GNU awk for word boundaries.

If your "words" do contain RE metachars etc. then you need a string-based solution using index() instead of an RE based one using match() (note that sed ONLY supports REs, not strings).

Solution 3:[3]

replace with callback function in javascript

similar to the perl solution by choroba

var i = 'abcd'
var r = {ab: "cd", cd: "ab"}

var o = i.replace(/ab|cd/g, (...args) => r[args[0]])

o == 'cdab'

can be optimized with capture groups like /(ab)|(cd)/g and checking args[i] for undefined values

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2
Solution 3 Mila Nautikus