Percebi que, se eu adicionar \n
um padrão para substituir usando sed
, ele não corresponde. Exemplo:
$ cat > alpha.txt
This is
a test
Please do not
be alarmed
$ sed -i'.original' 's/a test\nPlease do not/not a test\nBe/' alpha.txt
$ diff alpha.txt{,.original}
$ # No differences printed out
Como posso fazer isso funcionar?
sed
regular-expression
utilities
Belmin Fernandez
fonte
fonte
Respostas:
No chamado mais simples do sed , ele tem uma linha de texto no espaço padrão, ou seja. 1 linha de
\n
texto delimitado da entrada. A única linha no espaço do padrão não tem\n
... É por isso que o seu regex não está encontrando nada.Você pode ler várias linhas no espaço do padrão e manipular as coisas surpreendentemente bem, mas com um esforço mais do que o normal. O Sed tem um conjunto de comandos que permitem esse tipo de coisa ... Aqui está um link para um Resumo de Comando do sed . É o melhor que eu encontrei e me fez rolar.
No entanto, esqueça a idéia "one-liner" quando você começar a usar os micro comandos do sed. É útil apresentá-lo como um programa estruturado até você sentir a sensação ... É surpreendentemente simples e igualmente incomum. Você pode pensar nisso como a "linguagem assembler" da edição de texto.
Resumo: Use sed para coisas simples, e talvez um pouco mais, mas, em geral, quando vai além de trabalhar com uma única linha, a maioria das pessoas prefere outra coisa ...
Vou deixar outra pessoa sugerir outra coisa. realmente não tenho certeza qual seria a melhor opção (eu usaria o sed, mas é porque não conheço o perl o suficiente).
Aqui está o mesmo script, condensado no que é obviamente mais difícil de ler e trabalhar, mas alguns chamariam duvidosamente de uma linha
Aqui está o meu comando "cheat-sheet"
fonte
t
comando aqui - quando não recebe um rótulo, o padrão é ramificar para o final do script. O mesmosed '/^a test$/{$!{N;s/^a test\nPlease do not$/not a test\nBe/;t;P;D}}' alpha.txt
acontece exatamente com o seu comando em todas as circunstâncias. Obviamente, para esse arquivo em particular ,sed '/test/{N;s/.*/not a test\nBe/}' alpha.txt
faz o mesmo, mas meu primeiro exemplo é logicamente equivalente para todos os arquivos possíveis. Observe também que\n
em uma sequência de substituição não produz uma nova linha; você precisa de uma barra invertida `\` seguida por uma nova linha real para fazer isso.#
comando não separado do anterior,\n
no RHS des
). Com o GNU,sed
você também pode usar-z
para usar registros delimitados por NUL (e depois absorver toda a entrada se for texto (que por definição não contém NULs)).Use em
perl
vez desed
:-pi -e
é a sequência padrão da linha de comando "substituir no local" e -0777 faz com que o perl elimine os arquivos inteiros. Veja perldoc perlrun para descobrir mais sobre isso.fonte
sed
e responda usando awk ou perl. Eu acho que não é sobre o assunto, portanto, desculpe, mas eu demiti um menos.sed
resposta acima prova que uma resposta Perl está no tópico.Eu acho que é melhor substituir o
\n
símbolo por outro símbolo e depois trabalhar como de costume:por exemplo, código fonte não trabalhado:
pode ser alterado para:
Se alguém não souber, o
\n
final da linha UNIX é o\r\n
Windows\r
clássico do Mac OS. O texto normal do UNIX não usa\r
símbolo; portanto, é seguro usá-lo neste caso.Você também pode usar algum símbolo exótico para substituir temporariamente \ n. Como exemplo - \ f (símbolo de avanço de formulário). Você pode encontrar mais símbolos aqui .
fonte
\r
argumento porsed
por$(printf '\r')
.$
antes da string sed para impedir que ela converta o\r
para umr
. Exemplo curto:sed $'s/\r/~/'
. Exemplo completo:cat alpha.txt | tr '\n' '\r' | sed $'s/a test\rPlease do not/not a test\rBe/' | tr '\r' '\n'
Considerando tudo, devorar o arquivo inteiro pode ser o caminho mais rápido.
A sintaxe básica é a seguinte:
Lembre-se, devorar o arquivo inteiro pode não ser uma opção se o arquivo for tremendamente grande. Para esses casos, outras respostas fornecidas aqui oferecem soluções personalizadas que garantem o trabalho em um pequeno espaço de memória.
Para todas as outras situações de hack e slash, apenas o prefixo
-e '1h;2,$H;$!d;g'
seguido pelosed
argumento regex original praticamente faz o trabalho.por exemplo
O que
-e '1h;2,$H;$!d;g'
faz?A
1
,2,$
,$!
partes são linha especificadores que limite que linhas a seguinte directamente comando é executado.1
: Apenas primeira linha2,$
: Todas as linhas começando a partir do segundo$!
: Todas as linhas, exceto a últimaTão expandido, é o que acontece em cada linha de uma entrada de linha N.
O
g
comando não recebe um especificador de linha, mas od
comando anterior possui uma cláusula especial " Iniciar próximo ciclo " e isso impede ag
execução em todas as linhas, exceto a última.Quanto ao significado de cada comando:
h
seguido porH
s em cada linha copia as referidas linhas de entrada no espaço de espera desed
s . (Pense em um buffer de texto arbitrário.)d
descarta cada linha para impedir que essas linhas sejam gravadas na saída. O espaço de espera, no entanto, é preservado.g
restaura o acúmulo de todas as linhas do espaço de espera, para quesed
seja possível executar sua regex em toda a entrada (em vez de ser linha por vez) e, portanto, poder combinar em\n
s.fonte
sed
tem três comandos para gerenciar operações de multi-linha:N
,D
eP
(compará-los com normalidaden
,d
ep
).Nesse caso, você pode corresponder à primeira linha do seu padrão, use
N
para anexar a segunda linha ao espaço do padrão e, em seguida, uses
para fazer sua substituição.Algo como:
fonte
G
,H
,x
...). Mais linhas podem ser adicionadas ao espaço do padrão com os
comando também.N
comandosVocê pode, mas é difícil . Eu recomendo mudar para uma ferramenta diferente. Se houver uma expressão regular que nunca corresponda a nenhuma parte do texto que você deseja substituir, você poderá usá-la como um separador de registros do awk no GNU awk.
Se nunca houver duas linhas novas na sequência de caracteres de pesquisa, você poderá usar o "modo de parágrafo" do awk (uma ou mais linhas em branco separam os registros).
Uma solução fácil é usar Perl e carregar o arquivo completamente na memória.
fonte
perl -0777 -pe '…' <input-file >output-file
. Para modificar um arquivo no lugar, #perl -0777 -i -pe '…' filename
sed
's-z
opção (adicionado em 2012 depois que a resposta foi publicado):seq 10 | sed -z 's/4\n5/a\nb/'
.Eu acho que esta é a solução sed para duas linhas correspondentes.
Se você quiser 3 linhas correspondentes, então ...
Se você quiser 4 linhas correspondentes, então ...
Se a peça de substituição no comando "s" encolher linhas, será um pouco mais complicado assim
Se a parte de substituição crescer linhas, será um pouco mais complicado assim
fonte
Aqui
/a test/,/Please do not/
é considerado como um bloco de texto (multilinhas),c
é o comando change seguido por um novo textonot a test \nBe
No caso de o texto a ser substituído ser muito longo, sugiro a sintaxe ex .
fonte
Apenas amplie sua janela na entrada um pouco.
É bem fácil. Além da substituição padrão; você só precisa
$!N
,P
eD
aqui.fonte
Além do Perl, uma abordagem geral e útil para edição de múltiplas linhas para fluxos (e arquivos também) é:
Primeiro, crie um novo separador de linhas UNIQUE como desejar, por exemplo
Em seu comando sed (ou qualquer outra ferramenta), você substitui \ n por $ {S}, como
(o awk substitui o separador de linhas ASCII pelo seu e vice-versa.)
fonte
Esta é uma pequena modificação da resposta inteligente do xara para fazê-lo funcionar no OS X (estou usando o 10.10):
Em vez de usar explicitamente
\r
, você deve usar$(printf '\r')
.fonte
printf '\r'
(ouecho -e '\r'
) funciona corretamente, observe que você pode apenas usar a sintaxe do shell$'\r'
para se referir a literais de escape. Por exemplo,echo hi$'\n'there
ecoará uma nova linha entrehi
ethere
. Da mesma forma, você pode agrupar a sequência inteira para que cada barra invertida\
escape do caractere subseqüente:echo $'hi\nthere'
Eu queria adicionar algumas linhas de HTML a um arquivo usando sed (e acabei aqui). Normalmente eu usava perl, mas estava na caixa que tinha sed, bash e não muito mais. Eu descobri que se eu alterasse a string para uma única linha e deixasse bash / sed interpolar o \ t \ n tudo funcionava:
Seria mais limpo ter uma função para escapar das aspas duplas e das barras, mas às vezes a abstração é a ladra do tempo.
fonte
O GNU
sed
tem uma-z
opção que permite usar a sintaxe que o OP tentou aplicar. ( página de manual )Exemplo:
Esteja ciente: se você usar
^
e$
agora eles correspondem ao início e ao fim das linhas delimitadas com um caractere NUL (não\n
). E, para garantir que as correspondências em todas as suas\n
linhas ( separadas) sejam substituídas, não se esqueça de usar og
sinalizador para substituições globais (por exemplos/.../.../g
).Créditos: @ stéphane-chazelas mencionou pela primeira vez -z em um comentário acima.
fonte
Sed quebra a entrada em novas linhas. Ele mantém apenas uma linha por loop.
Portanto, não há como corresponder a
\n
(nova linha) se o espaço do padrão não o contiver.Há uma maneira, porém, de fazer o sed manter duas linhas consecutivas no espaço do padrão usando o loop:
Adicione qualquer processamento necessário entre N e P (substituindo o
l
).Nesse caso (2 linhas):
Ou, por três linhas:
Isso pressupõe que a mesma quantidade de linhas seja substituída.
fonte