staging.inyokaproject.org

Ich verstehe ein sed Script nicht

Status: Ungelöst | Ubuntu-Version: Kubuntu 16.04 (Xenial Xerus)
Antworten |

Whitewolf_Fox

Avatar von Whitewolf_Fox

Anmeldungsdatum:
12. November 2007

Beiträge: Zähle...

Hallo zusammen,

ich komme selbst mit intensivem Googlen bei einem Problem nicht weiter, da alle "Antworten" in einer Form verfasst sind, die voraussetzt das man sed - Profi ist. Bin ich leider nicht.

Ich habe in einem Script, dessen Funktionsweise ich verstehen muss, folgende zwei sed - Aufrufe:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
cat $file |\
sed ':join
/\\$/{N
s/,\\\n[[:space:]]*/,/
b join
}' |\
sed ':join
/\\$/{N
s/\\\n[[:space:]]*/ /
b join
}'

Kann mir jemand bitte erklären was da passiert?

Danke schonmal im Voraus!

toni52

Anmeldungsdatum:
4. März 2015

Beiträge: 664

Hallo,

ich bin jetzt auch nicht der sed-Profi, aber hast mal hier nachgeschaut sed ganz unten bei Links findet man das http://www.tty1.net/sed-tutorium_de.html hier. Scheint recht gut zu sein.

Vielleicht hilfts ja weiter.

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 10978

Soweit ich das verstehe, dient das Skript dazu bestimmte Zeilen zusammenzufügen, die mit einem Backslash (und optional einem Komma davor) enden. Wenn die Zeile mit Komma und Backslash endet, wird das (und das Newline-Zeichen sowie Whitespace am Anfang der folgenden Zeile) durch ein Komma ersetzt, wenn sie nur mit einem Backslash endet, durch ein Leerzeichen.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
cat $file |\            # pipe den Inhalt der Datei mit dem Pfad $file an den nachfolgenden Befehl weiter
sed ':join              # setze eine Marke "join"
/\\$/{N                 # suche eine Zeile, die mit einem Backslash endet. Packe sie mit dem Inhalt der folgenden Zeile (durch ein Newline '\n' getrennt) in den pattern space
s/,\\\n[[:space:]]*/,/  # Ersetze ein Komma gefolgt von einem Backslash und einem Newline und darauf folgenden Whitespace durch ein einzelnes Komma
b join                  # springe zurück zur join Marke
}' |\                   # pipe das Ergebnis an den nachfolgenden Befehl weiter
sed ':join              # setze eine Marke "join"
/\\$/{N                 # suche eine Zeile, die mit einem Backslash endet. Packe sie mit der Folgenden (durch ein Newline '\n' getrennt) in den pattern space
s/\\\n[[:space:]]*/ /   # Ersetze einen Backslash und ein Newline und den darauf folgenden Whitespace durch ein einzelnes Komma
b join                  # springe zurück zur join Marke
}'

Whitewolf_Fox

(Themenstarter)
Avatar von Whitewolf_Fox

Anmeldungsdatum:
12. November 2007

Beiträge: 48

toni52 schrieb:

Hallo,

ich bin jetzt auch nicht der sed-Profi, aber hast mal hier nachgeschaut sed ganz unten bei Links findet man das http://www.tty1.net/sed-tutorium_de.html hier. Scheint recht gut zu sein.

Vielleicht hilfts ja weiter.

Hi toni,

ich hatte jetzt nicht vor für das Verständnis einer Code-Passage langwierig die gesamte sed - Sprache über Tutorials zu lernen. Aber trotzdem danke.

LG

Whitewolf_Fox

(Themenstarter)
Avatar von Whitewolf_Fox

Anmeldungsdatum:
12. November 2007

Beiträge: 48

seahawk1986 schrieb:

Soweit ich das verstehe, dient das Skript dazu bestimmte Zeilen zusammenzufügen, die mit einem Backslash (und optional einem Komma davor) enden. Wenn die Zeile mit Komma und Backslash endet, wird das (und das Newline-Zeichen sowie Whitespace am Anfang der folgenden Zeile) durch ein Komma ersetzt, wenn sie nur mit einem Backslash endet, durch ein Leerzeichen.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
cat $file |\            # pipe den Inhalt der Datei mit dem Pfad $file an den nachfolgenden Befehl weiter
sed ':join              # setze eine Marke "join"
/\\$/{N                 # suche eine Zeile, die mit einem Backslash endet. Packe sie mit dem Inhalt der folgenden Zeile (durch ein Newline '\n' getrennt) in den pattern space
s/,\\\n[[:space:]]*/,/  # Ersetze ein Komma gefolgt von einem Backslash und einem Newline und darauf folgenden Whitespace durch ein einzelnes Komma
b join                  # springe zurück zur join Marke
}' |\                   # pipe das Ergebnis an den nachfolgenden Befehl weiter
sed ':join              # setze eine Marke "join"
/\\$/{N                 # suche eine Zeile, die mit einem Backslash endet. Packe sie mit der Folgenden (durch ein Newline '\n' getrennt) in den pattern space
s/\\\n[[:space:]]*/ /   # Ersetze einen Backslash und ein Newline und den darauf folgenden Whitespace durch ein einzelnes Komma
b join                  # springe zurück zur join Marke
}'

Hi seahawk1986,

vielen Dank schonmal für Deine Mühe und die Erklärungen! Ich verstehe aber leider auch die noch nicht ganz:

  1. Was bedeutet das etwas "in den pattern space" gepackt wird?

  2. Was bewirkt der Sprung zur "join"-Marke - soweit ich das verstehe würde das eine Endlos-Schleife erzeugen (was ja nicht der Fall ist).

  3. Zu Zeile 9: Ersetze durch einen einzelnen Whitespace, oder?

LG

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 10978

Whitewolf_Fox schrieb:

Ich verstehe aber leider auch die noch nicht ganz:

  1. Was bedeutet das etwas "in den pattern space" gepackt wird?

sed verarbeitet den Stream normalerweise zeilenweise (und die Newline-Zeichen werden daher nicht beim matchen/bearbeiten berücksichtigt) - den pattern space kannst du dir als einen String-Buffer vorstellen, auf den Befehle angewendet werden können (vgl. sed Dokumentation, die du mit

info sed

aufrufen kannst).

3.1 How `sed' Works
===================

`sed' maintains two data buffers: the active _pattern_ space, and the
auxiliary _hold_ space. Both are initially empty.

   `sed' operates by performing the following cycle on each line of
input: first, `sed' reads one line from the input stream, removes any
trailing newline, and places it in the pattern space.  Then commands
are executed; each command can have an address associated to it:
addresses are a kind of condition code, and a command is only executed
if the condition is verified before the command is to be executed.

[...]

Um in diesem speziellen Fall mit sed Zeilenenden entfernen zu können, wird der N Befehl genutzt, der die aktuelle und die folgende Zeile durch ein Newline getrennt zusammenführt:

`N'
     Add a newline to the pattern space, then append the next line of
     input to the pattern space.  If there is no more input then `sed'
     exits without processing any more commands.
  1. Was bewirkt der Sprung zur "join"-Marke - soweit ich das verstehe würde das eine Endlos-Schleife erzeugen (was ja nicht der Fall ist).

Der Sprung zurück ist notwendig, damit alle Zeilen mit Komma, Slash bzw. nur einem Slash am Ende alle zusammengefügt werden können. Stell dir vor, du findest eine Zeile, die mit einem Backslash endet, fügst die nachfolgende Zeile an und löscht das Newline-Zeichen. Jetzt würde als nächstes die nächste Zeile ausgewählt werden - was ein Problem verursacht, wenn die Zeile, die du gerade angefügt hast, ebenfalls mit einem Backslash endet. Deswegen springst du in dem Fall effektiv eine Zeile zurück, prüfst erneut, ob sie mit einem Backslash endet, führst falls nötig die Ersetzung durch und wiederholst das so lange, bis alle nötigen Ersetzungen vorgenommen wurden - und wenn er nichts findet, springt er ja nicht zurück zum Label. Edit: Das war Quatsch, der Sprung zum Label ist wie ein GOTO für das sed-Skript, d.h. er springt nicht eine Zeile zurück, sondern wendet die Befehle erneut auf den ganzen Stream an.

  1. Zu Zeile 9: Ersetze durch einen einzelnen Whitespace, oder?

Ja, klassischer C&Paste Fehler, die beiden sed-Aufrufe sind ja sehr ähnlich 😳

Whitewolf_Fox

(Themenstarter)
Avatar von Whitewolf_Fox

Anmeldungsdatum:
12. November 2007

Beiträge: 48

seahawk1986 schrieb:

Whitewolf_Fox schrieb:

Ich verstehe aber leider auch die noch nicht ganz:

  1. Was bedeutet das etwas "in den pattern space" gepackt wird?

sed verarbeitet den Stream normalerweise zeilenweise (und die Newline-Zeichen werden daher nicht beim matchen/bearbeiten berücksichtigt) - den pattern space kannst du dir als einen String-Buffer vorstellen, auf den Befehle angewendet werden können (vgl. sed Dokumentation, die du mit

info sed

aufrufen kannst).

3.1 How `sed' Works
===================

`sed' maintains two data buffers: the active _pattern_ space, and the
auxiliary _hold_ space. Both are initially empty.

   `sed' operates by performing the following cycle on each line of
input: first, `sed' reads one line from the input stream, removes any
trailing newline, and places it in the pattern space.  Then commands
are executed; each command can have an address associated to it:
addresses are a kind of condition code, and a command is only executed
if the condition is verified before the command is to be executed.

[...]

Um in diesem speziellen Fall mit sed Zeilenenden entfernen zu können, wird der N Befehl genutzt, der die aktuelle und die folgende Zeile durch ein Newline getrennt zusammenführt:

`N'
     Add a newline to the pattern space, then append the next line of
     input to the pattern space.  If there is no more input then `sed'
     exits without processing any more commands.

Super, das ist die nachvollziehbarste Erklärung die ich bisher dazu gelesen habe. Vielen Dank!

seahawk1986 schrieb:

Whitewolf_Fox schrieb:

  1. Was bewirkt der Sprung zur "join"-Marke - soweit ich das verstehe würde das eine Endlos-Schleife erzeugen (was ja nicht der Fall ist).

Der Sprung zurück ist notwendig, damit alle Zeilen mit Komma, Slash bzw. nur einem Slash am Ende alle zusammengefügt werden können. Stell dir vor, du findest eine Zeile, die mit einem Backslash endet, fügst die nachfolgende Zeile an und löscht das Newline-Zeichen. Jetzt würde als nächstes die nächste Zeile ausgewählt werden - was ein Problem verursacht, wenn die Zeile, die du gerade angefügt hast, ebenfalls mit einem Backslash endet. Deswegen springst du in dem Fall effektiv eine Zeile zurück, prüfst erneut, ob sie mit einem Backslash endet, führst falls nötig die Ersetzung durch und wiederholst das so lange, bis alle nötigen Ersetzungen vorgenommen wurden - und wenn er nichts findet, springt er ja nicht zurück zum Label. Edit: Das war Quatsch, der Sprung zum Label ist wie ein GOTO für das sed-Skript, d.h. er springt nicht eine Zeile zurück, sondern wendet die Befehle erneut auf den ganzen Stream an.

Ah, und da durch die gerade stattgefundene Veränderung das ganze an der Stelle keinen Match mehr hat, kommt es nicht zu einer Endlosschleife - sonst (ohne Änderung) wäre dem schon so?

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 10978

Whitewolf_Fox schrieb:

Ah, und da durch die gerade stattgefundene Veränderung das ganze an der Stelle keinen Match mehr hat, kommt es nicht zu einer Endlosschleife - sonst (ohne Änderung) wäre dem schon so?

Genau, er springt nur zum Label zürück, solange er etwas gefunden hat (das er dann ersetzt):

1
2
3
4
5
sed ':join              # setze eine Marke "join"
/\\$/{N                 # suche eine Zeile, die mit einem Backslash endet. Packe sie mit dem Inhalt der folgenden Zeile (durch ein Newline '\n' getrennt) in den pattern space
s/,\\\n[[:space:]]*/,/  # Ersetze ein Komma gefolgt von einem Backslash und einem Newline und darauf folgenden Whitespace durch ein einzelnes Komma
b join                  # springe zurück zur join Marke
}'

- mit t statt b könnte man dann sogar noch prüfen, ob überhaupt etwas ersetzt wurde:

3.7 Commands for `sed' gurus
============================

In most cases, use of these commands indicates that you are probably
better off programming in something like `awk' or Perl.  But
occasionally one is committed to sticking with `sed', and these
commands can enable one to write quite convoluted scripts.

`: LABEL'
     [No addresses allowed.]

     Specify the location of LABEL for branch commands.  In all other
     respects, a no-op.

`b LABEL'
     Unconditionally branch to LABEL.  The LABEL may be omitted, in
     which case the next cycle is started.

`t LABEL'
     Branch to LABEL only if there has been a successful `s'ubstitution
     since the last input line was read or conditional branch was taken.
     The LABEL may be omitted, in which case the next cycle is started.
Antworten |