Me gustaría actualizar una gran cantidad de archivos fuente C ++ con una directiva de inclusión adicional antes de cualquier #include existente. Para este tipo de tarea, normalmente uso un pequeño script bash con sed para volver a escribir el archivo.
¿Cómo puedo sed
reemplazar solo la primera aparición de una cadena en un archivo en lugar de reemplazar cada aparición?
Si yo uso
sed s/#include/#include "newfile.h"\n#include/
reemplaza todos los #incluye.
Sugerencias alternativas para lograr lo mismo también son bienvenidas.
command-line
sed
text-processing
David Dibben
fuente
fuente
0,
solo funciona congnu sed
s//
, es decir, una expresión regular vacía , significa que la expresión regular aplicada más recientemente se reutiliza implícitamente; en este caso,RE
. Este conveniente acceso directo significa que no tiene que duplicar la expresión regular de fin de rango en sus
llamada.Un
sed
script que solo reemplazará la primera aparición de "Apple" por "Banana"Ejemplo
Este es el guión simple: Nota del editor: funciona solo con GNU
sed
.Los dos primeros parámetros
0
y/Apple/
son el especificador de rango. Els/Apple/Banana/
es lo que se ejecuta dentro de ese rango. Entonces, en este caso "dentro del rango del principio (0
) hasta la primera instancia deApple
, reemplaceApple
conBanana
. SoloApple
se reemplazará la primera .Antecedentes: en el tradicional,
sed
el especificador de rango también es "comenzar aquí" y "terminar aquí" (inclusive). Sin embargo, el "comienzo" más bajo es la primera línea (línea 1), y si el "final aquí" es una expresión regular, entonces solo se intenta hacer coincidir en la siguiente línea después de "comenzar", por lo que el final más temprano posible es la línea 2. Entonces, dado que el rango es inclusivo, el rango más pequeño posible es "2 líneas" y el rango inicial más pequeño es ambas líneas 1 y 2 (es decir, si hay una ocurrencia en la línea 1, las ocurrencias en la línea 2 también se cambiarán, no deseadas en este caso )GNU
sed agrega su propia extensión que permite especificar el inicio como "pseudo"line 0
para que el final del rango pueda serline 1
, permitiéndole un rango de "solo la primera línea"O una versión simplificada (un RE vacío como
//
significa reutilizar el especificado antes, por lo que esto es equivalente):Y las llaves son opcionales para el
s
comando, por lo que esto también es equivalente:Todo esto funciona
sed
solo en GNU .También puede instalar GNU sed en OS X usando homebrew
brew install gnu-sed
.fuente
sed: 1: "…": bad flag in substitute command: '}'
sed -e '1s/Apple/Banana/;t' -e '1,/Apple/s//Banana/'
. De la respuesta de @ MikhailVS (actualmente) muy abajo.sed '0,/foo/s/foo/bar/'
sed: -e expression #1, char 3: unexpected
'' con estoEsto funcionó para mí.
ejemplo
Nota del editor: ambos funcionan solo con GNU
sed
.fuente
sed '1,/pattern/s/pattern/replacement/' filename
solo funciona si "el patrón no se producirá en la primera línea" en Mac. Eliminaré mi comentario anterior ya que no es exacto. Los detalles se pueden encontrar aquí ( linuxtopia.org/online_books/linux_tool_guides/the_sed_faq/… ). La respuesta de Andy solo funciona para GNU sed, pero no para Mac.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
, ozsh
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 POSIX
sed
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 coincidare
, ya sea quere
ocurra en la primera línea o en cualquier línea posterior.1,/re/
, que crea un rango que coincide desde la primera línea hasta e incluye la línea que coincidere
en las líneas posteriores ; en otras palabras: esto no detectará la primera aparición de unare
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). 1Si combina una
0,/re/
dirección con unas/.../.../
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 coincidare
.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,//
.Un POSIX-solo
sed
características como BSD (macOS)sed
(también funcionará con GNUsed
):Como
0,/re/
no se puede usar y el formulario1,/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í:
Nota:
El
//
atajo de expresiones regulares vacías se emplea dos veces aquí: una para el punto final del rango y otra en las
llamada; en ambos casos, regexfoo
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 casot
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 solofoo
en la primera línea, si se encuentra allí. Si es así, set
bifurca al final del script (omite los comandos restantes en la línea). (Lat
función se ramifica a una etiqueta solo si las
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
's0,/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 :
Técnica de bucle 2, solo para archivos pequeños : lea toda la entrada en la memoria y luego realice una única sustitución .
1 1.61803 proporciona ejemplos de lo que sucede con
1,/re/
, con y sin posterioridads//
:-
sed '1,/foo/ s/foo/bar/' <<<$'1foo\n2foo'
rendimientos$'1bar\n2bar'
; es decir, ambas líneas se actualizaron, porque el número de línea1
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 las/foo/bar/
sustitución se realiza en ambas.-
sed '1,/foo/ s//bar/' <<<$'1foo\n2foo\n3foo'
falla : consed: first RE may not be empty
(BSD / macOS) ysed: -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 que1
comienza el rango), todavía no se ha aplicado expresión regular, por lo que//
no se refiere a nadaCon la excepción de
sed
la0,/re/
sintaxis especial de GNU , cualquier rango que comience con un número de línea efectivamente impide el uso de//
.fuente
Podrías usar awk para hacer algo similar ...
Explicación:
Ejecuta la declaración de acción entre {} cuando la línea coincide con "#include" y aún no la hemos procesado.
Esto imprime #include "newfile.h", necesitamos escapar de las comillas. Luego establecemos la variable done en 1, por lo que no agregamos más inclusiones.
Esto significa "imprimir la línea" - una acción vacía por defecto imprime $ 0, que imprime toda la línea. Un trazador de líneas y más fácil de entender que sed IMO :-)
fuente
awk '/version/ && !done {print " \"version\": \"'${NEWVERSION}'\""; done=1;}; 1;' package.json
awk '/#include/ && !done { gsub(/#include/, "include \"newfile.h\""); done=1}; 1' file.c
Una colección bastante completa de respuestas sobre linuxtopia sed FAQ . También destaca que algunas respuestas que las personas proporcionaron no funcionarán con una versión de sed que no sea GNU, por ejemplo
en la versión no GNU tendrá que ser
Sin embargo, esta versión no funcionará con gnu sed.
Aquí hay una versión que funciona con ambos:
ex:
fuente
Cómo funciona esta secuencia de comandos: para las líneas entre 1 y la primera
#include
(después de la línea 1), si la línea comienza con#include
, entonces anteponga la línea especificada.Sin embargo, si el primero
#include
está en la línea 1, tanto la línea 1 como la siguiente#include
tendrán la línea antepuesta. Si está utilizando GNUsed
, tiene una extensión donde0,/^#include/
(en lugar de1,
) hará lo correcto.fuente
Simplemente agregue el número de ocurrencias al final:
fuente
sed
especifica el comando sustituto con:[2addr]s/BRE/replacement/flags
y observa que "El valor de las banderas será cero o más de: n Sustituye la enésima aparición solo del BRE encontrado dentro del espacio del patrón". Por lo tanto, al menos en POSIX 2008, el final1
no es unased
extensión de GNU . De hecho, incluso en el estándar SUS / POSIX 1997 , esto fue compatible, por lo que estaba muy fuera de lugar en 2008.Una posible solución:
Explicación:
fuente
sed: file me4.sed line 4: ":" lacks a label
Sé que esta es una publicación antigua pero tenía una solución que solía usar:
Básicamente use grep para imprimir la primera aparición y deténgase allí. Además, imprima el número de línea, es decir
5:line
. Canalice eso en sed y elimine el: y cualquier cosa posterior para que le quede un número de línea. Canalice eso en sed que agrega s /.*/ reemplazar al número final, lo que resulta en un script de 1 línea que se canaliza en el último sed para ejecutarse como un script en el archivo.entonces, si regex =
#include
y replace =blah
y la primera aparición que grep encuentra está en la línea 5, entonces los datos canalizados al último sed estarían5s/.*/blah/
.Funciona incluso si la primera aparición está en la primera línea.
fuente
sed -f -
algunos que no lo son, pero puedesSi alguien vino aquí para reemplazar un personaje por primera vez en todas las líneas (como yo), use esto:
Al cambiar 1 a 2, por ejemplo, puede reemplazar todas las segundas solo en su lugar.
fuente
's/a/b/'
significamatch a
, ydo just first match
for every matching line
Con la
-z
opción de GNU sed, puede procesar todo el archivo como si fuera solo una línea. De esa maneras/…/…/
, a solo reemplazaría la primera coincidencia en todo el archivo. Recuerde:s/…/…/
solo reemplaza la primera coincidencia en cada línea, pero con la-z
opciónsed
trata el archivo completo como una sola línea.En el caso general, debe reescribir su expresión sed ya que el espacio del patrón ahora contiene todo el archivo en lugar de solo una línea. Algunos ejemplos:
s/text.*//
puede reescribirse comos/text[^\n]*//
.[^\n]
coincide con todo excepto el carácter de nueva línea.[^\n]*
coincidirá con todos los símbolostext
hasta que se llegue a una nueva línea.s/^text//
puede reescribirse comos/(^|\n)text//
.s/text$//
puede reescribirse comos/text(\n|$)//
.fuente
Haría esto con un script awk:
luego ejecútalo con awk:
podría ser descuidado, soy nuevo en esto.
fuente
Como sugerencia alternativa, es posible que desee ver el
ed
comando.fuente
Finalmente conseguí que esto funcionara en un script Bash utilizado para insertar una marca de tiempo única en cada elemento en un feed RSS:
Solo cambia la primera aparición.
${nowms}
es el tiempo en milisegundos establecido por un script Perl,$counter
es un contador utilizado para el control de bucle dentro del script,\
permite que el comando continúe en la siguiente línea.El archivo se lee y stdout se redirige a un archivo de trabajo.
Según tengo entendido,
1,/====RSSpermalink====/
le dice a sed cuándo detenerse al establecer una limitación de rango, y luegos/====RSSpermalink====/${nowms}/
es el comando familiar sed para reemplazar la primera cadena por la segunda.En mi caso, pongo el comando entre comillas dobles porque lo estoy usando en un script Bash con variables.
fuente
Usando FreeBSD
ed
y evitaed
el error "no match" en caso de que no haya unainclude
declaración en un archivo para ser procesado:fuente
Esto podría funcionar para usted (GNU sed):
o si la memoria no es un problema:
fuente
El siguiente comando elimina la primera aparición de una cadena, dentro de un archivo. También elimina la línea vacía. Se presenta en un archivo xml, pero funcionaría con cualquier archivo.
Útil si trabaja con archivos xml y desea eliminar una etiqueta. En este ejemplo, elimina la primera aparición de la etiqueta "isTag".
Mando:
Archivo fuente (source.txt)
Archivo de resultados (output.txt)
ps: no funcionó para mí en Solaris SunOS 5.10 (bastante antiguo), pero funciona en Linux 2.6, sed versión 4.1.5
fuente
sed
(por lo tanto, no funcionó con Solaris). Debe eliminar esto, por favor, realmente no proporciona información nueva distintiva a una pregunta que ya tenía 4 años y medio cuando respondió. Por supuesto, tiene un ejemplo trabajado, pero tiene un valor discutible cuando la pregunta tiene tantas respuestas como esta.Nada nuevo, pero quizás una respuesta un poco más concreta:
sed -rn '0,/foo(bar).*/ s%%\1%p'
Ejemplo:
xwininfo -name unity-launcher
produce resultados como:Extraer ID de ventana con
xwininfo -name unity-launcher|sed -rn '0,/^xwininfo: Window id: (0x[0-9a-fA-F]+).*/ s%%\1%p'
produce:fuente
POSIXly (también válido en sed), solo se usa una expresión regular, necesita memoria solo para una línea (como de costumbre):
Explicado:
fuente
El caso de uso puede ser que sus ocurrencias se extiendan por todo el archivo, pero sabe que su única preocupación es en las primeras 10, 20 o 100 líneas.
Luego, simplemente abordar esas líneas soluciona el problema , incluso si la redacción del OP solo se refiere primero.
fuente
Una posible solución aquí podría ser decirle al compilador que incluya el encabezado sin que se mencione en los archivos de origen. EN GCC hay estas opciones:
El compilador de Microsoft tiene el / FI opción (inclusión forzada).
Esta característica puede ser útil para algunos encabezados comunes, como la configuración de la plataforma. El Makefile del kernel de Linux utiliza
-include
para esto.fuente
fuente