KeyzerSoze
Anmeldungsdatum: 28. September 2014
Beiträge: 120
|
Hallo ubuntuusers, ich möchte aus einer Datei Text extrahieren und diesen in eine andere Datei an einem bestimmten Punkt einfügen. Ich verwende dafür folgenden Code (kommentiert): ./merge.sh
#! /bin/bash
sed -n "/Anfang/,/Ende/p" data1| head -n-1 > temp #Text kopieren zwischen Token A (inkl.; Anfang) und Token B (exkl.; Ende) in file "temp"
input=$(cat temp) #Diesen Text in Variable speichern
sed -i "/tokenzeile/ { N; s/tokenzeile\n/$input\n&/ }" data
# Fügt in Variable gespeicherten Text vor der "tokenzeile" ein.
rm -f temp # lösche temporäres file
Ich erhalte als Meldung der Bash:
./merge.sh: Zeile 8: /usr/bin/sed: Die Argumentliste ist zu lang Bemerkung: Die Größenordnung des kopierten Textes ist ca. 1MB. Es sind 14.000 Zeilen zu je 6 Spalten mit doubles. Die Fehlermeldung scheint sich also auf die Dateigröße der Variable zu beziehen. Wie kann ich das schlau lösen? Bzw. ist es nicht sinnvoll, den Text vorher in einer Variable zwischen zu speichern? Leider weiß ich nicht, wie ich das auf andere Weise lösen könnte. Danke im Voraus!
|
user_unknown
Anmeldungsdatum: 10. August 2005
Beiträge: 17552
Wohnort: Berlin
|
Kannst Du in Worten sagen, was die Zeile
| sed -i "/tokenzeile/ { N; s/tokenzeile\n/$input\n&/ }" data
|
bewirken soll?
Es soll inplace die Datei data geändert werden, und zwar wo auf das Muster tokenzeile getroffen wird. Soweit klar.
Die nächste Zeile nach tokenzeile soll an den Puffer angehängt werden, der zu dieser Zeit leer ist, dann das Muster tokenzeile gefolgt von Zeilenumbruch mit Input, gefolgt von Zeilenumbruch, gefolgt vom passenden Muster (tokenzeile) ersetzt werden? Links, wenn das Muster bis zum Zeilenende reicht, gehört m.E. ein $ statt des \n hin, weil der Zeilenumbruch als Trenner ausgewertet wird, und nie Teil des Musters wird. Das eigentliche Probleme ist aber $input, das von der Shell ausgewertet und an sed übergeben wird, und alles mögliche an bedeutungsschweren Sonderzeichen enthalten kann oder, wie bei Dir, zu groß sein kann. Vielleicht hilft Dir r (read file) weiter, aber ob das vor oder hinter die tokenzeile gehört müsstest Du, bevor Du -i machst, mal prüfen: | sed '/tokenzeile/r temp' data
|
|
track
Anmeldungsdatum: 26. Juni 2008
Beiträge: 7174
Wohnort: Wolfen (S-A)
|
Das Problem ist: für sed ist der Befehl auf jeden Fall beim Zeilenumbruch zu Ende: track@track:~$ echo '1
2
3
4' | sed '2 s/2/a
b/'
sed: -e Ausdruck #1, Zeichen 7: Nicht beendeter `s'-Befehl Wenn Du dafür aber awk nimmst, geht es auch mit Mehrzeilern: track@track:~$ echo 'a
> b
> c
> d' > einfügen.txt
track@track:~$ echo '1
2
3
4' > einfügen.hier
track@track:~$ ein=$( sed -n '/b/,/d/{/d/q; p}' einfügen.txt )
track@track:~$ echo "$ein"
b
c
track@track:~$ awk -v "var=$ein" '{print} /3/ {print var}' einfügen.hier
1
2
3
b
c
4 Du siehst auch, dass ich das head durch einen Zeilenausschluss im sed ersetzt habe. Wie das mit einer Variablengröße von 1 MB aussieht, habe ich nicht probiert. Aber das sollte gehen, denke ich. Edit: Alternativ kannst Du das Zeug natürlich auch - ganz ohne Variablen - mit sed in Abschnitten zusammenstoppeln: track@track:~$ sed -n '1,/3/p' einfügen.hier; sed -n '/b/,/d/{/d/q; p}' einfügen.txt; sed '1,/3/d' einfügen.hier
1
2
3
b
c
4
LG, track
|
KeyzerSoze
(Themenstarter)
Anmeldungsdatum: 28. September 2014
Beiträge: 120
|
Hallo,
Kannst Du in Worten sagen, was die Zeile
sed -i "/tokenzeile/ { N; s/tokenzeile\n/$input\n&/ }" data
bewirken soll?
Ich habe mich im Wesentlichen an folgendem Artikel orientiert: (und das einzufügende Wort durch eine Variable ersetzt)
http://stackoverflow.com/questions/12248784/shell-bash-insert-text-before-a-certain-line#12248998
$ cat animals
dog
cat
dolphin
cat
$ sed "/cat/ { N; s/cat\n/giraffe\n&/ }" animals
dog
giraffe
cat
dolphin
cat
1. match a line with (/cat/)
2. continue on next line (N)
3. substitute the matched pattern with the insertion and the matched string, where & represent the matched string.
Dieser Code kommt schon sehr nahe an die Lösung heran: sed '/tokenzeile/r temp' data
"Leider" steht aber die tokenzeile vor dem eingefügten Text und nicht hinter dem eingefügten Text, wie ich es gerne hätte. (sed 'r temp /tokenzeile/' data liefert nicht den gewünschten Effekt.)
(sed 'r /tokenzeile/ temp' data liefert nicht den gewünschten Effekt.) Jetzt versuche ich nochmals die Lösung von track zu verstehen, die für mich auf dem ersten Blick leider etwas kryptisch aussieht ( ☺ ), aber ich glaube das kriege ich hin ☺ Kann man eigentlich pauschal sagen, wann für Textbearbeitungen besser awk und wann besser sed geeignet ist?
|
KeyzerSoze
(Themenstarter)
Anmeldungsdatum: 28. September 2014
Beiträge: 120
|
Okay, ein kurzes Update: ./merge.sh
#! /bin/bash
sed -n "/Anfang/,/Ende/{/Ende/q; p}" data1 > temp #Text kopieren zwischen Token A (inkl.; Anfang) und Token B (exkl.; Ende) in file "temp";
input=$(cat temp) #Diesen Text in Variable speichern
awk -v "var=$input" '{print} /Zeilevordereingefügtwerdensoll/ {print var}' data
rm -f temp # lösche temporäres file liefert: $ ./merge.sh
./merge.sh: Zeile 10: /usr/bin/awk: Die Argumentliste ist zu lang
Wie das mit einer Variablengröße von 1 MB aussieht, habe ich nicht probiert. Aber das sollte gehen, denke ich.
Ich fürchte, also doch, falls ich deine Lösung nicht falsch implementiert habe. Falls es wichtig ist (ich habe es leider zu erwähnen vergessen):
Die Zeile /Zeilevordereingefügtwerdensoll/ beinhaltet einen Unterstrich "_"
Müsste ich den irgendwie in
\_/
Slashes setzen? Danke im Voraus!
|
track
Anmeldungsdatum: 26. Juni 2008
Beiträge: 7174
Wohnort: Wolfen (S-A)
|
KeyzerSoze schrieb: $ ./merge.sh
./merge.sh: Zeile 10: /usr/bin/awk: Die Argumentliste ist zu lang
Dann scheint demnach zwar nicht die Variable selbst, aber die Länge der Kommandozeile mit den 1 MB Größe überfordert zu sein ... Wie das mit einer Variablengröße von 1 MB aussieht, habe ich nicht probiert. Aber das sollte gehen, denke ich.
Ich fürchte, also doch, falls ich deine Lösung nicht falsch implementiert habe.
Ja. Die Zeile /Zeilevordereingefügtwerdensoll/ beinhaltet einen Unterstrich "_"
Ich sehe keinen Grund, warum das Probleme machen sollte ...
Wenn Du jetzt sowieso die 2 Teile mit sed zusammenstoppelst, dann kannst Du Dir ja auch aussuchen, ob die Einfügung vor oder nach der Zeile mit dem Stichwort soll. Guck Dir meinen Vorschlag oben noch mal genau an: der besteht nämlich aus 3 Teilen:
drucken von der 1. bis einschließlich der Zielzeile - indem grundsätzlich erstmal nichts gedruckt wird, sondern nur ausdrücklich die 1. Zeile bis zur Zielzeile: | sed -n '1,/3/p' einfügen.hier;
|
Den Teil kennen wir schon, der ist das gleiche wie bei der Geschichte mit der Variablen: | sed -n '/b/,/d/{/d/q; p}' einfügen.txt;
|
durcken des Rests, ausschließlich der Zielzeile - indem erstmal grundsätzlich alles gedruckt wird, aber alles von 1. Zeile bis einschließlich der Zielzeile unterdrückt wird: | sed '1,/3/d' einfügen.hier
|
KeyzerSoze schrieb: "Leider" steht aber die tokenzeile vor dem eingefügten Text und nicht hinter dem eingefügten Text, wie ich es gerne hätte.
Ok, wenn Du es anders herum haben willst, brauchst Du also nur die Methoden 1 und 3 zu vertauschen (und den Bereich natürlich dann auch umdrehen: von der Zielzeile bis zum Ende), dann hast Du die Zielzeile hinter der Einfügung, genau wie Du es möchtest. Kann man eigentlich pauschal sagen, wann für Textbearbeitungen besser awk und wann besser sed geeignet ist?
Nee, so pauschal nicht. Grundsätzlich ist awk komplexer und kann viel mehr, aber speziell bei Zeilen-Bereichen ist eigentlich nur sed so genial einfach. LG, track
|
KeyzerSoze
(Themenstarter)
Anmeldungsdatum: 28. September 2014
Beiträge: 120
|
Ok, wenn Du es anders herum haben willst, brauchst Du also nur die Methoden 1 und 3 zu vertauschen (und den Bereich natürlich dann auch umdrehen: von der Zielzeile bis zum Ende), dann hast Du die Zielzeile hinter der Einfügung, genau wie Du es möchtest.
Okay, ich glaube ich habe Deinen Gedankengang jetzt verstanden ☺ 1. sed -n '1,/Trennpunkt/{/Trennpunkt/q; p}' data > newfile # datafile bis zum Trennpunkt (exklusiv) behalten, den Rest löschen/abschneiden
2. sed -n '/Anfang/,/Trennpunkt2/{/Trennpunkt2/q; p}' data1 >> newfile # extrahierten Text einfügen von data1
3. sed '1,/Trennpunkt/d' data >> newfile # den Rest des Datafiles mit Trennpunkt inklusive einfügen Was noch nicht klappt, ist Schritt 3., der Trennpunkt ist leider nicht dabei.
3. sed '1,/Trennpunkt/{/Trennpunkt/q; d}' data >> newfile # funktioniert nicht, ich verstehe nicht was hier passiert Wie könnte man alternativ die Zielzeile angeben? Um im 3. Schritt statt zu löschen, einfach zu kopieren.
Die Anfangszeile ist ja 1,, aber die Zielzeile ?? Viele Grüße
|
track
Anmeldungsdatum: 26. Juni 2008
Beiträge: 7174
Wohnort: Wolfen (S-A)
|
Ok, also nochmal, von oben: track schrieb: track@track:~$ echo 'a
b
c
d' > einfügen.txt
track@track:~$ echo '1
2
3
4' > einfügen.hier
track@track:~$ sed -n '1,/3/p' einfügen.hier; sed -n '/b/,/d/{/d/q; p}' einfügen.txt; sed '1,/3/d' einfügen.hier
1
2
3
b
c
4
Das schloss die Trennmarke im 1. Block ein. Nun bauen wir nur den 1. Block um, so wie ich sagte: track@track:~$ sed '/3/,$d' einfügen.hier; sed -n '/b/,/d/{/d/q; p}' einfügen.txt; sed '1,/3/d' einfügen.hier
1
2
b
c
4 Damit ist die Trennmarke nicht mehr im 1. Block vorhanden, aber auch nicht im 3. Block, sie fehlt. Also müssen wir jetzt auch noch den 3. Block umkrempeln: track@track:~$ sed '/3/,$d' einfügen.hier; sed -n '/b/,/d/{/d/q; p}' einfügen.txt; sed -n '/3/,$p' einfügen.hier
1
2
b
c
3
4 Jetzt passt's. Und dann noch alles zusammen in eine Datei umleiten: | ( sed '/3/,$d' einfügen.hier; sed -n '/b/,/d/{/d/q; p}' einfügen.txt; sed -n '/3/,$p' einfügen.hier ) > einfügen.neue_datei
|
Dadurch, dass Du die Befehle gruppierst, kannst Du sie gemeinsam in die neue Datei umleiten. LG, track
|
KeyzerSoze
(Themenstarter)
Anmeldungsdatum: 28. September 2014
Beiträge: 120
|
Okay ,$ ist also die Zeichensetzung für die letzte Zeile (bzw. bis zur letzten Zeile), im Vergleich zur ersten 1,''' .
Dadurch, dass Du die Befehle gruppierst, kannst Du sie gemeinsam in die neue Datei umleiten.
Sehr interessant ☺ Vielen Dank für die klare und verständliche Hilfe ☺
|
track
Anmeldungsdatum: 26. Juni 2008
Beiträge: 7174
Wohnort: Wolfen (S-A)
|
KeyzerSoze schrieb: ,$ ist also die Zeichensetzung für die letzte Zeile
Jo. - steht aber alles im sed - Manual drin ! LG, track
|