Me gustaría insertar una nueva cadena en la última línea con algo de texto usando sed,

Me gustaría insertar una nueva cadena en la última línea con algo de texto usando sed,

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 sedpuedes hacer esto:

sed ':a;N;$! ba;s/.*\nApple/&\nYour appended line/'

El patrón :a;N;$! baagrega líneas del archivo al búfer con Nmientras no se llega al final del archivo ( $dirección) ( !) (saltar a :acon ba). Entonces, si sale del ciclo, todas las líneas están en el búfer y el patrón de búsqueda .*\nApplecoincide 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 sedtambié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 \nen la cadena de reemplazo (que no se garantiza que funcione con sedarchivos que no sean GNU) nos referimos al del patrón rodeado \(\)con la \1referencia.

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 -ropción habilita expresiones regulares extendidas.
La -zopción indica sedque 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 sedcomando s/PATTERN/REPLACEMENT/busca la -rexpresión regular (extendida, debido a) PATTERNy la reemplaza con la REPLACEMENTcadena evaluada.

La expresión regular (^(.*\n)?Apple)(\n|$)coincide con cualquier número de líneas desde el principio ( ^) hasta la última aparición Appleseguida de un salto de línea ( \n) o el final del archivo ( $).

Ahora toda esta coincidencia se reemplaza con \1\ninserted line\n, donde \1representa el contenido original del primer grupo de coincidencias, es decir, todo hasta Apple. En realidad, se inserta inserted lineprecedido 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.txten el lugar, en lugar de simplemente generar la modificación, puede agregar la -iopció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, sedno 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 echocomando 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:

  1. sed+tac (inversión de doble entrada)
  2. AWK con doble entrada
  3. Perl con doble entrada
  4. 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 $lvariable. Lo que es diferente de awk aquí es que no hay ninguna FNRvariable en Perl, por lo que tenemos que usar $t+=1y close ARGV if eofpara ese propósito.

Lo que también es diferente aquí es que con -sswitch perl le permite pasar argumentos. En este caso la cadena que queremos insertar se pasa mediante -nswitch.

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

información relacionada