Troque todas as ocorrências de duas strings usando sed

12

Suponha que eu tenha um arquivo que contenha várias ocorrências de StringA e StringB. Eu quero substituir todas as ocorrências de StringA com StringB e (simultaneamente) todas as ocorrências de StringB com StringA.

Neste momento, estou fazendo algo como

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

O problema com essa abordagem é que ela assume que StringC não ocorre no arquivo. Embora isso não seja um problema na prática, esta solução ainda parece suja - isto é, parece uma oportunidade para aprender mais magia unix. :)

    
por Seth 19.09.2011 / 23:41

3 respostas

10

Se StringB e StringA não puderem aparecer na mesma linha de entrada, você poderá informar ao sed para realizar a substituição de uma maneira, e tentar somente de outra maneira se não houver ocorrências da primeira seqüência pesquisada.

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

No caso geral, não acho que haja um método fácil no sed. A propósito, observe que a especificação é ambígua se StringA e StringB puderem se sobrepor. Aqui está uma solução Perl, que substitui a ocorrência mais à esquerda de qualquer string e se repete.

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

Se você quiser ficar com as ferramentas POSIX, o awk é o caminho a percorrer. Awk não tem um primitivo para substituições gerais parametrizadas, então você precisa fazer o seu próprio.

<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
}'
    
por 20.09.2011 / 00:25
8

Right now, I'm doing something like
...............
The problem with this approach is that it assumes StringC doesn't occur in the file.

Eu acho que sua abordagem está bem, você deve apenas usar outra coisa em vez de uma string, algo que não pode ocorrer em uma linha (no espaço padrão). O melhor candidato é o \n ewline.
Normalmente, nenhuma linha de entrada no espaço padrão conterá esse caractere para que, para trocar todas as ocorrências de THIS e THAT em um arquivo, você possa executar:

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

ou, se o seu sed suportar \n no RHS também:

sed 's/THIS/\n/g;s/THAT/THIS/g;s/\n/THAT/g' infile
    
por 23.03.2015 / 22:14
3

Eu acho perfeitamente válido usar uma string "nonce" para trocar duas palavras. Se você quer uma solução mais geral, você pode fazer 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"

Isso produz

say me say you

Note que você precisa das duas substituições adicionais aqui para evitar a substituição de x_x se você tiver "x_x" strings. Mas mesmo isso ainda parece mais simples que a solução awk para mim.

    
por 26.04.2014 / 00:08

Tags