Estou aprendendo sed. Tudo parecia estar indo bem até eu encontrar o N (várias linhas a seguir). Eu criei este arquivo (guide.txt) para fins de prática / compreensão / contexto. Aqui está o conteúdo do referido arquivo ...
This guide is meant to walk you through a day as a Network
Administrator. By the end, hopefully you will be better
equipped to perform your duties as a Network Administrator
and maybe even enjoy being a Network Administrator that much more.
Network Administrator
Network Administrator
I'm a Network Administrator
Portanto, meu objetivo é substituir TODAS as instâncias do "Administrador de Rede" por "Usuário do Sistema". Como a primeira instância do "Administrador de rede" é separada por uma nova linha (\ n), preciso do próximo operador de várias linhas (N) para acrescentar a linha que começa com "Administrador" e a linha anterior que termina com "Rede \ n" . Sem problemas. Mas também quero capturar todas as outras instâncias de linha única "Administrador de rede".
Com minha pesquisa, aprendi que precisarei de dois comandos de substituição; um para a sequência separada por nova linha e um para os outros. Além disso, há alguma ação acontecendo por causa da última linha que contém a correspondência de substituição e a linha múltipla a seguir. Então eu faço isso ...
$ sed '
> s/Network Administrator/System User/
> N
> s/Network\nAdministrator/System\nUser/
> ' guide.txt
Isso retorna esses resultados ...
This guide is meant to walk you through a day as a System
User. By the end, hopefully you will be better
equipped to perform your duties as a System User
and maybe even enjoy being a Network Administrator that much more.
System User
Network Administrator
I'm a System User
Eu pensei que a substituição de linha única pegaria todas as instâncias "normais" de "Administrador de Rede" e a trocaria por "Usuário do sistema", enquanto a instrução de várias linhas funcionaria sua mágica na instância separada de nova linha, mas como você posso vê-lo retornado, o que considero, resultados inesperados.
Depois de algumas brincadeiras, eu cheguei aqui ...
$ sed '
> s/Network Administrator/System User/
> N
> s/Network\nAdministrator/System\nUser/
> s/Network Administrator/System User/
> ' guide.txt
E voilà, eu recebo a saída desejada de ...
This guide is meant to walk you through a day as a System
User. By the end, hopefully you will be better
equipped to perform your duties as a System User
and maybe even enjoy being a System User that much more.
System User
System User
I'm a System User
Por que isso funciona e o script sed original não? Eu realmente quero entender isso.
Agradecemos antecipadamente por qualquer ajuda.
Respostas:
Como você está aprendendo
sed
, aproveitarei o tempo para adicionar a resposta de @ John1024:1) Observe que você está usando
\n
a string de substituição. Isso funciona no GNUsed
, mas não faz parte do POSIX, portanto, ele insere uma barra invertida e umn
em muitos outrossed
s (o uso\n
do padrão é portátil, btw).Em vez disso, sugiro fazer
s/Network\([[:space:]]\)Administrator/System\1User/g
: O[[:space:]]
corresponderá a nova linha ou espaço em branco, para que você não precise de doiss
comandos, mas combine-os em um. Ao cercá-lo,\(...\)
você pode se referir a ele na substituição: O\1
será substituído pelo que corresponder no primeiro par de\(\)
.2) Para combinar corretamente os padrões em duas linhas, você deve conhecer o
N;P;D
padrão:O
N
é sempre acrescentar a próxima linha (exceto para a última linha, é por isso que é "dirigida" com$!
(= se não for última linha, você deve sempre considerar a precederN
com$!
para evitar que acidentalmente terminando o script) Em seguida, após a substituição dos.P
Só imprime a primeira linha no espaço do padrão eD
exclui essa linha e inicia o próximo ciclo com os restos do espaço do padrão (sem ler a próxima linha), provavelmente o que você pretendia originalmente.Lembre-se deste padrão, você frequentemente precisará dele.
3) Outro padrão útil para a edição de várias linhas, especialmente quando mais de duas linhas estão envolvidas: mantenha a coleta de espaço, como sugeri a John:
Repito para explicar:
H
acrescenta cada linha ao espaço de espera. Como isso resultaria em uma nova linha extra antes da primeira linha, a primeira linha precisa ser movida em vez de anexada1h
. O seguinte$!d
significa "para todas as linhas, exceto a última, exclua o espaço do padrão e comece novamente". Assim, o restante do script é executado apenas para a última linha. Nesse ponto, o arquivo inteiro é coletado no espaço de espera (portanto, não use isso para arquivos muito grandes!) E og
move para o espaço do padrão, para que você possa fazer todas as substituições de uma só vez, como pode com a-z
opção de GNUsed
.Esse é outro padrão útil que sugiro ter em mente.
fonte
Primeiro, observe que sua solução realmente não funciona. Considere este arquivo de teste:
E, em seguida, execute o comando:
O problema é que o código não substitui o último
Network\nAdministrator
.Esta solução funciona:
Também podemos aplicar isso ao seu
guide.txt
:A chave é continuar lendo as linhas até encontrar uma que não termine com
Network
. Quando isso é feito, as substituições podem ser feitas.Nota de compatibilidade: Todos os itens acima são usados
\n
no texto de substituição. Isso requer GNU sed. Não funcionará no BSD / OSX sed.[Gorjeta de chapéu para Philippos .]
Versão multilinha
Se isso ajudar a esclarecer, aqui está o mesmo comando dividido em várias linhas:
Como funciona
:a
Isso cria um rótulo
a
./Network$/{ $!{N;ba} }
Se esta linha terminar com
Network
, então, se esta não for a última linha ($!
), leia e acrescente a próxima linha (N
) e ramifique novamente para o rótuloa
(ba
).s/Network\nAdministrator/System\nUser/g
Faça a substituição com a nova linha intermediária.
s/Network Administrator/System User/g
Faça a substituição com o espaço em branco intermediário.
Solução mais simples (apenas GNU)
Com o GNU sed ( não o BSD / OSX), precisamos apenas de um comando substituto:
E no
guide.txt
arquivo:Nesse caso,
-z
diz ao sed para ler até o primeiro caractere NUL. Como os arquivos de texto nunca têm um caractere nulo, isso tem o efeito de ler o arquivo inteiro de uma só vez. Podemos então fazer a substituição sem nos preocupar em perder uma linha.Esse método não é bom se o arquivo for enorme (geralmente significa gigabytes). Se for muito grande, a leitura de tudo de uma só vez pode sobrecarregar a RAM do sistema.
Solução que funciona tanto no GNU quanto no BSD sed
Conforme sugerido por Phillipos , a seguir é uma solução portátil:
fonte
Network Administrator
estiver dividido entre a primeira e a segunda linha desse par, sua solução fará a substituição com êxito. Em seguida, imprime essas duas linhas e lê o próximo par. Se, no entanto, a segunda linha do primeiro par terminar comNetwork
e a primeira linha do segundo par começarAdministrator
, o código estará ausente. Meu código evita isso lendo linhas, até encontrar um que não termine comNetwork
.sed
: A\n
substituição não está definida no padrão.sed 'H;1h;$!d;x;s/Network\([[:space:]]\)Administrator/System\1User/g'
é uma maneira portátil de fazer isso.