sed: cómo agregar texto a las primeras x apariciones

sed: cómo agregar texto a las primeras x apariciones

Estoy intentando agregar texto al final de una línea las primeras x veces que ocurre. Sé cómo hacerlo globalmente y por si acaso. No puedo entender cómo hacerlo durante las primeras enésimas apariciones. Un ejemplo sería un archivo text.txt que contenga:

This is a test
junk
This is a test
More junk
This is a test
This is a test
This is a test

Y quiero agregar un '.' al final de las tres primeras veces que aparece "Esto es una prueba". El resultado que estoy tratando de obtener es:

This is a test.
junk
This is a test.
More junk
This is a test.
This is a test
This is a test

Respuesta1

This.*testes la expresión regular correcta. El asterisco significa "0 o más veces el carácter anterior", por lo que This*testno coincidiría con ninguna de sus líneas.

Ahora, Sed es malo en Aritmética. Para algo elegante sugiero Awk:

awk '/This.*test/{c++};{print $0 (c<4 ? "." : "")}' file

Creo que basta con decir que c, como cualquier variable no configurada en Awk, se trata como cero, pero avíseme si necesita más aclaraciones.

Respuesta2

Otra variante que evita hacer la coincidencia de expresiones regulares después de que ya se hayan encontrado las 3 apariciones:

awk -v n=3 'n && /This is a test/ {n--; $0 = $0 "."}; {print}'

Específicamente sed, podrías hacer algo como:

sed '
  1 {
    x
    s/^/.../
    x
  }
  /This is a test/ {
    s/$/./
    x
    s/.//
    /./ {
      x
      b
    }
    g
    :1
    $! {
      n
      b 1
    }
  }'

Donde realizamos un seguimiento del número de .s para agregar como número correspondiente de .s en el espacio de retención.

No hace falta decir que sedes mucho menos apropiado para este tipo de tareas. Si el motivo del deseo sedes la -iextensión para la edición in situ que se encuentra en algunas implementaciones (tomada de perl), tenga en cuenta que la implementación GNU de awktambién puede hacerlo con -i /usr/share/awk/inplace.awk¹, o puede usar la versión real:

perl -lpi -e '
  if ($n < 3 && /This is a test/) {
    $n++;
    $_ .= ".";
  }' your-file

Si desea agregar un .después de cada aparición de This is a testen lugar de todas las líneas que contienen al menos una aparición de This is a test, perltambién sería la mejor opción:

perl -pi -e 's{This is a test\K}{$n++ < 3 ? "." : ""}ge' your-file

¹no utilice-i inplaceas gawkintenta cargar primero la inplaceextensión (como inplaceo inplace.awk) desde el directorio de trabajo actual, donde alguien podría haber colocado malware. La ruta de la inplaceextensión suministrada gawkpuede variar según el sistema; consulte el resultado degawk 'BEGIN{print ENVIRON["AWKPATH"]}'

Respuesta3

Con perlpodríamos hacer lo que se muestra.

perl -lpe '
  $_ = $k == 3 ? next : s/This is a test(?{$k++}).*\K/./r;
' file

Los elefantes también pueden bailar, aunque con pasos sencillos. Usando GNU sedla escritura en su modo de expresión regular extendido, -E podemos almacenar el recuento como número de nuevas líneas en la retención.

K=3
sed -Ee '
  /This is a test/!b
  G
  /(.*\n){'"$K"'}.*\n/!{
    s/\n+/./p;z;H;d
  }
  s/\n+//
  :a;n;ba
' file

información relacionada