Como inserir uma nova linha na frente de um padrão?


138

Como inserir uma nova linha antes de um padrão em uma linha?

Por exemplo, isso inserirá uma nova linha atrás do padrão regex.

sed 's/regex/&\n/g'

Como posso fazer o mesmo, mas na frente do padrão?

Dado esse arquivo de entrada de amostra, o padrão para combinar é o número de telefone.

some text (012)345-6789

Deve se tornar

some text
(012)345-6789


1
@NilsvonBarth, por que uma pergunta direta é uma pergunta ruim?
21419 Josh

Respostas:


177

Isso funciona bashe é zshtestado no Linux e OS X:

sed 's/regexp/\'$'\n/g'

Em geral, para $seguido por uma string literal entre aspas simples bashrealiza a substituição da barra invertida no estilo C, por exemplo, $'\t'é traduzida para uma guia literal. Além disso, o sed deseja que seu literal de nova linha seja escapado com uma barra invertida, daí o \anterior $. E, finalmente, o próprio sinal de cifrão não deve ser citado para que seja interpretado pelo shell; portanto, fechamos a cotação antes da $e depois a abrimos novamente.

Edit : Como sugerido nos comentários por @ mklement0, isso também funciona:

sed $'s/regexp/\\\n/g'

O que acontece aqui é: todo o comando sed agora é uma string no estilo C, o que significa que a barra invertida que o sed precisa ser colocada antes que a nova linha literal agora seja escapada com outra barra invertida. Embora seja mais legível, nesse caso, você não poderá fazer substituições de string de shell (sem torná-lo feio novamente).


7
Isso me dá "nova linha sem escape dentro do padrão substituto" no OSX.
Matt Gibson

@ Matt Gibson, isso é muito estranho, porque "nova linha sem escape" é fornecida apenas quando você tem uma nova linha real sem uma barra invertida dentro do padrão de substituição. Meu código acima também funciona em alguns outros shells, por exemplo, zsh, ksh.
Mojuba

3
@ Matt Gibson ... ou se você esquecer a barra invertida antes de '$' \ n no meu código.
Mojuba

7
Conforme escritas, essas expressões substituem completamente a regex por uma nova linha, em vez de inserir uma nova linha no meio de uma linha existente, conforme solicitado. Veja como eu usei uma forma modificada de esta resposta para inserir uma nova linha entre dois padrões encontrados: sed '\(first match\)\(second match\)/\1\'$'\n''\2/g'. Observe as duas aspas simples após o \ n. O primeiro fecha a $seção " " para que o restante da linha não seja afetado por ele. Sem essas aspas, o \ 2 foi ignorado.
David Ravetti

12
Outra opção é usar uma única seqüência ANSI C-citada : sed $'s/regexp/\\\n/g', o que melhora a legibilidade - a única ressalva é que você precisa então de dobrar todos os literais \ caracteres.
mklement0

43

Algumas das outras respostas não funcionaram na minha versão do sed. Mudar a posição &e \nfuncionou.

sed 's/regexp/\n&/g' 

Editar: parece não funcionar no OS X, a menos que você instale gnu-sed.


9
Não tenho certeza se isso funciona em todas as versões do sed. Eu tentei isso no meu Mac eo \ n só fica de saída como 'n'
Todd Gamblin

3
Passei 15 minutos no mac no meu trabalho, antes de ler sua resposta. Vai a Apple!
9777 Rick7

1
Para aqueles que usam homebrew: brew install gnu-sedseguido porgsed 's/regexp/\n&/g'
aaaaaa 5/05

1
... seguido porecho 'alias sed=gsed' >> ~/.bashrc
Proximo

36

No sed, você não pode adicionar novas linhas no fluxo de saída com facilidade. Você precisa usar uma linha de continuação, o que é estranho, mas funciona:

$ sed 's/regexp/\
&/'

Exemplo:

$ echo foo | sed 's/.*/\
&/'

foo

Veja aqui para detalhes. Se você quiser algo um pouco menos estranho, tente usar perl -pecom grupos de correspondência em vez de sed:

$ echo foo | perl -pe 's/(.*)/\n$1/'

foo

$1 refere-se ao primeiro grupo correspondente na expressão regular, onde os grupos estão entre parênteses.


Por que você diz que não pode adicionar novas linhas? Você pode simplesmente fazer o sed 's / regexp / & \ n / g' É isso aí
Andres

2
Este é o menos coisa olhando hacky que você pode fazer em um Mac para inserir nova linha (\ n não funciona em um Mac)
Pylinux

A versão perl pode ser modificado para fazer em edição lugarperl -pi -e 's/(.*)/\n$1/' foo
epónimo

2
@Andres: (principalmente) implementações Sed somente de recursos POSIX, como a versão BSD que também vem com o OS X, não suportam sequências de escape de caracteres de controle na parte de substituição de uma schamada de função (ao contrário da implementação GNU Sed, que faz) . A resposta acima funciona com ambas as implementações; para uma visão geral de todas as diferenças, veja aqui .
mklement0

29

No meu mac, o seguinte insere um único 'n' em vez de nova linha:

sed 's/regexp/\n&/g'

Isso substitui a nova linha:

sed "s/regexp/\\`echo -e '\n\r'`/g"

Eu estava editando em linha sed -i '' -e ...e estava tendo problemas com um ^Mcursor M (ctrl + m) sendo gravado no arquivo. Acabei usando perl com os mesmos parâmetros.
Steve Tauber

2
Por favor, esteja ciente do fato de que o segundo código insere um código especial de nova linha LF CR (reverso do MS-DOS CR LF)! Os sistemas operacionais Unix e Mac OS X usam apenas LF ( \n).
Pabouk

Algo mais na minha expressão sed estava causando tanta infelicidade (apesar de funcionar bem sem a echo...e nova linha) que eu fiz isso no vim.
Ahmed Fasih

1
Ou simplesmente: sed "s/regexp/`echo`/g"- o que irá produzir um único LF vez de LF-CR
mojubá

2
@mojuba: No: `echo`resultará na string vazia , porque as substituições de comandos invariavelmente cortam todas as novas linhas à direita. Não há como usar uma substituição de comando para inserir diretamente uma única nova linha (e inserir \n\r- isto é, um CR adicional - é uma péssima idéia).
mklement0

15
echo one,two,three | sed 's/,/\
/g'

1
+1 funcionou perfeitamente e muito stright-forward / fácil de lembrar
gMale

2
Esta resposta é realmente uma solução sed , em vez de uma solução bash . Qualquer coisa que use construções como $'\n'está contando com o shell para gerar a nova linha. Tais soluções podem não ser portáteis. Esta está. Claro, também é uma duplicata do segundo exemplo na resposta do tgamblin de 2009.
ghoti

10

Nesse caso, eu não uso sed. Eu uso tr.

cat Somefile |tr ',' '\012' 

Isso pega a vírgula e a substitui pelo retorno de carro.


1
Achei que isso também funciona: cat Somefile | tr ',' '\n'YMMV
LS

9

Você pode usar one-liners perl da mesma forma que o sed, com a vantagem da expressão regular completa do perl suporte (que é muito mais poderoso do que o obtido com o sed). Também há muito pouca variação entre as plataformas * nix - o perl geralmente é o perl. Portanto, você pode parar de se preocupar em como fazer com que a versão do sed do seu sistema específico faça o que você deseja.

Nesse caso, você pode fazer

perl -pe 's/(regex)/\n$1/'

-pe coloca o perl em um loop "execute and print", bem como o modo normal de operação do sed.

' cita todo o resto para que o shell não interfira

()ao redor do regex é um operador de agrupamento. $1no lado direito da substituição, imprime o que foi encontrado dentro desses parênteses.

Finalmente, \né uma nova linha.

Independentemente de você estar usando parênteses como um operador de agrupamento, você precisa escapar dos parênteses que está tentando corresponder. Portanto, uma regex para corresponder ao padrão listado acima seria algo como

\(\d\d\d\)\d\d\d-\d\d\d\d

\(ou \)corresponde a um paren literal e \dcorresponde a um dígito.

Melhor:

\(\d{3}\)\d{3}-\d{4}

Eu imagino que você possa descobrir o que os números nos aparelhos estão fazendo.

Além disso, você pode usar delimitadores diferentes de / para seu regex. Portanto, se você precisar corresponder / não precisará escapar dela. Qualquer uma das opções abaixo é equivalente à regex no início da minha resposta. Em teoria, você pode substituir qualquer caractere pelos padrões.

perl -pe 's#(regex)#\n$1#'
perl -pe 's{(regex)}{\n$1}'

Algumas considerações finais.

usando em -nevez de -peagir da mesma forma, mas não é impresso automaticamente no final. Pode ser útil se você quiser imprimir sozinho. Por exemplo, aqui está um grep-alike ( m/foobar/é uma correspondência de regex):

perl -ne 'if (m/foobar/) {print}'

Se você acha que lidar com novas linhas é problemático e deseja que ele seja tratado magicamente por você, adicione -l. Não é útil para o OP, que estava trabalhando com novas linhas, no entanto.

Dica de bônus - se você tiver o pacote pcre instalado, ele vem com ele pcregrep, que usa expressões regulares compatíveis com perl.


4

Hmm, as novas linhas que escaparam parecem funcionar em versões mais recentes do sed(eu tenho o GNU sed 4.2.1),

dev:~/pg/services/places> echo 'foobar' | sed -r 's/(bar)/\n\1/;'
foo
bar

1
Como mencionado, isso funciona com várias versões do GNU sed, mas não o sed incluído no macOS.
LS

4
echo pattern | sed -E -e $'s/^(pattern)/\\\n\\1/'

funcionou bem no El Captitan com o ()apoio


Isso funcionou muito bem e você até dá um comando completo para testar e extrapolar para se especializar para o próprio propósito. Bom trabalho!
jxramos

3

Para inserir uma nova linha no fluxo de saída no Linux, usei:

sed -i "s/def/abc\\\ndef/" file1

Onde file1estava:

def

Antes da substituição no local sed e:

abc
def

Após a substituição no local sed. Observe o uso de \\\n. Se os padrões tiverem um "interior, escape usando \".


Para mim, o código acima não funciona. sedinsere em \nvez de LF porque obtém \\no parâmetro do shell. --- Este código funciona: sed -i "s/def/abc\ndef/" file1. --- GNU sed version 4.2.1, GNU bash, version 4.1.2(1) / 4.2.25(1)(versão do CentOS 6.4 / Ubuntu 12.04.3).
Pabouk

2

no sed, você pode fazer referência a grupos no seu padrão com "\ 1", "\ 2", .... portanto, se o padrão que você procura é "PATTERN" e deseja inserir "ANTES" na frente dele , você pode usar, sem escapar

sed 's/(PATTERN)/BEFORE\1/g'

ie

  sed 's/\(PATTERN\)/BEFORE\1/g'

Acabei de fazer: testfile contents = "ABC ABC ABC". Ran "sed 's / \ (ABC \) / \ n \ 1 / g' testfile, recebi as novas linhas. Experimente as escapadas, tente adicionar uma coisa de cada vez ao seu padrão, por exemplo, verifique se você está correspondendo ao teste padrão, em seguida, verificar a correspondência do grupo, em seguida, adicione a verificação de nova linha.
Steve B.

Eu apenas tentei exatamente isso e consegui "nabc nabc nabc' Você está usando alguma outra versão do sed.?
Todd Gamblin

a fuga de conchas provavelmente está atrapalhando as tentativas de tgamblin. colocar os argumentos completos do sed em aspas simples como Steve B deve corrigir isso. É possível, porém, que versões diferentes do sed não entendam \ n para nova linha.
Dan Pritts

2

Você também pode fazer isso com o awk, usando -vpara fornecer o padrão:

awk -v patt="pattern" '$0 ~ patt {gsub(patt, "\n"patt)}1' file

Isso verifica se uma linha contém um determinado padrão. Nesse caso, ele anexa uma nova linha ao início.

Veja um exemplo básico:

$ cat file
hello
this is some pattern and we are going ahead
bye!
$ awk -v patt="pattern" '$0 ~ patt {gsub(patt, "\n"patt)}1' file
hello
this is some 
pattern and we are going ahead
bye!

Observe que isso afetará todos os padrões em uma linha:

$ cat file
this pattern is some pattern and we are going ahead
$ awk -v patt="pattern" '$0 ~ patt {gsub(patt, "\n"patt)}1' d
this 
pattern is some 
pattern and we are going ahead

1
o que o 1 faz nisso?
precisa saber é o seguinte

1
@whatahitson 1é usado no Awk como uma abreviação de {print $0}. O motivo é que qualquer condição avaliada como True dispara a ação padrão do Awk, que consiste em imprimir o registro atual.
fedorqui 'SO parar de prejudicar'

1

Isso funciona no MAC para mim

sed -i.bak -e 's/regex/xregex/g' input.txt sed -i.bak -e 's/qregex/\'$'\nregex/g' input.txt

Dono, se é o perfeito ...


1

Depois de ler todas as respostas para essa pergunta, ainda me levou muitas tentativas para obter a sintaxe correta para o seguinte script de exemplo:

#!/bin/bash
# script: add_domain
# using fixed values instead of command line parameters $1, $2
# to show typical variable values in this example
ipaddr="127.0.0.1"
domain="example.com"
# no need to escape $ipaddr and $domain values if we use separate quotes.
sudo sed -i '$a \\n'"$ipaddr www.$domain $domain" /etc/hosts

O script anexa uma nova linha \nseguida por outra linha de texto ao final de um arquivo usando um único sedcomando.


1
sed -e 's/regexp/\0\n/g'

\ 0 é nulo; portanto, sua expressão é substituída por nulo (nada) e depois ...
\ n é a nova linha

Em alguns sabores do Unix não funciona, mas acho que é a solução para o seu problema.

echo "Hello" | sed -e 's/Hello/\0\ntmow/g'
Hello
tmow

0

No vi da Red Hat, eu pude inserir retornos de carro usando apenas o caractere \ r. Acredito que isso execute internamente 'ex' em vez de 'sed', mas é semelhante e o vi pode ser outra maneira de fazer edições em massa, como patches de código. Por exemplo. Estou cercando um termo de pesquisa com uma instrução if que insiste em retornos de carro após as chaves:

:.,$s/\(my_function(.*)\)/if(!skip_option){\r\t\1\r\t}/

Observe que eu também pedi para inserir algumas guias para melhorar o alinhamento das coisas.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.