Posso usar o `sed` para traduzir caracteres como o` tr`?

14

Gostaria de substituir um conjunto de caracteres pelos caracteres correspondentes de outro conjunto, algo como isto:

original set: ots
"target" set: u.x

foobartest → fuubar.ex.

Traduções / transliterações como esta são a especialidade do trcomando:

$ echo 'foobartest' | tr 'ots' 'u.x'
fuubar.ex.

Infelizmente tr, não suporta a alteração de arquivos no local, como o sedfaz.
Gostaria de usar sedpara não precisar reinventar a roda de arquivos temporários de malabarismo.

n.st
fonte
Resposta automática a essa pergunta, pois não consegui encontrar nenhum resultado para "caracteres de conversão sed". A palavra-chave mágica acabou sendo "transliterada", mas imaginei que vale a pena tornar esse recurso o mais facilmente possível.
N
Algo a ter em mente ao tentar implementar soluções alternativas para isso: tr(corretamente) ignora a recursão nos conjuntos de substituição: echo 'abc' | tr ab bxbxc. Uma solução primitiva pode ocultar isso xxcporque reaplicará a tradução a caracteres que já foram traduzidos.
n.st
Relacionado: tr analógico para caracteres unicode? (GNU sedcontrário ao GNU trtransliteramos caracteres multi-byte)
Stéphane Chazelas
Se você quiser outra possibilidade: o perl pode traduzir, e -i e (a menos que seja antigo) multibyte. Não é POSIX, mas é bastante comum.
precisa saber é o seguinte

Respostas:

24

sedtem o ycomando que funciona exatamente como tr:

$ echo 'foobartest' | sed 'y/ots/u.x/'
fuubar.ex.

O ycomando faz parte da especificação POSIXsed , portanto, deve funcionar em praticamente qualquer plataforma.

E como é sed, você pode substituir um arquivo por sua versão editada, poupando-lhe os incômodos negócios de arquivos temporários (desde que sua implementação sedofereça suporte à -iopção, que não é especificada pelo POSIX):

$ sed -i 'y/ots/u.x/' some-file.txt
n.st
fonte
@ StéphaneChazelas Obrigado por apontar isso; Eu não estava ciente do funcionamento interno até agora. Eu editei minha resposta para mencionar isso.
n.st
Obrigado, isso é extraordinariamente útil! Eu esperava que funcionasse no VIM (8.0.1092 no CentOS 7.3), mas não funciona. Nada que o sed faça, o VIM faz?
dotancohen 14/09/17
1
@dotancohen Só porque a função de substituição do Vim é modelada após sednão significa que as outras funções também sejam. ;) A lista de discussão do Vim tem um tópico sobre como encontrar um y/abc/def/equivalente; a melhor opção parece ser :%call setline(".", tr(getline("."),"abc","def")).
N
8

Se, como no seu caso, você estiver transliterando caracteres sem alterar seu tamanho (de qualquer forma, algumas implementações como o GNU trsuportam apenas caracteres de byte único), você pode:

tr 'ots' 'u.x' < file 1<> file

Ou seja, trsubstitui o arquivo por si próprio.

Isso é melhor do que sed -iem várias contas:

  • ele não precisa de espaço em disco extra (exceto em alguns arquivos esparsos, casos especiais de cópia na gravação)
  • preserva números de inode, propriedade, permissões, ACLs ...
  • funciona bem com links simbólicos, não quebra links físicos
  • ele não deixa os arquivos temporários caídos quando mortos.

Uma desvantagem é que, se for interrompido, o arquivo acabará sendo parcialmente traduzido (nesse caso, você pode executá-lo novamente para finalizá-lo). Algumas sedimplementações lidariam com isso corretamente, garantindo que o arquivo original permanecesse inalterado, a menos que o comando tenha êxito.

Stéphane Chazelas
fonte
3
Tenha cuidado em executar novamente a tradução se você tiver recursão nos conjuntos de traduções, por exemplo echo 'abc' | tr ab bx.
n.st
1
@ n.st, sim, é por isso que eu disse neste caso , embora eu concorde que vale a pena explicar .
Stéphane Chazelas
No final, eu tive que trabalhar com arquivos temporários, afinal: gist.github.com/n-st/048facd0c12f105ac122030fb58b962f - Os caracteres multibyte impossibilitavam o uso do GNU tre em nosso ambiente PXE com muitos links simbólicos, sed -iera uma espera estragada acontecer…: /
n.st 13/09/17
@ n.st, iconv -t cp437parece mais apropriado para isso.
Stéphane Chazelas
iconvquebra quando o arquivo de entrada já contém bytes codificados em cp437 ou uma mistura de várias codificações. Portanto, embora seja preferível no caso geral, é mais robusto fazer substituições manuais nesse caso.
N
4

Como outra alternativa, se o seu principal problema for a falta de suporte para a alteração de arquivos no local, você pode estar interessado na spongeferramenta do pacote moreutils :

tr 'ots' 'u.x' < file | sponge file

gravará em file, mas só será aberto filepara gravação quando a entrada estiver concluída. Na página de manual :

spongelê a entrada padrão e a grava no arquivo especificado. Ao contrário de um redirecionamento de shell, a esponja absorve toda a sua entrada antes de abrir o arquivo de saída. Isso permite a construção de pipelines que leem e gravam no mesmo arquivo.

A menos que você tenha arquivos muito grandes que não possam ser armazenados na memória, eles spongepodem funcionar para você.

mindriot
fonte
2
Um problema com spongeé que ele ainda substitui filese trfalhar (por exemplo, se você tinha gravação, mas não acesso de leitura file)
Stéphane Chazelas
Oh, de fato faz; Eu não esperava isso. Obrigado.
mindriot
Veja o cat file >; fileoperador do ksh93 que grava a saída em um arquivo temporário que é renomeado para o destino somente se o comando for bem-sucedido (mas sed -i, por exemplo, isso cria um novo arquivo em vez de substituir o original).
Stéphane Chazelas