
Busque en el archivo la última línea con una cadena determinada para agregar una línea después de esa coincidencia. Ejemplo:
Aporte:
Apple
Banana
Apple
Orange
Apple
Grapefruit
Producción:
Apple
Banana
Apple
Orange
Apple
I am new string replaced at last apple
Grapefruit
Respuesta1
Con GNU sed
puedes hacer esto:
sed ':a;N;$! ba;s/.*\nApple/&\nYour appended line/'
El patrón :a;N;$! ba
agrega líneas del archivo al búfer con N
mientras no se llega al final del archivo ( $
dirección) ( !
) (saltar a :a
con ba
). Entonces, si sale del ciclo, todas las líneas están en el búfer y el patrón de búsqueda .*\nApple
coincide con todo el archivo hasta la última línea que comienza con Apple
. En &
la cadena de reemplazo, inserte la coincidencia completa y agregue su texto.
Una solución portátil que funcione sed
también en otras versiones sería
sed -e ':a' -e 'N;$! ba' -e 's/.*\(\n\)Apple/&\1Your appended line/'
Aquí el script se divide en las etiquetas y en lugar de usarlo \n
en la cadena de reemplazo (que no se garantiza que funcione con sed
archivos que no sean GNU) nos referimos al del patrón rodeado \(\)
con la \1
referencia.
Respuesta2
vi este ejemploaquí
sed -i -e "\$aTEXTTOEND" <filename>
Información:
i: edit files in place
e: add the script to the commands to be executed
$: sed address location
a: append command
TEXTTOEND: text to append to end of file
Respuesta3
Supongo que la entrada de tu ejemplo es un archivo llamado fruits.txt
.
Si desea insertar una línea después de la última aparición de "Apple", puede hacerlo así sed
:
sed -rz 's/(^(.*\n)?Apple)(\n|$)/\1\ninserted line\n/'
La -r
opción habilita expresiones regulares extendidas.
La -z
opción indica sed
que se tomen caracteres NUL (código ASCII 0) como separadores de línea en lugar de los caracteres de nueva línea normales. De esta manera, todo el archivo de texto se procesará de una vez en lugar de línea por línea.
El sed
comando s/PATTERN/REPLACEMENT/
busca la -r
expresión regular (extendida, debido a) PATTERN
y la reemplaza con la REPLACEMENT
cadena evaluada.
La expresión regular (^(.*\n)?Apple)(\n|$)
coincide con cualquier número de líneas desde el principio ( ^
) hasta la última aparición Apple
seguida de un salto de línea ( \n
) o el final del archivo ( $
).
Ahora toda esta coincidencia se reemplaza con \1\ninserted line\n
, donde \1
representa el contenido original del primer grupo de coincidencias, es decir, todo hasta Apple
. En realidad, se inserta inserted line
precedido y seguido de un salto de línea ( \n
) justo después de la última aparición de "Apple".
Esto también funciona si la línea "Apple" es la primera, la última o la única línea del archivo.
Archivo de entrada de ejemplo (se agregó una línea a su ejemplo para aclarar el comportamiento):
Apple
Banana
Apple
Orange
Apple
Cherry
Salida de ejemplo:
Apple
Banana
Apple
Orange
Apple
inserted line
Cherry
Si está satisfecho con el resultado y desea editarlo fruits.txt
en el lugar, en lugar de simplemente generar la modificación, puede agregar la -i
opción:
sed -irz 's/(^(.*\n)?Apple)(\n|$)/\1\ninserted line\n/'
Sin embargo, si solo desea agregar una línea de texto al final de ese archivo, sed
no es la herramienta óptima.
Simplemente puede usar >>
la redirección de salida adjunta de Bash y luego:
echo "I am new string replaced at last apple" >> fruits.txt
Tomará >>
la salida estándar del comando a su izquierda (el echo
comando aquí simplemente imprime sus argumentos en la salida estándar) y lo agregará al archivo especificado a su derecha. Si el archivo aún no existe, se creará.
Respuesta4
En esta respuesta:
- sed+tac (inversión de doble entrada)
- AWK con doble entrada
- Perl con doble entrada
- Perl alternativo con inversión de doble entrada
sed + tac
Este enfoque utiliza 3 procesos: primero invierte el archivo, usa sed para encontrar la primera coincidencia e inserta el texto deseado después de la primera coincidencia, y vuelve a invertir el texto.
$ tac input.txt | sed '0,/Apple/{s/Apple/NEW THING\n&/}' | tac
Apple
Banana
Apple
Orange
Apple
NEW THING
something else
something other entirely
blah
La idea básica es la misma que la versión alternativa de Perl escrita a continuación: la primera coincidencia en la lista inversa de líneas es la última coincidencia en la lista original. La ventaja de este enfoque es la simplicidad y la legibilidad para el usuario.
Este enfoque funcionará bien para archivos pequeños, pero es engorroso para archivos grandes y probablemente llevará un tiempo considerable procesar una gran cantidad de líneas.
AWK
Awk puede ser un poco amigable para lo que estás tratando de hacer:
$ awk -v n="AFTER LAST APPLE" 'NR==FNR&&/Apple/{l=NR};NR!=FNR{if(FNR==l){print $0;print n}else print}' input.txt input.t>
Apple
Banana
Apple
Orange
Apple
AFTER LAST APPLE
something else
something other entirely
blah
La idea básica aquí es darle su archivo de entrada a awk dos veces: primero para encontrar dónde está la última manzana y segundo para insertar el texto cuando pasamos la posición de esa última "manzana" que encontramos en la primera iteración.
La clave para que esto funcione es evaluar NR
(que sigue contando todas las líneas procesadas, totales) y FNR
(número de línea en el archivo actual) las variables. Cuando estos dos archivos no son iguales, sabemos que estamos procesando el segundo archivo, por lo que sabemos que hemos realizado nuestra primera pasada para encontrar la ubicación de la última aparición de Apple.
Como leemos el archivo dos veces, el rendimiento dependerá del tamaño del archivo, pero es mejor que la combinación sed + tac, ya que no necesitamos revertir el archivo dos veces.
perla
perl -sne '$t+=1;$l=$. if /Apple/ and $.==$t;
if($t!=$.){if($.==$l){print $_ . $n . "\n";}else{print $_}};
close ARGV if eof;' -- -n="AFTER LAST APPLE" input.txt input.txt
La solución perl sigue la misma idea que la solución AWK. Pasamos la entrada dos veces y usamos la primera vez que se pasa el archivo para encontrar dónde está la última aparición de Apple y almacenar su número de línea en una $l
variable. Lo que es diferente de awk aquí es que no hay ninguna FNR
variable en Perl, por lo que tenemos que usar $t+=1
y close ARGV if eof
para ese propósito.
Lo que también es diferente aquí es que con -s
switch perl le permite pasar argumentos. En este caso la cadena que queremos insertar se pasa mediante -n
switch.
Perl alternativo
Aquí hay otra forma de hacerlo en Perl. La idea es simplemente leer el archivo en una serie de líneas e invertir esa matriz. La primera coincidencia en la matriz invertida es la última en la lista original de líneas. Así, podemos insertar el texto deseado antes de esa línea y volver a invertir la lista:
$ perl -le '@a1=<>;END{do{push @a2,"NEW LINE\n" and $f=1 if $_ =~ /Apple/ and !$f; push @a2,$_} for reverse(@a1); print reverse(@a2)}' input.txt
Apple
Banana
Apple
Orange
Apple
NEW LINE
something else
something other entirely
blah