Substitua um regex, em seguida, substitua o segundo regex em todas as linhas que não correspondam primeiro

6

Exemplo de tarefa: se uma linha contiver foo , substitua-a por bar , caso contrário, anexe baz à linha.

sed -e s/foo/bar/ -e s/$/baz/ não funciona, pois o segundo comando é executado, quer o primeiro corresponda ou não. Existe uma maneira de dizer a sed para ir para a próxima linha após uma partida?

    
por Vladimir Panteleev 24.06.2018 / 16:50

4 respostas

14

Você pode usar o comando t sem um rótulo para iniciar o próximo ciclo na substituição bem-sucedida

$ cat ip.txt 
a foo 123
xyz
fore
1foo

$ sed -e 's/foo/bar/' -e t -e 's/$/baz/' ip.txt
a bar 123
xyzbaz
forebaz
1bar

Do manual:

t label (test)

Branch to label only if there has been a successful substitution since the last input line was read or conditional branch was taken. The label may be omitted, in which case the next cycle is started.

    
por 24.06.2018 / 17:24
3

Uma maneira mais robusta com o script awk :

awk '{ if (/foo/) gsub(/foo/, "bar"); else $0 = $0 "baz" }1' file

Ou ainda mais curto:

awk '{ if (!gsub(/foo/, "bar")) $0 = $0 "baz"; }1' file
    
por 24.06.2018 / 17:21
2

Tente isto:

sed -e '/foo/!s/$/baz/g' -e s/foo/bar/g
    
por 24.06.2018 / 17:01
1

Descobri:

sed -e '/foo/{s//bar/;p;D};s/$/baz/' 

Explicação:

  • Vários comandos sed podem ser encadeados com ; e agrupados com { ... } .
  • /foo/ seleciona todas as linhas que contêm o padrão foo .
  • s//bar/ é o mesmo que s/foo/bar/ - um padrão vazio significa repetir a última pesquisa (nesse caso, foo ).
  • p significa imprimir o espaço do padrão (a linha após a substituição).
  • D significa limpar a linha atual e ir para a próxima.
  • s/$/baz/ executará a segunda parte da tarefa, por exemplo, anexar baz às linhas pelas quais a regra anterior deixou de passar.
por 24.06.2018 / 17:20

Tags