Inserir linha após a primeira partida usando sed


245

Por alguma razão, não consigo encontrar uma resposta direta para isso e estou com um pouco de dificuldade no momento. Como eu inseri uma linha de texto de escolha após a primeira linha que corresponde a uma sequência específica usando o sedcomando Eu tenho ...

CLIENTSCRIPT="foo"
CLIENTFILE="bar"

E eu quero inserir uma linha após a CLIENTSCRIPT=linha, resultando em ...

CLIENTSCRIPT="foo"
CLIENTSCRIPT2="hello"
CLIENTFILE="bar"

Respostas:


379

Tente fazer isso usando o GNU sed:

sed '/CLIENTSCRIPT="foo"/a CLIENTSCRIPT2="hello"' file

se você quiser substituir no local , use

sed -i '/CLIENTSCRIPT="foo"/a CLIENTSCRIPT2="hello"' file

Resultado

CLIENTSCRIPT="foo"
CLIENTSCRIPT2="hello"
CLIENTFILE="bar"

Doc


Obrigado! E fazê-lo gravar novamente no arquivo sem imprimir o conteúdo do arquivo?
usar o seguinte comando

11
Note que ambos assumem o GNU sed. Essa não é a sedsintaxe padrão e não funcionará com nenhuma outra sedimplementação.
Stephane Chazelas

Eu tive problemas para fazer isso funcionar para mim porque estava usando um delimitador diferente. Após o RTFM'ing, percebi que tinha que iniciar o regex com a \ e depois o delimitador.
Christy

10
Como isso se aplica APENAS à primeira partida? é claro que ele anexa o texto após a partida, mas como ele sabe apenas da primeira partida?
Mohammed Noureldin

7
Isso adiciona o texto após cada partida, para mim.
Schoppenhauer

49

Observe a sedsintaxe padrão (como no POSIX, suportada por todas as sedimplementações em conformidade (GNU, OS / X, BSD, Solaris ...)):

sed '/CLIENTSCRIPT=/a\
CLIENTSCRIPT2="hello"' file

Ou em uma linha:

sed -e '/CLIENTSCRIPT=/a\' -e 'CLIENTSCRIPT2="hello"' file

( -expressions (e o conteúdo de -files) são associados a novas linhas para compor as sedinterpretações do script sed ).

A -iopção para edição in-loco também é uma extensão GNU, algumas outras implementações (como o FreeBSD) suportam -i ''isso.

Como alternativa, para portabilidade, você pode usar perl:

perl -pi -e '$_ .= qq(CLIENTSCRIPT2="hello"\n) if /CLIENTSCRIPT=/' file

Ou você pode usar edou ex:

printf '%s\n' /CLIENTSCRIPT=/a 'CLIENTSCRIPT2="hello"' . w q | ex -s file

2
Posso estar errado, mas a corrente sed -e '/CLIENTSCRIPT=/a\' -e 'CLIENTSCRIPT2="hello"' fileescapa da citação no final do primeiro parâmetro e quebra o comando.
Carrinho abandonado

1
@AbandonedCart, nas conchas dos Bourne, cshou rcfamília, '...'são citações fortes dentro das quais a barra invertida não é especial. A única exceção que eu sei é o fishshell.
Stephane Chazelas

1
Terminal no MacOS (e subsequentemente um script executado no terminal) também é uma exceção, aparentemente. Encontrei sintaxe alternativa, mas obrigado de qualquer maneira.
Carro abandonado

1
@AbandonedCart, isso é outra coisa. O macOS sednão é compatível com POSIX aqui. Isso faz com que minha afirmação sobre ele seja portátil seja incorreta (para a variante de uma linha). Pedirei confirmação ao grupo aberto se é de fato uma não conformidade ou uma interpretação incorreta do padrão da minha parte.
Stephane Chazelas

19

Um compatível com POSIX usando o scomando:

sed '/CLIENTSCRIPT="foo"/s/.*/&\
CLIENTSCRIPT2="hello"/' file

1
... que suporta até a inserção de novas linhas. Eu amo isso!
Stephan Henningsen

1
Consulte também o sed -e '/.../s/$/\' -e 'CLI.../'que evitaria problemas com linhas que contêm seqüências de bytes que não formam caracteres válidos.
Stephane Chazelas

14

Comando Sed que funciona no MacOS (pelo menos, no OS 10) e no Unix (ou seja, não requer gnu sed como o de Gilles (atualmente aceito)):

sed -e '/CLIENTSCRIPT="foo"/a\'$'\n''CLIENTSCRIPT2="hello"' file

Isso funciona no bash e talvez em outros shells que também conhecem o estilo de cotação de avaliação $ '\ n'. Tudo pode estar em uma linha e funcionar em comandos / POSIX sed mais antigos. Se houver várias linhas correspondentes ao CLIENTSCRIPT = "foo" (ou seu equivalente) e você desejar adicionar apenas a linha extra na primeira vez, poderá refazê-la da seguinte maneira:

sed -e '/^ *CLIENTSCRIPT="foo"/b ins' -e b -e ':ins' -e 'a\'$'\n''CLIENTSCRIPT2="hello"' -e ': done' -e 'n;b done' file

(isso cria um loop após o código de inserção de linha que apenas percorre o restante do arquivo, nunca voltando ao primeiro comando sed novamente).

Você pode notar que adicionei um '^ *' ao padrão correspondente, caso essa linha apareça em um comentário, digamos, ou seja recuada. Não é 100% perfeito, mas cobre algumas outras situações que provavelmente serão comuns. Ajuste conforme necessário ...

Essas duas soluções também contornam o problema (para a solução genérica de adicionar uma linha) de que, se a nova linha inserida contiver barras invertidas ou e comercial sem escape, elas serão interpretadas pelo sed e provavelmente não sairão da mesma forma, como \nsão - por exemplo. \0seria a primeira linha correspondente. Especialmente útil se você estiver adicionando uma linha que vem de uma variável em que você teria que escapar de tudo primeiro usando $ {var //} antes ou outra instrução sed etc.

Essa solução é um pouco menos confusa nos scripts (que citar e \ n não é fácil de ler), quando você não deseja colocar o texto de substituição para o comando a no início de uma linha, digamos, em uma função com linhas recuadas. Eu aproveitei que $ '\ n' é avaliado como uma nova linha pelo shell, não está nos valores regulares de aspas simples '\ n'.

Está ficando longo o suficiente, mas acho que o perl / awk pode ganhar devido a ser mais legível.


1
Obrigado pela resposta! O primeiro também funciona como um encanto no SO AIX.
abhishek 23/09

como você faz isso, exceto na inserção de várias linhas?
tatsu 04/07/19

1
O POSIX sed suporta novas linhas literais na cadeia de substituição, se você "escapá-las" com uma barra invertida ( origem ). Então: s/CLIENTSCRIPT="foo"/&\ [Enter] CLIENTSCRIPT2="hello"/ . A barra invertida deve ser o último caractere da linha (sem espaço em branco depois dela), ao contrário da aparência neste comentário.
TheDudeAbides 6/12/19

1
@tatsu As novas linhas na sequência de substituição de s///, assim como as dos comandos ae, ipodem ser "escapadas", usando o mesmo método que descrevi no meu comentário acima.
TheDudeAbides 6/12/19

4

Talvez um pouco tarde para postar uma resposta para isso, mas achei algumas das soluções acima um pouco complicadas.

Eu tentei a substituição simples de string no sed e funcionou:

sed 's/CLIENTSCRIPT="foo"/&\nCLIENTSCRIPT2="hello"/' file

O sinal de & reflete a sequência correspondente e, em seguida, você adiciona \ n e a nova linha.

Como mencionado, se você quiser fazer isso no local:

sed -i 's/CLIENTSCRIPT="foo"/&\nCLIENTSCRIPT2="hello"/' file

Outra coisa. Você pode combinar usando uma expressão:

sed -i 's/CLIENTSCRIPT=.*/&\nCLIENTSCRIPT2="hello"/' file

Espero que isso ajude alguém


Isso funciona bem no Linux, onde o GNU sed é o padrão, porque entende \nna cadeia de substituição. Plain vanilla (POSIX) sedque não entendem \nno texto de substituição, no entanto, assim que este não irá funcionar no BSD ou MacOS-a menos que você tenha instalado o GNU sed de alguma forma. Com o vanilla sed, você pode incorporar novas linhas "escapando" delas, ou seja, encerrando a linha com uma barra invertida e pressionando [Enter] ( referência , sob o cabeçalho [2addr]s/BRE/replacement/flags). Isso significa que o seu comando sed precisa abranger várias linhas no terminal.
TheDudeAbides 6/12/19

Outra opção para obter uma nova linha literal na cadeia de substituição é usar o recurso de cotação ANSI-C no Bash, conforme mencionado em uma das outras respostas .
TheDudeAbides 6/12/19


1

Eu tive uma tarefa semelhante e não foi capaz de obter a solução perl acima para funcionar.

Aqui está a minha solução:

perl -i -pe "BEGIN{undef $/;} s/^\[mysqld\]$/[mysqld]\n\ncollation-server = utf8_unicode_ci\n/sgm" /etc/mysql/my.cnf

Explicação:

Usa uma expressão regular para procurar uma linha no meu arquivo /etc/mysql/my.cnf que continha apenas [mysqld]e a substituiu por

[mysqld] collation-server = utf8_unicode_ci

adicionando efetivamente a collation-server = utf8_unicode_cilinha após a linha que contém [mysqld].


0

Eu tive que fazer isso recentemente também para sistemas operacionais Mac e Linux e, depois de navegar por várias postagens e experimentar muitas coisas, em minha opinião particular, nunca cheguei aonde queria: onde é simples o suficiente para entender a solução usando soluções conhecidas. e comandos padrão com padrões simples, um liner, portátil, expansível para adicionar mais restrições. Então tentei olhar para ele com uma perspectiva diferente; foi quando percebi que poderia ficar sem a opção "one liner" se um "2 liner" atender ao restante dos meus critérios. No final, eu vim com esta solução que eu gosto que funciona tanto no Ubuntu quanto no Mac, que eu queria compartilhar com todos:

insertLine=$(( $(grep -n "foo" sample.txt | cut -f1 -d: | head -1) + 1 ))
sed -i -e "$insertLine"' i\'$'\n''bar'$'\n' sample.txt

No primeiro comando, o grep procura números de linhas que contêm "foo", cut / head seleciona a 1ª ocorrência, e a operação aritmética aumenta esse número da linha da primeira ocorrência em 1, desde que desejo inserir após a ocorrência. No segundo comando, é uma edição de arquivo no local, "i", para inserção: um ansi-c citando uma nova linha, "bar", depois outra nova linha. O resultado é adicionar uma nova linha contendo "bar" após a linha "foo". Cada um desses 2 comandos pode ser expandido para operações e correspondências mais complexas.

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.