Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

Suppose I have a file that contains multiple occurrences of both StringA and StringB. I want to replace all occurrences of StringA with StringB, and (simultaneously) all occurrences of StringB with StringA.

Right now, I'm doing something like

cat file.txt | sed 's/StringB/StringC/g' | sed 's/StringA/StringB/g' | sed 's/StringC/StringA/g'

The problem with this approach is that it assumes StringC doesn't occur in the file. While this isn't an issue in practice, this solution still feels dirty -- that is, it feels like an opportunity to learn more unix magic. :)

share|improve this question

2 Answers 2

up vote 5 down vote accepted

If StringB and StringA can't appear on the same input line, then you can tell sed to perform the replacement one way, and only try the other way if there were no occurrences of the first searched string.

<file.txt sed -e 's/StringA/StringB/g' -e t 's/StringB/StringA/g'

In the general case, I don't think there is an easy method in sed. By the way, note that the specification is ambiguous if StringA and StringB can overlap. Here's a Perl solution, which replaces the leftmost occurrence of either string, and repeats.

<file.txt perl -pe 'BEGIN {%r = ("StringA" => "StringB", "StringB" => "StringA")}
                    s/(StringA|StringB)/$r{$1}/ge'

If you want to stick with POSIX tools, awk is the way to go. Awk doesn't have a primitive for general parametrized replacements, so you need to roll your own.

<file.txt awk '{
    while (match($0, /StringA|StringB/)) {
        printf "%s", substr($0, 1, RSTART-1);
        $0 = substr($0, RSTART);
        printf "%s", /^StringA/ ? "StringB" : "StringA";
        $0 = substr($0, 1+RLENGTH)
    }
    print
}'
share|improve this answer

I think it's perfectly valid to use a "nonce" string to swap two words. If you want a more general solution you can do something like:

sed 's/_/__/g; s/you/x_x/g; s/me/you/g; s/x_x/me/g; s/__/_/g' <<<"say you say me"

That yields

say me say you

Note that you need two additional substitution here to avoid replacing x_x if you happen to have "x_x" strings. But even that still seems simpler than the awk solution for me.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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