So löschen Sie Leerzeilen ab Zeile 5

10

Ich habe das:

sed -i '/^$/d' temp_spec.rb

Das entfernt leere Zeilen und funktioniert gut. Wie kann ich dies nur für die Zeilen 5-999 (oder idealerweise 5 bis zum Dateiende) tun?

Ich habe es versucht:

sed -n5,999 -i '/^$/d' temp_spec.rb
sed '5,999!d/^$/d' temp_spec.rb

aber keiner hat funktioniert (keine Fehler).

Michael Durrant
quelle
Nur um sicherzugehen: Möchten Sie die Zeilen 1 bis 4 behalten , egal ob sie leer sind oder nicht, oder möchten Sie die Zeilen 1 bis 4 löschen , egal ob sie leer sind oder nicht? (Bisher habe ich das erstere angenommen, aber der zweite Befehl, den Sie versucht haben, lässt mich wundern.)
Uwe

Antworten:

12

Wenn Sie alle Leerzeilen ab Zeile 5 löschen und die Zeilen 1 bis 4 beibehalten möchten, können Sie verwenden

sed -i '5,${;/^$/d;}' temp_spec.rb

Das {ist der Gruppierungsoperator, daher bedeutet der erste Befehl 5,${"von Zeile 5 bis zum Ende von input ( $) die folgenden Befehle bis zum Abgleich ausführen }". Den Befehlen zwischen {und }kann erneut Adressen vorangestellt werden. Der innere Befehl /^$/dbedeutet also "Wenn zwischen Anfang ( ^) und Ende ( $) der Zeile nichts steht , löschen Sie ihn". Sed-Befehle können durch getrennt werden ;. (Dies ist eine schlecht dokumentierte Funktion von sed. Sie wird von den meisten sed-Implementierungen unterstützt, ist jedoch nicht vollständig portierbar .) Wie von Hauke ​​festgestellt, ist das ;After {optional. der vorhergehende }ist jedoch erforderlich.

Wenn Sie alle Leerzeilen ab Zeile 5 und auch die Zeilen 1 bis 4 löschen möchten, ist dies einfacher:

sed -i '1,4d;/^$/d' temp_spec.rb
Uwe
quelle
Nett. Ich wusste nicht, dass Adressen auf diese Weise verschachtelt werden können. Bei mir funktioniert es auch ohne Führung ;.
Hauke ​​Laging
Ja, Sie haben Recht mit dem ersten ;.
Uwe
@Hauke ​​Laging Sie können Befehle in einem sed-Skript durch ';' trennen. Setzen Sie sie stattdessen in die separaten Zeilen. Das ist eine "undokumentierte" Funktion.
Predrag Punosevac
4

Eine weitere Option mit awk:

awk 'NR<5||/./'
Lri
quelle
2
sed '5,${s/^$//; t delete; b end; : delete; d; : end;}' temp_spec.rb

Bearbeiten 1:

Ich soll das erklären, also ...

Das ist unnötig kompliziert. Ich wusste nicht, dass Adressbereiche innerhalb erlaubt sind {}. Also musste ich "leere Zeilen löschen" anders ausdrücken. Der Kernbefehl ist tder von sed if ... then. Twäre einfacher gewesen, ist aber nur für GNU sed verfügbar. Ich zitiere die Manpage:

t label: Wenn as /// eine erfolgreiche Ersetzung seit dem Lesen der letzten Eingabezeile und seit dem letzten t- oder T-Befehl durchgeführt hat, verzweigen Sie zu label. Wenn die Bezeichnung weggelassen wird, verzweigen Sie zum Ende des Skripts.

Ich missbrauche den berühmten sBefehl. Es soll nichts ersetzen, sondern nur prüfen, ob die Leitung leer ist. Es ersetzt also eine leere Zeile durch eine leere Zeile (könnte alles als Ersatz verwenden, da die Zeile sowieso gelöscht wird).

Wenn sein "Ersatz" durchgeführt wurde, ist die Zeile leer. In diesem Fall muss der Befehl dausgeführt werden. Sonst ist nichts zu tun. Da tim Falle einer sAktion nicht gesprungen wird, muss der Verzweigungsbefehl bzum Ende des Skripts springen. : labelsind Zweigziele. Wie gotodamals in den dunklen Zeiten (als sed erfunden wurde ... te-hee).

Eine andere Möglichkeit wäre, salle nicht leeren Zeilen zu "ersetzen", was den skomplizierteren, aber den Rest des Befehls einfacher macht:

sed '5,${s/^\(..*\)$/\1/; t end; d; : end;}' input

^..*$bedeutet "nicht leere Zeile" und \1bedeutet "den Inhalt der ersten Klammern".

Hauke ​​Laging
quelle
Ein bisschen Erklärung für OP (ich kann fühlen, wie ihr Gehirn versucht, durch die Ohren zu kriechen,
wenn es