Limpiamente cambia todas las ocurrencias de dos cadenas usando sed

Supongamos que tengo un file que contiene múltiples ocurrencias de StringA y StringB. Quiero replace todas las ocurrencias de StringA con StringB y (simultáneamente) todas las ocurrencias de StringB con StringA.

En este momento, estoy haciendo algo como

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

El problema con este enfoque es que asume que StringC no ocurre en el file. Si bien esto no es un problema en la práctica, esta solución todavía se siente sucia, es decir, se siente como una oportunidad para aprender más magia de Unix. 🙂

Si StringB y StringA no pueden aparecer en la misma línea de input, puede decirle a sed que realice el reemploop de una manera, y solo intente de otra manera si no hubo ocurrencias de la primera cadena buscada.

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

En el caso general, no creo que haya un método fácil en sed. Por cierto, tenga en count que la especificación es ambigua si StringA y StringB pueden superponerse. Aquí hay una solución de Perl, que reemplaza la ocurrencia más a la izquierda de cualquier cadena y repite.

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

Si quiere seguir con las herramientas POSIX, awk es el path a seguir. Awk no tiene una primitiva para reemploops parametrizados en general, por lo que necesita hacer la suya propia.

 <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 }' 

En este momento, estoy haciendo algo como
……………
El problema con este enfoque es que asume que StringC no ocurre en el file.

Creo que su enfoque es bueno, debería usar algo más en lugar de una cadena, algo que no puede ocurrir en una línea (en el espacio del patrón). El mejor candidato es la \n ewline.
Normalmente, ninguna línea de input en el espacio del patrón contendrá ese carácter, por lo que, para intercambiar todas las apariciones de THIS THAT en un file, podría ejecutar:

 sed 's/THIS/\ /g s/THAT/THIS/g s/\n/THAT/g' infile 

o, si su sed soporta \n en el RHS también:

 sed 's/THIS/\n/g;s/THAT/THIS/g;s/\n/THAT/g' infile 

Creo que es perfectamente válido usar una cadena "nonce" para intercambiar dos palabras. Si quieres una solución más general, puedes hacer algo como:

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

Eso rinde

 say me say you 

Tenga en count que necesita las dos sustituciones adicionales aquí para evitar replace x_x si tiene cadenas "x_x". Pero incluso eso parece más simple que la solución awk para mí.