¿Es posible hacer coincidir múltiples números de línea específicos (no rango) con sed?

9

Considerar:

echo -e "a\nb\nc\nd\ne\nf\ng\nh" | sed '3,5a test'

Esto coincidirá con las líneas 3, 4 y 5.

Pero estoy tratando de hacer coincidir solo las líneas 3 y 5 (no 4). Y agregue 'prueba' después de ellos.

Poder de acuario
fuente

Respuestas:

9
echo ... | sed -e '3a test' -e '5a test'

Si la operación es más compleja que en este caso, puede usar una estructura como esta:

sed 'b pattern; : action; a \
lalala
b end; : pattern; 3b action; 5b action; : end'

Es decir, pones todos los comandos que necesitas entre b pattern;y b end;.

Y agrega todos sus patrones (números de línea o lo que sea) después : pattern;.

Lo que pasa es esto:

  1. El primer comando salta sobre la parte de acción (tal vez sea más fácil de leer si los patrones están al principio y b end;directamente antes de la parte de acción).

  2. Si un patrón coincide, la ejecución salta a la parte de acción. Después de la acción, la ejecución de la parte salta al final.

Intento arreglar esto:

sed '3b action; 5b action; b end; : action; a \
lalala
: end'

En una sola línea sería como:

sed "3b idAction; 5b idAction; b; : idAction; a test"

Portablemente, necesitas escribirlo:

sed '
   3b action
   5b action
   b
   : action
   a\
   lalala'

( bsin ramas de etiqueta hasta el final, por lo que no necesita una endetiqueta explícita , ;es válido un carácter en una etiqueta en sedimplementaciones estándar ).

Hauke ​​Laging
fuente
44
Wow, sed subrutinas. Genial
Glenn Jackman
Wow, ¿podrías ampliar eso? ¡Se ven realmente útiles!
terdon
el que tiene 'patrón' y 'acción' parece muy bueno ya que no necesito repetir 'prueba' (o 'lalala'), ¡aún tengo que entenderlo mejor! :)
Aquarius Power
@terdon ¿Qué podría hacer?
Hauke ​​Laging
Quise decir que el segundo se ve muy bien pero es difícil de entender y si tienes tiempo, agradecería una explicación.
terdon
5

Con sed(ver la respuesta de @ HaukeLaging )

Con awk:

$ echo -e "a\nb\nc\nd\ne\nf\ng\nh" | awk 'NR==3 || NR==5{$0=$0"\ntest"}1;'

Con perl:

$ echo -e "a\nb\nc\nd\ne\nf\ng\nh" | perl -pe '$_ .="test\n" if $. == 3 || $. == 5'
terdon
fuente
Puede jugar un poco a su perl, utilizando el operador "partido inteligente":perl -lnE 'say if $. ~~ @{[3,5]}'
Glenn Jackman
@glennjackman ah, sí, de hecho, gracias. Tengo que admitir que desconfío un poco ~~, no lo entiendo tan bien como debería. Además, ambos sayy ~~necesitan perl v> = 5.10 ¿verdad?
terdon
Necesito agregar 'prueba' después de esas líneas, ahora veo que hubiera sido mejor haberlo aclarado desde el principio. Acabo de editar el OP.
Acuario Power
@AquariusPower, perl -pE 'say "test" if $. ~~ @{[4,6]}'- esto antepone la línea: perl's -phace una impresión implícita al final del programa dado.
Glenn Jackman
@AquariusPower también puede ver la respuesta actualizada.
terdon
3

Como @HaukeLaging aleady dijo, este comando hace lo que quiere:

sed -e'3a test' -e'5a test'

Ahora, esto puede resultar bastante engorroso de escribir si desea hacer coincidir, por ejemplo, 20 líneas.

En estos casos, suponiendo que su shell sea compatible con la expansión de llaves , puede usar este comando en su lugar:

sed -e{3,5}'a test'

(Tenga en cuenta que los corchetes y la coma deben permanecer sin comillas).

Como resultado, la cáscara se pasan los argumentos para -e3a testy -e5a testcon sed, que es exactamente lo que hace el primer comando.

Dennis
fuente