• Willkommen im Linux Club - dem deutschsprachigen Supportforum für GNU/Linux. Registriere dich kostenlos, um alle Inhalte zu sehen und Fragen zu stellen.

[gelöst] leere Zeilen entfernen

Hallo,

ich habe jede Menge von Quellcode-Dateien in einem Verzeichnis und an einer Zeile, sagen wir dort stehe "blabla" folgen immer 4 leere Zeilen.
(die sind leider beim vielen Rumprobieren mit sed entstanden ;-) )
Diese möchte ich nun entfernen.
Wie macht man das?
Folgendes funktioniert komischerweise nicht:
Code:
find . -regex ".*\.[ch]p*" | xargs sed 's/blabla\n\n\n\n/blabla/g'

Hm ich bin da ratlos :?

Danke für Hilfe
Christian
 
Habe gerade mal dieses hier ausprobiert:

Code:
grep \.\*\n Datei

...filtert Leerzeilen aus. Schönheitsfehler: Leider filtert es auch soche Zeilen aus, die einen in eckige Klammern gesetzten Text enthalten, wie z.B. "
".

Wäre sicher, dass keine Texte in Eckigen Klammern alleine in einer Zeile stehen, dürfte das hier zum Ziel führen:

Code:
grep \.\*\n Datei >DateiOhneLeerzeilen
 
Hi taki,

ich möchte nicht alle Leerzeilen entfernen, sondern nur die, wo vorher eine bestimmte Zeile steht (ich hatte dafür oben blabla geschrieben).
Wie kann man dies machen?

Vielen Dank
Christian
 
Das Problem ist wohl, dass grep und sed etwas zeilenfixiert sind. Da du weißt, dass genau vier Leerzeilen auf blabla folgen, kannst du folgendes machen:
Code:
sed -e '/blabla/{$!N;$!N;$!N;$!N;s/blabla\n*/blabla\n/}'
Hier wird zuerst getestet, ob die eingelesene Zeile auf blabla passt. Wenn das der Fall ist, dann wird die folgende Gruppe von Kommandos ausgeführt, die in {} eingeschlossen sind:
Jedes $!N liest eine weitere Zeile ein, wenn noch eine da ist. Wir arbeiten uns also in der Datei um vier Zeilen vor. Dann wird blabla mit einer beliebigen Anzahl von Zeilenumbrüchen durch blabla mit einem einzigen Zeilenumbruch ersetzt (hoffe ich).
 
Und wenn "blablaba" kein "@" enthält:

cat "file" | tr "@\n" "\n@" | sed "s/blablabla@@@@@/blablabla@/" | tr "@\n" "\n@" > file.neu

Einfach mal mit tr newline und @ vertauschen.

Haveaniceday

PS: Könnte theoretisch Probleme bei großen Dateien geben. Ich weiss nicht ab wieviel x0kByte
wenn die Datei kein "@" enthalten, da dann alles als eine Zeile an sed geht.
 
Ok Danke, ich werde das dann mal austest. Der letzte Vorschlag ist wohhl am besten oder? (ich steig da nicht so ganz durch)

Leider hatte ich eine Sache noch vergessen zu erwähnen, die das ganze wohl noch komplizierter machen könnte:
nach blabla müssen nicht 4 leere Zeilen folgen. Aber wenn sie folgen, dann sollen sie entfernt werden.
Deswegen müsste ich irgendwie blabla\n\n\n\n durch blabla ersetzen.
Aber das ist mit dem sed wohl problematisch oder?

Grüße
Christian
 
Die "alten" UNIX-Tools wie awk,sed,... arbeiten zeilenorientiert.
=> lese Zeile => verarbeite Zeile...

Dabei werden vorherige Zeilen vergessen.

Der Trick bei tr "@\n" "\n@" ist:
- alle "Zeilenumbrüche" werden umgewandelt in "@", sind also keine Zeilenumbrüche mehr.
- alle "@" werden in Zeilenumbrüche umgewandelt.

Die beiden werden also vertauscht. Nur ersetzen wäre problematisch wenn @ vorkommt.

Zwischen '"vertauschen" ....Befehl.... "rücktauschen"' sieht Befehl die alten Zeilenumbrüche als "@" => Kann zeilenorientiert arbeiten...

Mir ist gerade noch ein Fehler aufgefallen: das "g" fehlte ( für alle ersetzen ).

cat "file" | tr "@\n" "\n@" | sed "s/blablabla@@@@@/blablabla@/g" | tr "@\n" "\n@" > file.neu

Beim Ersetzen musst du aber:
blablabla@@@@@
durch
blablabla@
ersetzen.

Wenn man "@" als Zeilenumbruch liest sieht es so aus:
Code:
blablabla<zeilenumbruch>
<zeilenumbruch>
<zeilenumbruch>
<zeilenumbruch>
<zeilenumbruch>
=>
Code:
blablabla<zeilenumbruch>

Haveaniceday
PS: Ich hoffe meine Erklärung ist nicht zu verwirrend...

Edit: Nur "blablabla" mit 4 Leerzeilen danach würde ersetzt.
"blablabla" mit 3 Leerzeilen und dann Text nicht !
 
für mich klingt das wie eine Aufgabe für Perl oder awk, mit awk würde ich das glaube ich so machen:

Code:
BEGIN { found = 0; }

/blahblah/ { found = 1; print}
/./ && ! /blahblah/ { found = 0; print }
/^$/ { if (found == 0) print; }

als datei speichern und dann
Code:
awk -f datei < inputfile > outputfile

ich gebe jede nicht leere Zeile aus, bei einem blahblah wird ein Flag gesetzt und bei einer Leerzeile wird auf das Flag getestet.
 
Christian123 schrieb:
Hi haveaniceday,

könnte ich statt ein @ auch ein anderes Zeichen verwenden?
Ja, jedes beliebige, welches nicht in "blabla" vorkommt.

@TeXpert,

ich habe die Aufgabe als "genau 4 Leerzeilen unterdrücken" angesehen.
Bei awk oder Perl müsste man über Variablen zählen.
Deshalb habe ich Zeichen umgemappt und dann sed genutzt. Wird dann nur ein Einzeiler

Haveaniceday
 
haveaniceday schrieb:
ich habe die Aufgabe als "genau 4 Leerzeilen unterdrücken" angesehen.
Bei awk oder Perl müsste man über Variablen zählen.
Deshalb habe ich Zeichen umgemappt und dann sed genutzt. Wird dann nur ein Einzeiler

gut bei genau 4 passt das :) aber ich bin einFreund von allgemeinen Lösungen....
das problem beim Ummappen ist halt, dass Du Sicherstellen musst, dass das Zeichen nicht verwendet wird und das kann schon mal ein problem sein.... "@" ist kein seltenes Zeichen und ich persönlich wäre spontan nicht in der Lage definiv zu sagen, dieses Zeichen kommt in meinem Code nicht vor... insbesondere steht da nur Quellcode... und wenn das jetzt ein perl-code ist ;) wird es schon schwer ein passendes Zeichen zu finden...
 
Hi,

wenn ich diese Operation bei jeder Datei in einem Verzeichnis rekursiv durchführen will, muss ich dann
Code:
find . -type f | xargs awk -f awkcode
(wobei in awkcode ebend der Code zum ausführen steht)
schreiben?
 
@TeXpert

"@" darf nur in "blabla" nicht vorkommen ! sonst überall.

Wenn du was haben willst was in deinem Text nicht vorkommt könnte man bestimmt
\0177 nutzen. Ein del ist höchst unwahrscheinlich im Text.

@Christian123,

es ist besser eine Schleife zu verwenden, da du 2 mal den Namen verwenden musst.
Code:
find . -type f | while read name
do
   case $name in
   *.before)
    continue;;
   *)
    ;;
   esac

   if [ ! -r $name.before ]
   then
    mv $name $name.before
   fi
    awk -f awkcode < $name.before > $name
done
- Code ist ungetestet !
- "case" verhindert das "*.before" files angepackt werden
- mv sollte nur ausgeführt werden, wenn noch kein "before" existiert
- => einmalige Sicherung
- => bei mehrfachem Aufruf hast du immer die original Dateien noch.

Viele Grüße,

Haveaniceday

PS: Für mich sieht das eleganter aus es ohne awk zu machen. Obwohl ich eigentlich awk-Fan bin.
Code:
find . -type f | while read name
do
   case $name in
   *.before)
    continue;;
   *)
    ;;
   esac

   if [ ! -r $name.before ]
   then
    mv $name $name.before
   fi
       cat $name.before | tr "@\n" "\n@" | sed "s/blablabla@@@@@/blablabla@/g" | tr "@\n" "\n@" > $name
done
 
das ist jetzt mal auf die schnelle als Einzeiler :)

Code:
find PATH -type f WEITERE_OPTS \( -exec awk -f SCRIPT {} > {}.temp \; -exec mv {}.temp {} \; \)

statt awk natürlich auch mit dem tr+sed möglich.

die find-Op in den Klammern ist ein UND der 2. Teil wird ausgeführt, wenn der 1. wahr ist. Dateinamen sind natürlich wie immer evtl. zu quoten...

Wenn man jetzt mit perl arbeitet kann man sich auch das Temp-File sparen

"@" darf nur in "blabla" nicht vorkommen ! sonst überall.
stimmt, zu schnell gelesen :) irgendwie mag ich diese Ersetzungskonstrukte nicht :) da sträuben sich bei mir die Haare.... das hat irgendwie immer was von Seiteneffekten und ich hasse schon Seiteneffekte beim Programmieren ... daher wäre mir persönlich bei so einer Aufgabe mit einem perl oder awk-Script lieber... aber das ist ja das schöne an Unix
 
Hi,

der Versuch mit:
cat $name.before | tr "@\n" "\n@" | sed "s/blablabla@@@@@/blablabla@/g" | tr "@\n"
hat alles durcheinander gewirbelt.

Zum Glück hat das mit awk geklappt! Ein kleiner Schönheitsfehler war aber noch: ich musste noch alle *.before Dateien killen ;-)

Gruß Christian
 
Christian123 schrieb:
der Versuch mit:
cat $name.before | tr "@\n" "\n@" | sed "s/blablabla@@@@@/blablabla@/g" | tr "@\n"
hat alles durcheinander gewirbelt.
I love perl ...
Code:
cat before | perl -e 'undef $/;$_=<STDIN>;s/(blabla\n)\n{4}/$1/g;print'
Siehe auch
Code:
perldoc perlvar
Da steht so ein ähnliches Beispiel drin.
 
Oben