Uma visão geral das muitas respostas existentes úteis , complementadas com explicações :
Os exemplos aqui usam um caso de uso simplificado: substitua a palavra 'foo' por 'bar' apenas na primeira linha correspondente.
Devido ao uso de cordas ANSI C-citados ( $'...') para proporcionar as linhas de entrada de amostra, bash, ksh, ou zshé assumida como a casca.
sedApenas GNU :
A resposta de Ben Hoffstein nos mostra que o GNU fornece uma extensão para a especificação POSIX,sed que permite o seguinte formato de 2 endereços : 0,/re/( rerepresenta uma expressão regular arbitrária aqui).
0,/re/permite que o regex corresponda também na primeira linha . Em outras palavras: esse endereço criará um intervalo da 1ª linha até a linha correspondente re- inclusive se reocorrerá na 1ª linha ou em qualquer linha subsequente.
- Compare isso com o formulário compatível com POSIX
1,/re/, que cria um intervalo que corresponde da 1ª linha até a linha correspondente reàs linhas subseqüentes ; em outras palavras: isso não detectará a primeira ocorrência de uma recorrespondência se ocorrer na 1ª linha e também evita o uso de taquigrafia// para reutilizar o regex usado mais recentemente (consulte o próximo ponto). 1
Se você combinar um 0,/re/endereço com uma s/.../.../chamada (substituição) que use a mesma expressão regular, seu comando efetivamente executará a substituição apenas na primeira linha correspondente re.
sedfornece um atalho conveniente para reutilizar a expressão regular aplicada mais recentemente : um par de delimitadores vazio// ,.
$ sed '0,/foo/ s//bar/' <<<$'1st foo\nUnrelated\n2nd foo\n3rd foo'
1st bar # only 1st match of 'foo' replaced
Unrelated
2nd foo
3rd foo
Um recurso POSIX somente sedcomo BSD (macOS)sed (também funcionará com o GNU sed ):
Como 0,/re/não pode ser usado e o formulário 1,/re/não detectará rese ocorrer na primeira linha (veja acima), é necessário um tratamento especial para a 1ª linha .
A resposta do MikhailVS menciona a técnica, colocada em um exemplo concreto aqui:
$ sed -e '1 s/foo/bar/; t' -e '1,// s//bar/' <<<$'1st foo\nUnrelated\n2nd foo\n3rd foo'
1st bar # only 1st match of 'foo' replaced
Unrelated
2nd foo
3rd foo
Nota:
O //atalho de regex vazio é empregado duas vezes aqui: uma para o ponto final do intervalo e uma vez na schamada; nos dois casos, a regex fooé reutilizada implicitamente, permitindo que não tenhamos que duplicá-la, o que resulta em código mais curto e mais sustentável.
O POSIX sedprecisa de novas linhas reais após determinadas funções, como após o nome de um rótulo ou mesmo sua omissão, como é o caso taqui; dividir estrategicamente o script em várias -eopções é uma alternativa ao uso de novas linhas reais: finalize cada -eparte do script para onde normalmente uma nova linha precisaria ir.
1 s/foo/bar/substitui apenas foona 1ª linha, se encontrada lá. Nesse caso, tramifica para o final do script (ignora os comandos restantes na linha). (A tfunção ramifica para um rótulo somente se a schamada mais recente executou uma substituição real; na ausência de um rótulo, como é o caso aqui, o final do script é ramificado).
Quando isso acontecer, o endereço do intervalo 1,//, que normalmente encontra a primeira ocorrência iniciando na linha 2 , não corresponderá e o intervalo não será processado, porque o endereço é avaliado quando a linha atual já está 2.
Por outro lado, se não houver correspondência na 1ª linha, 1,// será inserida e encontrará a primeira correspondência verdadeira.
O efeito líquido é o mesmo que com GNU sed's 0,/re/: apenas a primeira ocorrência é substituído, se ocorre na linha 1 ou qualquer outro.
Abordagens fora da faixa
a resposta de potong demonstra técnicas de loop que ignoram a necessidade de um intervalo ; como ele usa a sintaxe GNU sed , eis os equivalentes compatíveis com POSIX :
Técnica de loop 1: Na primeira partida, execute a substituição e insira um loop que simplesmente imprima as linhas restantes como estão :
$ sed -e '/foo/ {s//bar/; ' -e ':a' -e '$!{n;ba' -e '};}' <<<$'1st foo\nUnrelated\n2nd foo\n3rd foo'
1st bar
Unrelated
2nd foo
3rd foo
Técnica de loop 2, apenas para arquivos pequenos : leia toda a entrada na memória e execute uma única substituição nela .
$ sed -e ':a' -e '$!{N;ba' -e '}; s/foo/bar/' <<<$'1st foo\nUnrelated\n2nd foo\n3rd foo'
1st bar
Unrelated
2nd foo
3rd foo
1 1.61803 fornece exemplos do que acontece com 1,/re/, com e sem subsequentes s//:
- sed '1,/foo/ s/foo/bar/' <<<$'1foo\n2foo'rendimentos $'1bar\n2bar'; ou seja, ambas as linhas foram atualizadas, porque o número da linha 1corresponde à 1ª linha e a regex /foo/- o final do intervalo - é procurada apenas para iniciar na próxima linha. Portanto, as duas linhas são selecionadas nesse caso e a s/foo/bar/substituição é realizada em ambas.
- sed '1,/foo/ s//bar/' <<<$'1foo\n2foo\n3foo' falha : com sed: first RE may not be empty(BSD / macOS) esed: -e expression #1, char 0: no previous regular expression (GNU), porque, no momento em que a 1ª linha está sendo processada (devido ao número da linha 1iniciar o intervalo), nenhuma regex foi aplicada ainda, portanto//não se refere a nada.
Com exceção da sintaxe sedespecial do GNU 0,/re/, qualquer intervalo que comece com um número de linha efetivamente impede o uso de //.