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 zsh
se 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/
( re
representa 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 re
ocurra 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 re
en las líneas posteriores ; en otras palabras: esto no detectará la primera aparición de una re
coincidencia 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
.
sed
proporciona 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 sed
caracterí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á re
si 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 s
llamada; en ambos casos, regex foo
se 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 sed
necesita 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 t
aquí; dividir estratégicamente el guión en múltiples -e
opciones es una alternativa al uso de una nueva línea real: finalice cada -e
fragmento del guión donde normalmente debería ir una nueva línea.
1 s/foo/bar/
reemplaza solo foo
en la primera línea, si se encuentra allí. Si es así, se t
bifurca al final del script (omite los comandos restantes en la línea). (La t
función se ramifica a una etiqueta solo si la s
llamada 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 1
coincide 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 1
comienza el rango), todavía no se ha aplicado expresión regular, por lo que//
no se refiere a nada
Con la excepción de sed
la 0,/re/
sintaxis especial de GNU , cualquier rango que comience con un número de línea efectivamente impide el uso de //
.