Deseja substituir apenas a primeira ocorrência por sed

23

Arquivo original

claudio
antonio
claudio
michele

Eu quero mudar apenas a primeira ocorrência de "claudio" com "claudia" então o resultado do arquivo

claudia
antonio
claudio
michele

Eu tentei

sed -e '1,/claudio/s/claudio/claudia/' nomi

Mas realize uma substituição global. Por quê?

    
por elbarna 05.03.2015 / 00:32

8 respostas

18

Se você estiver usando o GNU sed , tente:

sed -e '0,/claudio/ s/claudio/claudia/' nomi

sed não inicia a verificação da expressão regular que termina um intervalo até após a linha que inicia esse intervalo.

De man sed (página de manual do POSIX, ênfase minha):

An editing command with two addresses shall select the inclusive  range
from the first pattern space that matches the first address through the
next pattern space that matches the second. 

Usando awk

Os intervalos em awk funcionam mais como você esperava:

$ awk 'NR==1,/claudio/{sub(/claudio/, "claudia")} 1' nomi
claudia
antonio
claudio
michele

Explicação:

  • NR==1,/claudio/

    Este é um intervalo que começa com a linha 1 e termina com a primeira ocorrência de claudio .

  • sub(/claudio/, "claudia")

    Enquanto estamos no intervalo, este comando substituto é executado.

  • 1

    Esta abreviada enigmática do awk para imprimir a linha.

por 05.03.2015 / 00:51
4

Aqui estão mais dois esforços programáticos com o sed: ambos lêem o arquivo inteiro em uma única string e, em seguida, a pesquisa substituirá apenas o primeiro.

sed -n ':a;N;$bb;ba;:b;s/\(claudi\)o/a/;p' file
sed -n '1h;1!H;${g;s/\(claudi\)o/a/;p;}' file

Com comentários:

sed -n '                # don't implicitly print input
  :a                    # label "a"
  N                     # append next line to pattern space
  $bb                   # at the last line, goto "b"
  ba                    # goto "a"
  :b                    # label "b"
  s/\(claudi\)o/a/    # replace
  p                     # and print
' file
sed -n '                # don't implicitly print input
  1h                    # put line 1 in the hold space
  1!H                   # for subsequent lines, append to hold space
  ${                    # on the last line
    g                     # put the hold space in pattern space
    s/\(claudi\)o/a/    # replace
    p                     # print
  }
' file
    
por 05.03.2015 / 01:11
1

Você pode usar awk com um sinalizador para saber se a substituição já foi feita. Caso contrário, prossiga:

$ awk '!f && /claudio/ {$0="claudia"; f=1}1' file
claudia
antonio
claudio
michele
    
por 05.03.2015 / 13:25
1

Na verdade, é muito fácil configurar um pequeno atraso. Não é necessário usar extensões não confiáveis:

sed '$H;x;1,/claudio/s/claudio/claudia/;1d' <<\IN
claudio
antonio
claudio
michele
IN

Isso apenas adia a primeira linha para a segunda e a segunda para a terceira e etc.

Imprime:

claudia
antonio
claudio
michele
    
por 31.03.2015 / 03:29
1

E mais uma opção

sed --in-place=*.bak -e "1 h;1! H;\$! d;$ {g;s/claudio/claudia/;}" -- nomi

A vantagem é que usa cotação dupla, então você pode usar variáveis dentro, ou seja,

export chngFrom=claudio
export chngTo=claudia
sed --in-place=*.bak -e "1 h;1! H;\$! d;$ {g;s/${chngFrom}/${chngTo}/;}" -- nomi
    
por 09.04.2016 / 21:53
1

Isso também pode ser feito sem o espaço de espera e sem concentrar todas as linhas no espaço padrão:

sed -n '/claudio/{s/o/a/;bx};p;b;:x;p;n;bx' nomi

Explicação: Tentamos encontrar "claudio" e, se o fizermos, passamos para o pequeno loop de carga de impressão entre :x e bx . Caso contrário, imprimiremos e reiniciaremos o script com a próxima linha.

sed -n '      # do not print lines by default
  /claudio/ { # on lines that match "claudio" do ...
    s/o/a/    # replace "o" with "a"
    bx        # goto label x
  }           # end of do block
  p           # print the pattern space
  b           # go to the end of the script, continue with next line
  :x          # the label x for goto commands
  p           # print the pattern space
  n           # load the next line in the pattern space (clearing old contents)
  bx          # goto the label x
  ' nomi
    
por 10.04.2016 / 00:28
1
sed -n '/claudia/{p;Q}'

sed -n '           # don't print input
    /claudia/      # regex search
    {              # when match is found do
    p;             # print line
    Q              # quit sed, don't print last buffered line
    {              # end do block
    
por 06.07.2016 / 21:52
1

Um padrão curto, POSIX válido, apenas um (regex) para gravar, armazenando apenas uma linha:

sed '/\(claudi\)o/!b;//s//a/;:1;n;b1'

sed '
/\(claudi\)o/!b    # Match the regex 'claudio'. But in two parts 'claudi' and 'o'
                   # and, for lines that do not match yet go to end (print).
//s//a/          # For one line that match the previous regex, change o to a.
:1                 # label to loop from now on.
n                  # read next line (it gets auto printed).
b1                 # keep (only) printing to the end of file.
'
    
por 11.10.2018 / 07:16

Tags