Ist es möglich, mehrere bestimmte Zeilennummern (nicht den Bereich) mit sed abzugleichen?

9

Erwägen:

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

Dies entspricht den Zeilen 3, 4 und 5.

Aber ich versuche nur die Zeilen 3 und 5 (nicht 4) abzugleichen. Und fügen Sie 'Test' nach ihnen hinzu.

Wassermann-Kraft
quelle

Antworten:

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

Wenn die Operation komplexer ist als in diesem Fall, können Sie eine Struktur wie die folgende verwenden:

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

Dh Sie setzen alle Befehle, die Sie brauchen, zwischen b pattern;und b end;.

Und Sie fügen danach alle Ihre Muster (Zeilennummern oder was auch immer) hinzu : pattern;.

Was passiert ist folgendes:

  1. Der erste Befehl springt über den Aktionsteil (möglicherweise ist es einfacher zu lesen, wenn sich die Muster am Anfang und b end;direkt vor dem Aktionsteil befinden).

  2. Wenn ein Muster übereinstimmt, springt die Ausführung zum Aktionsteil. Nach der Aktion springt die Ausführung des Teils zum Ende.

Ich versuche das aufzuräumen:

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

In einer einzigen Zeile wäre wie:

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

Portabel müssen Sie es schreiben:

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

( bOhne Label-Verzweigungen bis zum Ende, sodass Sie kein explizites endLabel benötigen , ;ist ein Zeichen in einem Label in Standardimplementierungen gültig sed.)

Hauke ​​Laging
quelle
4
Wow, sed Unterprogramme. Cool
Glenn Jackman
Wow, könnten Sie diese erweitern? Sie sehen wirklich nützlich aus!
Terdon
der mit 'pattern' und 'action' scheint sehr gut zu sein, da ich 'test' (oder 'lalala') nicht wiederholen muss, muss es aber noch besser verstehen, danke! :)
Wassermann Power
@terdon Was könnte ich?
Hauke ​​Laging
Ich meinte, dass der zweite großartig aussieht, aber schwer zu verstehen ist, und wenn Sie die Zeit haben, würde ich mich über eine Erklärung freuen.
Terdon
5

Mit sed(siehe Antwort von @ HaukeLaging )

Mit awk:

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

Mit perl:

$ echo -e "a\nb\nc\nd\ne\nf\ng\nh" | perl -pe '$_ .="test\n" if $. == 3 || $. == 5'
terdon
quelle
Kann Ihre Perl ein bisschen Golf spielen, mit dem Operator "Smart Match":perl -lnE 'say if $. ~~ @{[3,5]}'
Glenn Jackman
@glennjackman ah, ja in der Tat, danke. Ich muss zugeben, dass ich ein bisschen vorsichtig bin ~~, verstehe es nicht so gut, wie ich sollte. Auch beide sayund ~~brauchen Perl v> = 5.10 richtig?
Terdon
Ich muss 'test' nach diesen Zeilen anhängen, ich sehe jetzt, es wäre besser gewesen, das von Anfang an klargestellt zu haben. Ich habe gerade das OP bearbeitet.
Wassermann Macht
@AquariusPower, perl -pE 'say "test" if $. ~~ @{[4,6]}'- dies stellt die Zeile voran : Perl -pmacht einen impliziten Druck am Ende des angegebenen Programms.
Glenn Jackman
@AquariusPower siehe auch aktualisierte Antwort.
Terdon
3

Wie @HaukeLaging bereits sagte, macht dieser Befehl, was Sie wollen:

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

Die Eingabe kann jetzt ziemlich umständlich werden, wenn Sie beispielsweise 20 Zeilen abgleichen möchten.

In diesen Fällen können Sie stattdessen diesen Befehl verwenden , vorausgesetzt, Ihre Shell unterstützt die Klammererweiterung :

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

(Beachten Sie, dass die geschweiften Klammern und das Komma nicht in Anführungszeichen stehen müssen.)

Infolgedessen übergibt die Shell die Argumente an -e3a testund -e5a testan sed, genau wie der erste Befehl.

Dennis
quelle