Quiere sustituir solo la primera ocurrencia con sed

Archivo original

claudio antonio claudio michele 

Quiero cambiar solo la primera aparición de "claudio" con "claudia" para que el resultado del file

 claudia antonio claudio michele 

Yo he tratado

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

Pero realice una sustitución global. ¿Por qué?

Si está utilizando GNU sed , intente:

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

sed no comienza a search la expresión regular que finaliza un range hasta después de la línea que inicia ese range.

De man sed (página de man sed POSIX, énfasis mío):

 Un command de edición con dos direcciones debe seleccionar el range inclusivo
 desde el primer espacio de patrón que coincide con la primera dirección a través del 
  espacio del siguiente patrón que coincide con el segundo. 

Usando awk

Los ranges en awk funcionan más de lo que esperabas:

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

Explicación:

  • NR==1,/claudio/

    Este es un range que comienza con la línea 1 y termina con la primera aparición de claudio .

  • sub(/claudio/, "claudia")

    Mientras estamos en el range, este command sustituto se ejecuta.

  • 1

    Taquigrafía de este awk para imprimir la línea.

Aquí hay 2 esfuerzos programáticos más con sed: ambos leen todo el file en una sola cadena, luego la búsqueda solo replaceá al primero.

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

Con comentario:

 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/\1a/ # 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/\1a/ # replace p # print } ' file 

Puede usar awk con una bandera para saber si el reemploop ya estaba hecho. Si no, proceda:

 $ awk '!f && /claudio/ {$0="claudia"; f=1}1' file claudia antonio claudio michele 

En realidad, es realmente fácil si solo configura un poco de retraso: no hay necesidad de search extensiones poco confiables:

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

Eso solo difiere la primera línea al segundo y la segunda al tercero, etc.

Imprime:

 claudia antonio claudio michele 

Y una opción más

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

La ventaja es que utiliza doble cita, por lo que puede usar variables dentro, es decir.

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

Esto también se puede hacer sin el espacio de espera y sin concatenar todas las líneas en el espacio del patrón:

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

Explicación: Tratamos de encontrar "claudio" y si lo hacemos saltamos al loop de carga de letra pequeña entre :x y bx . De lo contrario, imprimiremos y reiniciaremos el script con la siguiente línea.

 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 
 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 buffenetworking line { # end do block