2016-06-21 6 views
1

Bitte werfen Sie einen Blick auf die Beispieldatei und die gewünschte Ausgabe unten, um zu verstehen, wonach ich suche.Wie verbindet man Zeilen, die nicht mit einem bestimmten Muster beginnen, mit der vorherigen Zeile in UNIX?

Es kann mit Schleifen in einem Shell-Skript gemacht werden, aber ich habe Mühe, eine awk/sed One Liner zu bekommen.

SampleFile.txt

These are leaves. 
These are branches. 
These are greenery which gives 
oxygen, provides control over temperature 
and maintains cleans the air. 
These are tigers 
These are bears 
and deer and squirrels and other animals. 
These are something you want to kill 
Which will see you killed in the end. 
These are things you must to think to save your tomorrow. 

Wunsch Ausgang

These are leaves. 
These are branches. 
These are greenery which gives oxygen, provides control over temperature and maintains cleans the air. 
These are tigers 
These are bears and deer and squirrels and other animals. 
These are something you want to kill Which will see you killed in the end. 
These are things you must to think to save your tomorrow. 
+0

Und was ist das Muster? Keine Interpunktion und nächste Zeile beginnt mit Kleinbuchstaben? Oder einfach "nicht diese"? –

+0

Muss es ein Einzeiler sein? –

+0

Wenn Sie dies in einem Shell-Skript tun können, rufen Sie dieses Shell-Skript auf. Das ist dein Einzeiler. – Kusalananda

Antwort

3

Bitte versuchen Sie folgendes:

awk 'BEGIN {accum_line = "";} /^These/{if(length(accum_line)){print accum_line; accum_line = "";}} {accum_line = accum_line " " $0;} END {if(length(accum_line)){print accum_line; }}' < data.txt 

Der Code besteht aus drei Teilen:

  1. Der Block markiert durch BEGIN bevor irgendetwas anderes ausgeführt wird. Es ist nützlich für die globale Initialisierung
  2. Der Block markiert durch END wird ausgeführt, wenn die normale Verarbeitung abgeschlossen ist. Es ist gut, die Dinge zu verpacken. Wie das Drucken der letzten gesammelten Daten, wenn diese Zeile These am Anfang hat (in diesem Fall)
  3. Der Rest ist der Code für jede Zeile durchgeführt. Zuerst wird nach dem Muster gesucht und die relevanten Dinge werden erledigt. Zweitens erfolgt die Datenerfassung unabhängig vom String-Inhalt.
+0

Danke - das funktioniert, aber die letzte Zeile fehlt. Es wäre großartig, wenn Sie erklären könnten, wie der Code funktioniert, während ich versuche, meine Lese- und Schreibfähigkeit in awk zu erhöhen. – instinct246

+0

Ich habe den Code behoben und die Erklärung hinzugefügt – GMichael

+0

ah - das funktioniert jetzt! Vielen Dank. – instinct246

1
awk '$1==These{print row;row=$0}$1!=These{row=row " " $0}' 

Sie es von dort nehmen. Leerzeilen, Separatoren,
andere nicht näher bezeichnete Verhalten (ungetestet)

+0

Danke tomc! Ich werde das überprüfen. – instinct246

+0

@Kusalananda macht eine viel bessere Arbeit zu erweitern und zu erklären – tomc

1

kein Einzeiler (aber Ende Antwort sehen!), aber ein awk -script:

#!/usr/bin/awk -f 

NR == 1  { line = $0 } 
/^These/ { print line; line = $0 } 
! /^These/ { line = line " " $0 } 
END   { print line } 

Erläuterung:

Ich sammle, aufbaue Zeilen, die mit "These" beginnen, mit Zeilen, die nicht mit "This" beginnen, und gebe die fertiggestellten Zeilen aus, wenn ich am Anfang die nächste Zeile mit "These" finde.

  1. Speichern Sie die erste Zeile (den ersten "Datensatz").
  2. Wenn die Zeile mit "This" beginnt, drucken Sie die akkumulierte (vorherige, jetzt vollständige) Zeile und ersetzen Sie das, was wir bisher gefunden haben, durch die aktuelle Zeile.
  3. Wenn es nicht mit "Diese" beginnt, akkumulieren Sie die Zeile (z. B. verketten Sie sie mit den zuvor gelesenen unvollständigen Zeilen mit einem Leerzeichen dazwischen).
  4. Wenn keine Eingabe mehr erfolgt, drucken Sie die zuletzt akkumulierte (jetzt vollständige) Zeile aus.

Run wie folgt aus:

$ ./script.awk data.in 

Als Einzeiler:

$ awk 'NR==1{c=$0} /^These/{print c;c=$0} !/^These/{c=c" "$0} END{print c}' data.in 

... aber warum man so etwas auf der Kommandozeile ist mir laufen möchte .

BEARBEITEN Sah, dass es die spezifische Zeichenfolge "Diese" (/^These/) war, was gesucht werden sollte. Zuvor hatte mein Code am Anfang der Zeile nach Großbuchstaben gesucht (/^[A-Z]/).

+0

Fantastisch! Dies funktioniert und außerdem kann ich gründlich verstehen, wie es funktioniert (aus Ihrer detaillierten Erklärung.) Danke! – instinct246

2

Mit sed:

sed ':a;N;/\nThese/!s/\n/ /;ta;P;D' infile 

was

These are leaves. 
These are branches. 
These are greenery which gives oxygen, provides control over temperature and maintains cleans the air. 
These are tigers 
These are bears and deer and squirrels and other animals. 
These are something you want to kill Which will see you killed in the end. 
These are things you must to think to save your tomorrow. 

Hier ist, wie es funktioniert:

sed ' 
:a     # Label to jump to 
N     # Append next line to pattern space 
/\nThese/!s/\n// # If the newline is NOT followed by "These", append 
        # the line by replacing the newline with a space 
ta     # If we changed something, jump to label 
P     # Print part until newline 
D     # Delete part until newline 
' infile 

Die N;P;D ist der idiomatische Weg, um mehrere Zeilen in dem Musterraum zu halten ; Der bedingte Verzweigungsteil kümmert sich um die Situation, in der wir mehr als eine Zeile anhängen.

Dies funktioniert mit GNU sed; für andere seds wie das in Mac OS gefunden, die oneliner aufgeteilt werden muss, um Verzweigungen und Etikett sind in separaten Befehle können die Zeilenumbrüche müssen entwertet werden, und wir brauchen ein zusätzliches Semikolon:

sed -e ':a' -e 'N;/'$'\n''These/!s/'$'\n''/ /;ta' -e 'P;D;' infile 

Dieser letzte Befehl wurde nicht getestet. siehe this answer für Unterschiede zwischen verschiedenen seds und wie man damit umgeht.

Eine weitere Alternative ist die Zeilenumbrüche eingeben wörtlich:

sed -e ':a' -e 'N;/\ 
\These/!s/\ 
//;ta' -e 'P;D;' infile 

Aber dann per Definition, es ist nicht mehr ein Einzeiler.

+0

Danke Benjamin! Wie Sie richtig gesagt haben, funktioniert das in GNU gut, aber gibt den folgenden Fehler in Solaris: "Label zu lang:: a; N;/\ nDiese /! S/\ n//; ta; P; D" (für die erste Befehl). "sed: Befehl verstümmelt: N; /" (Für den zweiten Befehl). Dies ist jedoch sehr nützlich mit den Erläuterungen, die Sie zur Verfügung gestellt haben. Ich werde weiter nachsehen, wie mein Skript auf Solaris läuft. – instinct246

+0

@ instinct246 könnte es mit literarischen Zeilenumbrüche funktionieren, siehe die Ergänzung zu der Antwort. –

1

andere awk, wenn Sie Unterstützung für Multi-char RS hat (gawk hat)

$ awk -v RS="These" 'NR>1{$1=$1; print RS, $0}' file 

These are leaves. 
These are branches. 
These are greenery which gives oxygen, provides control over temperature and maintains cleans the air. 
These are tigers 
These are bears and deer and squirrels and other animals. 
These are something you want to kill Which will see you killed in the end. 
These are things you must to think to save your tomorrow. 

Erklärung die Satzbegrenzer als "Die" Set, den ersten (leer) Rekord überspringt. Feld neu zuweisen, um awk zu erzwingen, um den Datensatz neu zu strukturieren; Druck Datensatz Trennzeichen und den Rest des Datensatzes.

+0

Das wird sich unerwünscht verhalten, wenn "This" in der Mitte einer Zeile erscheint. Das OP sagte, er interessiere sich für "Linien, die mit ... beginnen". Vielleicht wollten Sie 'RS = '(^ | \ n) These'' oder ähnliches verwenden. Außerdem komprimiert es alle Ketten von Leerzeichen zu einzelnen leeren Zeichen. Vielleicht wollten Sie '-F '\ n'' hinzufügen. –

2
$ awk '{printf "%s%s", (NR>1 ? (/^These/?ORS:OFS) : ""), $0} END{print ""}' file 
These are leaves. 
These are branches. 
These are greenery which gives oxygen, provides control over temperature and maintains cleans the air. 
These are tigers 
These are bears and deer and squirrels and other animals. 
These are something you want to kill Which will see you killed in the end. 
These are things you must to think to save your tomorrow. 
Verwandte Themen