Est-il possible de faire correspondre plusieurs numéros de ligne spécifiques (pas de plage) avec sed?

9

Considérer:

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

Cela correspondra aux lignes 3, 4 et 5.

Mais j'essaie de faire correspondre uniquement les lignes 3 et 5 (pas 4). Et ajoutez «test» après eux.

Puissance du Verseau
la source

Réponses:

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

Si l'opération est plus complexe que dans ce cas, vous pouvez utiliser une structure comme celle-ci:

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

C'est-à-dire que vous mettez toutes les commandes dont vous avez besoin entre b pattern;et b end;.

Et vous ajoutez tous vos modèles (numéros de ligne ou autre) après : pattern;.

Ce qui se passe est le suivant:

  1. La première commande saute sur la partie action (il est peut-être plus facile de lire si les motifs sont au début et b end;juste avant la partie action).

  2. Si un modèle correspond, l'exécution passe à la partie action. Après l'exécution, l'exécution de la partie passe à la fin.

J'essaye de ranger ça:

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

En une seule ligne serait comme:

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

Portablement, vous devez l'écrire:

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

( bsans les branches d'étiquette à la fin, vous n'avez donc pas besoin d'une endétiquette explicite , ;c'est un caractère valide dans une étiquette dans les sedimplémentations standard ).

Hauke ​​Laging
la source
4
Wow, sous-programmes sed. Cool
Glenn Jackman
Wow en effet, pourriez-vous développer cela? Ils ont l'air vraiment utiles!
terdon
celui avec 'pattern' et 'action' semble très bon car je n'ai pas besoin de répéter 'test' (ou 'lalala'), je dois encore mieux le comprendre tho thx! :)
Aquarius Power
@terdon Que pourrais-je?
Hauke ​​Laging
Je voulais dire que le second était super mais difficile à comprendre et si vous avez le temps, j'apprécierais une explication.
terdon
5

Avec sed(voir la réponse de @ HaukeLaging )

Avec awk:

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

Avec perl:

$ echo -e "a\nb\nc\nd\ne\nf\ng\nh" | perl -pe '$_ .="test\n" if $. == 3 || $. == 5'
terdon
la source
Peut jouer un peu à votre perl en utilisant l'opérateur "smart match":perl -lnE 'say if $. ~~ @{[3,5]}'
glenn jackman
@glennjackman ah, oui en effet, merci. Je dois admettre que je me méfie un peu ~~, je ne le comprends pas aussi bien que je le devrais. Aussi, les deux sayet ont ~~besoin de perl v> = 5.10 non?
terdon
Je dois ajouter «test» après ces lignes, je vois maintenant qu'il aurait mieux valu clarifier cela depuis le début. Je viens d'éditer l'OP.
Aquarius Power
@AquariusPower, perl -pE 'say "test" if $. ~~ @{[4,6]}'- ceci ajoute la ligne: perl -pfait une impression implicite à la fin du programme donné.
glenn jackman
@AquariusPower voir également la réponse mise à jour.
terdon
3

Comme l'a déjà dit @HaukeLaging, cette commande fait ce que vous voulez:

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

Maintenant, cela peut devenir assez lourd à taper si vous voulez faire correspondre, par exemple, 20 lignes.

Dans ces cas, en supposant que votre shell prend en charge l' expansion d'accolade , vous pouvez utiliser cette commande à la place:

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

(Notez que les accolades et la virgule doivent rester sans guillemets.)

En conséquence, le shell passera les arguments à -e3a testet -e5a testà sed, ce qui est exactement ce que fait la première commande.

Dennis
la source