Una descripción general de las muchas respuestas útiles existentes , complementadas con explicaciones :
Los ejemplos aquí usan un caso de uso simplificado: reemplace la palabra 'foo' con 'bar' en la primera línea coincidente solamente.
Debido al uso de cadenas entre comillas-C ANSI ( $'...') para proporcionar las líneas de entrada de muestra, bash, ksh, o zshse asume como la cáscara.
GNU sed solamente:
La respuesta de Ben Hoffstein nos muestra que GNU proporciona una extensión a la especificación POSIXsed que permite la siguiente forma de 2 direcciones : 0,/re/( rerepresenta una expresión regular arbitraria aquí).
0,/re/permite que la expresión regular coincida en la primera línea también . En otras palabras: dicha dirección creará un rango desde la primera línea hasta la línea que coincida re, ya sea que reocurra en la primera línea o en cualquier línea posterior.
- Compare esto con el formulario compatible con POSIX
1,/re/, que crea un rango que coincide desde la primera línea hasta e incluye la línea que coincide reen las líneas posteriores ; en otras palabras: esto no detectará la primera aparición de una recoincidencia si ocurre en la primera línea y también evita el uso de la taquigrafía// para la reutilización de la expresión regular utilizada más recientemente (ver el siguiente punto). 1
Si combina una 0,/re/dirección con una s/.../.../llamada (de sustitución) que usa la misma expresión regular, su comando efectivamente solo realizará la sustitución en la primera línea que coincida re.
sedproporciona un cómodo acceso directo para la reutilización de la expresión regular más recientemente aplicado : un vacío par delimitador,// .
$ 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
Un POSIX-solo sedcaracterísticas como BSD (macOS)sed (también funcionará con GNU sed ):
Como 0,/re/no se puede usar y el formulario 1,/re/no detectará resi ocurre en la primera línea (ver arriba), se requiere un manejo especial para la primera línea .
La respuesta de MikhailVS menciona la técnica, puesta en un ejemplo concreto aquí:
$ 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:
El //atajo de expresiones regulares vacías se emplea dos veces aquí: una para el punto final del rango y otra en la sllamada; en ambos casos, regex foose reutiliza implícitamente, lo que nos permite no tener que duplicarlo, lo que hace que el código sea más corto y más fácil de mantener.
POSIX sednecesita nuevas líneas reales después de ciertas funciones, como después del nombre de una etiqueta o incluso su omisión, como es el caso taquí; dividir estratégicamente el guión en múltiples -eopciones es una alternativa al uso de una nueva línea real: finalice cada -efragmento del guión donde normalmente debería ir una nueva línea.
1 s/foo/bar/reemplaza solo fooen la primera línea, si se encuentra allí. Si es así, se tbifurca al final del script (omite los comandos restantes en la línea). (La tfunción se ramifica a una etiqueta solo si la sllamada más reciente realizó una sustitución real; en ausencia de una etiqueta, como es el caso aquí, se ramifica el final del script).
Cuando eso sucede, la dirección de rango 1,//, que normalmente encuentra la primera aparición a partir de la línea 2 , no coincidirá, y el rango no se procesará, porque la dirección se evalúa cuando la línea actual ya está 2.
Por el contrario, si no hay una coincidencia en la primera línea, 1,// se ingresará y encontrará la primera coincidencia verdadera.
El efecto neto es el mismo que con GNU sed 's 0,/re/: sólo la primera aparición se sustituye, si se produce en la primera línea de o cualquier otro.
Enfoques sin rango
la respuesta de potong demuestra técnicas de bucle que evitan la necesidad de un rango ; ya que usa GNU sed sintaxis , aquí están los equivalentes compatibles con POSIX :
Técnica de bucle 1: en la primera coincidencia, realice la sustitución, luego ingrese un bucle que simplemente imprima las líneas restantes tal como están :
$ 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 bucle 2, solo para archivos pequeños : lea toda la entrada en la memoria y luego realice una única sustitución .
$ 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 proporciona ejemplos de lo que sucede con 1,/re/, con y sin posterioridad s//:
- sed '1,/foo/ s/foo/bar/' <<<$'1foo\n2foo'rendimientos $'1bar\n2bar'; es decir, ambas líneas se actualizaron, porque el número de línea 1coincide con la primera línea y la expresión regular /foo/, el final del rango, solo se busca para comenzar en la siguiente línea. Por lo tanto, ambas líneas se seleccionan en este caso, y la s/foo/bar/sustitución se realiza en ambas.
- sed '1,/foo/ s//bar/' <<<$'1foo\n2foo\n3foo' falla : con sed: first RE may not be empty(BSD / macOS) y sed: -e expression #1, char 0: no previous regular expression(GNU), porque, en el momento en que se procesa la primera línea (debido al número de línea que 1comienza el rango), todavía no se ha aplicado expresión regular, por lo que//no se refiere a nada
Con la excepción de sedla 0,/re/sintaxis especial de GNU , cualquier rango que comience con un número de línea efectivamente impide el uso de //.