Wie füge ich alle zwei Zeilen über die Befehlszeile zu einer zusammen?


151

Ich habe eine Textdatei mit dem folgenden Format. Die erste Zeile ist der "SCHLÜSSEL" und die zweite Zeile ist der "WERT".

KEY 4048:1736 string
3
KEY 0:1772 string
1
KEY 4192:1349 string
1
KEY 7329:2407 string
2
KEY 0:1774 string
1

Ich brauche den Wert in der gleichen Zeile wie der Schlüssel. Die Ausgabe sollte also so aussehen ...

KEY 4048:1736 string 3
KEY 0:1772 string 1
KEY 4192:1349 string 1
KEY 7329:2407 string 2
KEY 0:1774 string 1

Es ist besser, wenn ich ein Trennzeichen wie $oder verwenden könnte ,.

KEY 4048:1736 string , 3

Wie füge ich zwei Zeilen zu einer zusammen?


Dafür gibt es viele Möglichkeiten! Ich habe getan , kleine Bank mit pr, paste, awk, xargs, sedundpure bash ! ( xargsist langsamer, langsamer als Bash !)
F. Hauri

Antworten:


182

awk:

awk 'NR%2{printf "%s ",$0;next;}1' yourFile

Beachten Sie, dass am Ende der Ausgabe eine leere Zeile steht.

sed:

sed 'N;s/\n/ /' yourFile

Funktioniert nicht mit farbiger Ausgabe. Ich habe alles in diesem Q & A ausprobiert und nichts hat funktioniert, wenn die Ausgabe ansi-farbig ist. Getestet auf Ubuntu 13.04
Leo Gallucci

1
@elgalu: Weil ANSI-Farben nur eine Reihe von Escape-Zeichenkombinationen sind. Führen Sie einen Hexedit für eine solche Ausgabe durch, um zu sehen, was Sie haben.
not2qubit

7
Diese awk-Lösung kann brechen, wenn printfErweiterungszeichenfolgen wie darin enthalten %ssind $0. Dieser Fehler kann folgendermaßen vermieden werden:'NR%2{printf "%s ",$0;next;}1'
Ghoti

9
Was bedeutet das 1nach dem Schließen der Klammer, weil es wirklich schwer zu googeln ist ?
Erikbwork

5
@ erikb85 Hier gehen Sie stackoverflow.com/questions/24643240/…
Viraj

243

paste ist gut für diesen Job:

paste -d " "  - - < filename

10
Ich denke, dies ist die beste Lösung, obwohl weder sed noch awk verwendet werden. Bei einer Eingabe, die eine ungerade Anzahl von Zeilen ist, überspringt Kents awk-Lösung die letzte neue Zeile, seine sed-Lösung überspringt die letzte Zeile in ihrer Gesamtheit und meine Lösung wiederholt die letzte Zeile. pasteauf der anderen Seite verhält sich perfekt. +1.
Ghoti

8
Ich benutze oft cut, vergesse aber immer paste. Es rockt für dieses Problem. Ich musste alle Zeilen von stdin kombinieren und tat es einfach mit paste -sd ' ' -.
Clint Pachl

4
Einfach und schön!
krlmlr

8
so -gemein stdin, so paste - -gemein lesen von stdin, dann lesen von stdin, du kannst so viele davon stapeln, wie du willst, wie ich erwarte.
ThorSummoner

1
Ja, @ThorSummoner ... Ich musste alle drei Zeilen in eine einzelne Zeile einfügen und habe - - - eingefügt, und es hat perfekt funktioniert.
Daniel Goldfarb

35

Alternative zu sed, awk, grep:

xargs -n2 -d'\n'

Dies ist am besten geeignet, wenn Sie N Zeilen verbinden möchten und nur eine durch Leerzeichen getrennte Ausgabe benötigen.

Meine ursprüngliche Antwort war, xargs -n2dass sich Wörter und nicht Zeilen trennen. -dkann verwendet werden, um die Eingabe durch ein einzelnes Zeichen zu teilen.


4
Dies ist eine nette Methode, aber sie funktioniert mit Wörtern, nicht mit Zeilen. Damit es auf Linien funktioniert, könnte hinzugefügt werden-d '\n'
Don Hatch

2
Wow, ich bin ein normaler xargsBenutzer, wusste das aber nicht. Toller Tipp.
Sridhar Sarnobat

1
Ich liebe es. So sauber.
Alexander Guo

28

Es gibt mehr Möglichkeiten, einen Hund zu töten als zu hängen. [1]

awk '{key=$0; getline; print key ", " $0;}'

Fügen Sie in die Anführungszeichen ein beliebiges Trennzeichen ein.


Verweise:

  1. Ursprünglich "Viele Möglichkeiten, die Katze zu häuten", wurde ein älterer, möglicherweise ursprünglicher Ausdruck verwendet, der auch nichts mit Haustieren zu tun hat.

Ich liebe diese Lösung.
Luis.espinal

5
Als Katzenbesitzer schätze ich diese Art von Humor nicht.
witkacy26

4
@ witkacy26, Angepasster Ausdruck für Ihr Anliegen.
Ghoti

Ich liebe diese awk-Lösung, aber ich verstehe nicht, wie sie funktioniert: S
Rubendob

@Rubendob - awk liest jede Eingabezeile und platziert sie in der Variablen $0. Der getlineBefehl erfasst auch "die nächste" Eingabezeile und platziert sie $0. Die erste Anweisung erfasst also die erste Zeile, und der Befehl print verkettet das, was in der Variablen gespeichert wurde, keymit einer Zeichenfolge, die ein Komma enthält, zusammen mit der Zeile, die mit abgerufen wurde getline. Klarer? :)
Ghoti

12

Hier ist meine Lösung in Bash:

while read line1; do read line2; echo "$line1, $line2"; done < data.txt

11

Obwohl es den Anschein hat, dass die vorherigen Lösungen funktionieren würden, würde die Ausgabe in Stücke gehen, wenn eine einzelne Anomalie im Dokument auftritt. Unten ist ein bisschen sicherer.

sed -n '/KEY/{
N
s/\n/ /p
}' somefile.txt

3
Warum ist es sicherer? Was macht /KEY/das Was macht der pam Ende?
Stewart

die /KEY/Suche nach der Zeile mit dem KEY. das pdruckt das Ergebnis aus. Es ist sicherer, weil es die Operation nur auf Zeilen mit einem KEYdarin anwendet .
Minghua

11

Hier ist ein anderer Weg mit awk:

awk 'ORS=NR%2?FS:RS' file

$ cat file
KEY 4048:1736 string
3
KEY 0:1772 string
1
KEY 4192:1349 string
1
KEY 7329:2407 string
2
KEY 0:1774 string
1

$ awk 'ORS=NR%2?FS:RS' file
KEY 4048:1736 string 3
KEY 0:1772 string 1
KEY 4192:1349 string 1
KEY 7329:2407 string 2
KEY 0:1774 string 1

Wie von Ed Morton in den Kommentaren angegeben, ist es besser, aus Sicherheitsgründen Zahnspangen und aus Gründen der Tragbarkeit Parens hinzuzufügen.

awk '{ ORS = (NR%2 ? FS : RS) } 1' file

ORSsteht für Output Record Separator. Was wir hier tun, ist das Testen einer Bedingung unter Verwendung der, in NRder die Zeilennummer gespeichert ist. Wenn das Modulo von NRein wahrer Wert ist (> 0), setzen wir den Ausgabe-Feldtrenner auf den Wert von FS(Feldtrenner), der standardmäßig Leerzeichen ist, andernfalls weisen wir den Wert von RS(Datensatztrenner) zu, der eine neue Zeile ist.

Wenn Sie ,als Trennzeichen hinzufügen möchten, verwenden Sie Folgendes:

awk '{ ORS = (NR%2 ? "," : RS) } 1' file

1
Auf jeden Fall der richtige Ansatz, also +1, aber ich frage mich, welche Bedingung ausgewertet wird, um die Standardaktion zum Drucken des Datensatzes aufzurufen. Ist die Aufgabe erfolgreich? Ist es einfach ORSund wird so behandelt, als trueob ORS einen Wert erhält, der nicht Null oder eine Nullzeichenfolge ist, und richtig errät, dass es ein Stich statt eines numerischen Vergleichs sein sollte? Ist es etwas anderes? Ich bin mir wirklich nicht sicher und hätte es so geschrieben awk '{ORS=(NR%2?FS:RS)}1' file. Ich habe den ternären Ausdruck in Klammern gesetzt, um auch die Portabilität sicherzustellen.
Ed Morton

1
@EdMorton Ja, ich habe gerade ein paar positive Stimmen zu dieser Antwort gesehen, die gerade aktualisiert werden sollte, um die Sicherheitsklammern aufzunehmen. Fügt auch Parens hinzu.
Jaypal Singh

7

"ex" ist ein skriptfähiger Zeileneditor, der zur selben Familie gehört wie sed, awk, grep usw. Ich denke, es könnte das sein, wonach Sie suchen. Viele moderne vi-Klone / Nachfolger haben auch einen vi-Modus.

 ex -c "%g/KEY/j" -c "wq" data.txt

Dies sagt für jede Zeile, wenn es passt „KEY“ eine ausführen j oin der folgenden Zeile. Nachdem dieser Befehl beendet ist (gegen alle Linien), geben eine w rite und q uit.


4

Wenn Perl eine Option ist, können Sie versuchen:

perl -0pe 's/(.*)\n(.*)\n/$1 $2\n/g' file.txt

Sagt das -0Perl, das Datensatztrennzeichen $/)auf Null zu setzen, damit wir mehrere Zeilen in unserem übereinstimmenden Muster überspannen können. Die Manpages sind etwas zu technisch, als dass ich herausfinden könnte, was dies in der Praxis bedeutet.
Sridhar Sarnobat

4

Sie können awk wie folgt verwenden, um immer zwei Zeilenpaare zu kombinieren:

awk '{ if (NR%2 != 0) line=$0; else {printf("%s %s\n", line, $0); line="";} } \
     END {if (length(line)) print line;}' flle

4

Eine andere Lösung mit vim (nur als Referenz).

Lösung 1 :

Öffnen Sie die Datei in vim vim filenameund führen Sie den Befehl aus:% normal Jj

Dieser Befehl ist sehr einfach zu verstehen:

  • %: für alle Zeilen,
  • normal: normalen Befehl ausführen
  • Jj: Führe den Join-Befehl aus und springe dann zur unteren Zeile

Speichern Sie danach die Datei und beenden Sie mit :wq

Lösung 2 :

Führen Sie den Befehl in der Shell aus, vim -c ":% normal Jj" filenamespeichern Sie die Datei und beenden Sie mit :wq.


Auch norm!robuster als normalim Fall Jneu zugeordnet wurde. +1 für vim Lösung.
Qeatzy

@qeatzy Danke, dass du mir das beigebracht hast. Sehr froh es zu wissen. ^ _ ^
Jensen

3

Sie können auch den folgenden Befehl vi verwenden:

:%g/.*/j

Oder sogar, :%g//jda Sie nur eine Übereinstimmung für den auszuführenden Join benötigen und eine Nullzeichenfolge immer noch eine gültige Regex ist.
Ghoti

1
@ghoti, In Vim wird bei Verwendung von just //stattdessen das vorherige Suchmuster verwendet. Wenn es kein vorheriges Muster gibt, meldet Vim einfach einen Fehler und unternimmt nichts. Die Lösung von Jdamian funktioniert ständig.
Tzunghsing David Wong

1
@TzunghsingDavidWong - das ist ein guter Zeiger für vim-Benutzer. Handlich für mich, weder die Frage noch diese Antwort erwähnten vim.
Ghoti

3

Eine geringfügige Abweichung von der Antwort von Glenn Jackman mit paste: Wenn der Wert für die -dTrennzeichenoption mehr als ein Zeichen enthält, pastewerden die Zeichen einzeln durchlaufen und in Kombination mit den -sOptionen weiterhin ausgeführt, während dieselbe Eingabedatei verarbeitet wird.

Dies bedeutet, dass wir alles, was wir wollen, als Trennzeichen und die Escape-Sequenz verwenden können \n, um zwei Zeilen gleichzeitig zusammenzuführen.

Komma verwenden:

$ paste -s -d ',\n' infile
KEY 4048:1736 string,3
KEY 0:1772 string,1
KEY 4192:1349 string,1
KEY 7329:2407 string,2
KEY 0:1774 string,1

und das Dollarzeichen:

$ paste -s -d '$\n' infile
KEY 4048:1736 string$3
KEY 0:1772 string$1
KEY 4192:1349 string$1
KEY 7329:2407 string$2
KEY 0:1774 string$1

Dies kann nicht verwendet werden, indem ein Trennzeichen verwendet wird, das aus mehreren Zeichen besteht.

Als Bonus pasteändert dies nicht die neue Zeile der letzten Zeile in der Datei , wenn POSIX-kompatibel ist, also für eine Eingabedatei mit einer ungeraden Anzahl von Zeilen wie

KEY 4048:1736 string
3
KEY 0:1772 string

paste wird das Trennungszeichen in der letzten Zeile nicht anheften:

$ paste -s -d ',\n' infile
KEY 4048:1736 string,3
KEY 0:1772 string

1
nawk '$0 ~ /string$/ {printf "%s ",$0; getline; printf "%s\n", $0}' filename

Dies lautet wie folgt

$0 ~ /string$/  ## matches any lines that end with the word string
printf          ## so print the first line without newline
getline         ## get the next line
printf "%s\n"   ## print the whole line and carriage return

1

Für den Fall, dass ich zwei Zeilen kombinieren musste (um die Verarbeitung zu vereinfachen), aber die Daten über das Spezifische hinaus zulassen, fand ich dies nützlich

data.txt

string1=x
string2=y
string3
string4
cat data.txt | nawk '$0 ~ /string1=/ { printf "%s ", $0; getline; printf "%s\n", $0; getline } { print }' > converted_data.txt

Die Ausgabe sieht dann so aus:

convert_data.txt

string1=x string2=y
string3
string4

1

Ein anderer Ansatz mit vim wäre:

:g/KEY/join

Dies gilt a join(für die Zeile darunter) für alle Zeilen, in denen das Wort enthalten KEYist. Ergebnis:

KEY 4048:1736 string 3
KEY 0:1772 string 1
KEY 4192:1349 string 1
KEY 7329:2407 string 2
KEY 0:1774 string 1

0

Der einfachste Weg ist hier:

  1. Entfernen Sie gerade Zeilen und schreiben Sie sie in eine temporäre Datei 1.
  2. Entfernen Sie ungerade Zeilen und schreiben Sie sie in eine temporäre Datei 2.
  3. Kombinieren Sie zwei Dateien in einer, indem Sie den Befehl Einfügen mit -d verwenden (bedeutet Leerzeichen löschen).

sed '0~2d' file > 1 && sed '1~2d' file > 2 && paste -d " " 1 2

0
perl -0pE 's{^KEY.*?\K\s+(\d+)$}{ $1}msg;' data.txt > data_merged-lines.txt

-0verschlingt die gesamte Datei, anstatt sie Zeile für Zeile zu lesen;
pEUmschließt den Code mit einer Schleife und druckt die Ausgabe. Weitere Informationen finden Sie unter http://perldoc.perl.org/perlrun.html .
^KEYÜbereinstimmung mit "KEY" am Zeilenanfang, gefolgt von einer nicht gierigen Übereinstimmung von irgendetwas ( .*?) vor der Sequenz von

  1. ein oder mehrere Leerzeichen \s+jeglicher Art, einschließlich Zeilenumbrüche;
  2. eine oder mehrere Ziffern, (\d+)die wir erfassen und später wieder einfügen als $1;

gefolgt vom Zeilenende $.

\KSchließt bequem alles auf der linken Seite von der Substitution aus und { $1}ersetzt nur 1-2 Sequenzen, siehe http://perldoc.perl.org/perlre.html .


0

Eine allgemeinere Lösung (ermöglicht das Zusammenfügen mehrerer Follow-up-Zeilen) als Shell-Skript. Dies fügt eine Linie zwischen jedem hinzu, weil ich Sichtbarkeit brauchte, aber das ist leicht zu beheben. In diesem Beispiel endete die Zeile "Schlüssel" mit: und keine anderen Zeilen.

#!/bin/bash
#
# join "The rest of the story" when the first line of each   story
# matches $PATTERN
# Nice for looking for specific changes in bart output
#

PATTERN='*:';
LINEOUT=""
while read line; do
    case $line in
        $PATTERN)
                echo ""
                echo $LINEOUT
                LINEOUT="$line"
                        ;;
        "")
                LINEOUT=""
                echo ""
                ;;

        *)      LINEOUT="$LINEOUT $line"
                ;;
    esac        
done

-1

Versuchen Sie die folgende Zeile:

while read line1; do read line2; echo "$line1 $line2"; done <old.txt>new_file

Setzen Sie das Trennzeichen dazwischen

"$line1 $line2";

zB wenn das Trennzeichen ist |, dann:

"$line1|$line2";

Diese Antwort fügt nichts hinzu, was nicht in Hai Vus Antwort enthalten ist , die 4 Jahre vor Ihrer veröffentlicht wurde.
Fedorqui 'SO hör auf zu schaden'

Ich stimme teilweise zu, ich versuche eine Erklärung hinzuzufügen und allgemeiner Es wird auch keine alte Datei bearbeitet. Vielen Dank für Ihren Vorschlag
Suman

-2

Sie können xargswie folgt verwenden:

xargs -a file

% cat> Datei abc% xargs -a Datei abc% Funktioniert für mich
RSG

Es macht etwas, ja, aber nicht das, was das OP verlangt hat. Insbesondere werden so viele Zeilen wie möglich verbunden. Sie könnten tatsächlich bekommen, was Sie wollen, xargs -n 2aber diese Antwort erklärt dies überhaupt nicht.
Tripleee
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.