Reemplazar cadena con índice secuencial

9

¿Alguien puede sugerir una forma elegante de lograr esto?

Entrada:

test  instant  ()

test  instant  ()

...
test  instant  ()    //total 1000 lines

la salida debe ser:

test      instant1  ()

test      instant2  ()

test      instant1000()

Las líneas vacías están en mis archivos de entrada y hay muchos archivos en el mismo directorio que necesito procesar a la vez.

Intenté esto para reemplazar muchos archivos en el mismo directorio y no funcionó.

for file in ./*; do perl -i -000pe 's/instance$& . ++$n/ge' "$file"; done

errores:

Substitution replacement not terminated at -e line 1.
Substitution replacement not terminated at -e line 1.

y también probé esto: perl -i -pe 's/instant/$& . ++$n/ge' *.vs

Funcionó, pero el índice siguió incrementándose de un archivo a otro. Me gustaría restablecer eso a 1 para el archivo diff. alguna buena sugerencia?

find . -type f -exec perl -pi -e 's/instant/$& . ++$n{$ARGV}/ge' {} +

funciona pero reemplazó todos los demás archivos no deben ser reemplazados. Prefiero reemplazar los archivos solo con "* .txt".

user3342338
fuente
¿Y todos consisten exclusivamente en líneas en blanco o test instant ()?
terdon
Puse las líneas de doble espacio de nuevo, a menudo son una señal de que los nuevos usuarios no saben cómo usar el marcado de este sitio, es por eso que terdon las eliminó al sangrar correctamente el bloque de contenido del archivo para que se muestre como contenido del archivo. Espero que esté bien ahora.
Timo

Respuestas:

13
perl -pe 's/instant/$& . ++$n/ge'

o con GNU awk:

awk -vRS=instant '{$0=n$0;ORS=RT}++n'

Para editar los archivos en el lugar, agregue la -iopción a perl:

perl -pi -e 's/instant/$& . ++$n{$ARGV}/ge' ./*

O recursivamente:

find . -type f -exec perl -pi -e 's/instant/$& . ++$n{$ARGV}/ge' {} +

Explicaciones

perl -pe 's/instant/$& . ++$n/ge'

-pes procesar la entrada línea por línea, evaluar la expresión pasada -epara cada línea e imprimirla. Para cada línea, sustituimos (usando el s/re/repl/flagsoperador) instantpor sí mismo ( $&) y el valor incrementado de una variable ++$n. La gbandera es hacer la sustitución en todo el mundo (no sólo una vez), y epor lo que la sustitución se interpreta como código de Perl para e valuar (no una cadena fija).

Para la edición en el lugar donde una invocación perl procesa más de un archivo, queremos $nrestablecer en cada archivo. En su lugar, usamos $n{$ARGV}(donde $ARGVestá el archivo procesado actualmente).

El awkque merece un poco de explicación.

awk -vRS=instant '{$0=n$0;ORS=RT}++n'

Estamos utilizando la capacidad de GNU awkpara separar registros en cadenas arbitrarias (incluso expresiones regulares). Con -vRS=instant, configuramos el separador de registro en instant. RTes la variable que contiene lo que coincide RS, por lo general, instantexcepto por el último registro donde será la cadena vacía. En la entrada anterior, los registros ( $0) y los terminadores de registro ( RT) son ( [$0|RT]):

[test  |instant][  ()
test  |instant][  ()
...
test  |instant][  ()    //total 1000 lines|]

Entonces, todo lo que tenemos que hacer es insertar un número incremental al comienzo de cada registro, excepto el primero.

Que es lo que hacemos arriba. Para el primer registro, nestará vacío. Configuramos ORS (el separador de registro de salida ) a RT, para que se awk imprima n $0 RT. Lo hace sobre la segunda expresión ( ++n), que es una condición que siempre se evalúa como verdadera (un número distinto de cero) y, por lo tanto, la acción predeterminada (de impresión $0 ORS) se realiza para cada registro.

Stéphane Chazelas
fuente
44
Esto podría usar un poco de explicación .
Gilles 'SO- deja de ser malvado'
4

sedRealmente no es la mejor herramienta para el trabajo, desea algo con mejores capacidades de secuencias de comandos. Aquí hay algunas opciones:

  • perl

    perl -000pe 's/instant/$& . $./e' file 

    El -pmedio "imprime cada línea" después de aplicar cualquier script que se le dé -e. Las -000vueltas en "modo de párrafo" lo que los registros (líneas) se definen por salto de línea consecutiva ( \n) caracteres, esto permite que se maneja correctamente las líneas a doble espacio. $&es el último patrón coincidente y $.es el número de línea actual del archivo de entrada. El een s///eme permite evaluar expresiones en el operador de sustitución.

  • awk (esto supone que sus datos son exactamente como se muestran, con tres campos separados por espacios)

    awk '{if(/./) print $1,$2 ++k,$3; else print}' file 

    Aquí, incrementamos la kvariable ksolo si la línea actual no está vacía, /./en cuyo caso también imprimimos la información necesaria. Las líneas vacías se imprimen tal cual.

  • varias conchas

     n=0; while read -r a b c; do 
       if [ "$a" ] ; then 
          (( n++ ))
          printf "%s %s%s %s\n" "$a" "$b" "$n" "$c"
       else
          printf "%s %s %s\n" "$a" "$b" "$c"
       fi
     done < file 

    Aquí, cada línea de entrada se divide automáticamente en espacios en blanco y los campos se guardan como $a, $by $c. Luego, dentro del ciclo, $cse aumenta en uno para cada línea para la que $ano está vacía y su valor actual se imprime al lado del segundo campo $b,.

NOTA: todas las soluciones anteriores suponen que todas las líneas del archivo tienen el mismo formato. Si no, la respuesta de @ Stephane es el camino a seguir.


Para tratar con muchos archivos y suponer que desea hacer esto a todos los archivos en el directorio actual, puede usar esto:

for file in ./*; do perl -i -000pe 's/instant/$& . $./e' "$file"; done

CUIDADO: Esto supone nombres de archivos simples, sin espacios, si es necesario para hacer frente a algo más complejo, ir a (suponiendo ksh93, zsho bash):

find . -type f -print0 | while IFS= read -r -d ''; do
    perl -i -000pe 's/instant/$& . $./e' "$file"
done
terdon
fuente
el guión perl funciona. Sin embargo, hay un pequeño problema si las líneas son de doble espacio.
user3342338
@ user3342338 sí, eso incrementará el contador ya que estoy usando el número de línea actual. Este es un enfoque muy ingenuo, como dije, el de Stephane es más robusto. Ninguno de estos funciona si tiene líneas en blanco o si alguna de sus líneas se desvía de lo que muestra.
terdon
@ user3342338 ver respuesta actualizada. Todos deberían funcionar ahora para archivos a doble espacio.
terdon
¡Gran respuesta y la opción de métodos alternativos! Gracias
Madivad