Textverarbeitung - Verbinden Sie alle zwei Zeilen mit Kommas

35

Ich habe mehr als 1000 Zeilen in einer Datei. Die Datei beginnt wie folgt (Zeilennummern hinzugefügt):

Station Name
Station Code
A N DEV NAGAR
ACND
ABHAIPUR
AHA
ABOHAR
ABS
ABU ROAD
ABR

Ich muss dies in eine Datei mit durch Kommas getrennten Einträgen konvertieren, indem ich alle zwei Zeilen verbinde. Die endgültigen Daten sollten so aussehen

Station Name,Station Code
A N DEV NAGAR,ACND
ABHAIPUR,AHA
ABOHAR,ABS
ABU ROAD,ABR
...

Ich habe versucht, ein Shell-Skript und dann ein echoKomma dazwischen zu schreiben . Aber ich denke, ein einfacher, effektiver Einzeiler würde die Arbeit hier vielleicht in sed/ erledigen awk.

Irgendwelche Ideen?

mtk
quelle
@ l0b0 Du hast die Bemerkung des OP überarbeitet, dass die Zeilennummern "nur zur Erklärung da sind" ...
jasonwryan
@ jasonwryan Sorry, ich dachte die Zeilen wären zur Erklärung da.
Analysefehler

Antworten:

39

Einfach benutzen cat(wenn du Katzen magst ;-)) und paste:

cat file.in | paste -d, - - > file.out

Erläuterung: pasteLiest aus einer Reihe von Dateien und fügt die entsprechenden Zeilen zusammen (Zeile 1 aus der ersten Datei mit Zeile 1 aus der zweiten Datei usw.):

paste file1 file2 ...

Anstelle eines Dateinamens können wir -(Bindestrich) verwenden. pasteNimmt die erste Zeile von file1 (das ist stdin). Dann möchte es die erste Zeile aus Datei2 lesen (die auch stdin ist). Da jedoch die erste stdin-Zeile bereits gelesen und verarbeitet wurde, wartet nun auf den Eingabestream die zweite stdin-Zeile, die pasteglücklich mit der ersten zusammenklebt. Die -dOption setzt das Trennzeichen als Komma und nicht als Tabulator.

Alternativ dazu tun

cat file.in | sed "N;s/\n/,/" > file.out

PS Ja, man kann das oben genannte vereinfachen

< file.in sed "N;s/\n/,/" > file.out

oder

< file.in paste -d, - - > file.out

Das hat den Vorteil, nicht zu verwenden cat.

Ich habe dieses Idiom jedoch aus Gründen der Klarheit nicht absichtlich verwendet - es ist weniger ausführlich und ich mag es cat(CATS ARE NICE). Also bitte nicht bearbeiten.

Wenn Sie das Einfügen Katzen vorziehen (Einfügen ist der Befehl, um Dateien horizontal zu verketten, während Katze sie vertikal verkettet), können Sie Folgendes verwenden:

paste file.in | paste -d, - -
Januar
quelle
Um es noch einmal zu erwähnen. Zeilennummern sind nicht Teil der Datei :)
MTK
Der paste Befehl funktioniert einwandfrei. Können Sie das bitte etwas näher erläutern? Die Bindestriche ???
MTK
2
Die Bindestriche bedeuten "read from stdin". Wenn dieselbe Eingabequelle wiederholt wird, kann paste mehrmals pro Ausgabezeile daraus lesen.
dubiousjim
@sch: cool edit, ich werde es nicht berühren :-)
Januar
1
In Bezug auf Ihr catArgument. Geht sed "N;s/\n/,/" file.in > file.outnicht
Bernhard
8

Wenn jemand hier landet und alle Linien zu einem CSV-Einzeiler zusammenfassen möchte, versuchen Sie es

cat file | tr '\n' ','
Darren Weber
quelle
3
sed 'N;s/\n/,/' file

Verbinden Sie (N) mit sed alle 2 Zeilen und ersetzen Sie die neue Zeile (\ n) durch ",".

Guru
quelle
3
paste -sd ',\n' file.in > file.out

Beachten Sie auch, dass wir die Eingabedatei direkt bearbeiten können, da wir lediglich ein Zeichen durch ein anderes ersetzen (jede zweite Zeile durch ein Komma):

paste -sd ',\n' file.in 1<> file.in

(Beachten Sie jedoch, dass dies auf Nicht-Unix-Systemen mit CRLF-Terminatoren (wie Microsoft-Systemen) nicht funktioniert, die von einigen emulierten POSIX-Systemen pastemöglicherweise nicht unter Unix behandelt werden.)

Stéphane Chazelas
quelle
Was hat das 1hier in tut 1<>? ist das ein Tippfehler?
α18sнιη
@ αғsнιη, siehe dies
iruvar
@iruvar danke
αғsнιη
2

Hier ist ein Einzeiler (obwohl möglicherweise Millionen von Befehlsausführern), der reines Bash verwendet:

(IFS=; while read -r name; do read -r code; printf '%s\n" "$name,$code"; done < file.in) > file.out

Ich verwende eine Unterschale (die Klammer), damit ich nicht speichern und wiederherstellen muss IFS. Was man sonst tun sollte, um die Benutzerumgebung nicht durcheinander zu bringen, falls die Quelle stammt. Die Alternative wäre, dass neue IFS passieren nur , readwie in IFS= read -r name, IFS= read -r code.

Die Tatsache, dass alle Befehle in der Schleife in der Shell integriert sind, macht die Leistung akzeptabel und ist sogar schneller als die anderen Lösungen für kleine Dateien. Aber viele Leute würden es für eine schlechte Praxis halten, und man sollte vorsichtig sein, wenn man es auf etwas anderes verallgemeinert.

Gelöscht
quelle
Im Allgemeinen müssen Sie Subshells verwenden, um Umgebungsänderungen zu lokalisieren. Aber in diesem Fall wird es nicht benötigt: Sie können es stattdessen tun while IFS='\n' read -r name; do IFS='\n' read -r code ... done < file.in, eine Redewendung, die ich oft in Shell-Skripten sehe. Das -rFlag readbedeutet "interpretiere das Zeichen '\' gefolgt von dem Zeichen 'n' im Standard-Stream als zwei Zeichen und nicht als Zeilenvorschub". Wahrscheinlich ist es ästhetischer, die Unterschale so zu erstellen, wie Sie es tun, als sie zu wiederholen IFS='\n'.
dubiousjim
@dubiousjim: Das hat -rdie Lösung technisch verbessert. Groß! Ich bin kein Fan von der Idee, eine Veränderung IFSzweimal zu bestehen. Wenn ich eins gelesen hätte, super schön, aber nicht zweimal. Das ist natürlich Ansichtssache . Die Verwendung einer Subshell geht ein bisschen über das allgemeine Bash-Wissen hinaus, das ich sagen würde, sodass viele Leute Probleme haben werden, den Zweck dieser Subshell zu verstehen. Das ist eine schlechte Sache.
Gelöscht
2

Für den vollständigen Satz von Antworten kann eine mögliche awkLösung sein:

awk 'NR%2==1 {printf $0","} NR%2==0 { print $0}' *file*
Bernhard
quelle
@DownVoter: Was ist falsch an meiner Antwort, um eine Downvote zu verdienen? Wie kann es verbessert werden?
Bernhard
Vielleicht weil die faul sind printf? Schlägt in seltenen Fällen fehl, wenn ein Sendername einen Formatbezeichner enthält. (Siehe pastebin.com/wgxFttrJ für ein Beispiel.) Aber das ist nur eine Vermutung, die von Downvote mir nicht.
Manatwork
1

Hoary alte Kastanie einer awkRedewendung

awk '{ORS=NR%2?",":"\n";print}' file
Station Name,Station Code
A N DEV NAGAR,ACND
ABHAIPUR,AHA
ABOHAR,ABS
ABU ROAD,ABR
iruvar
quelle
awk '{ORS=NR%2?",":"\n"};1'ist kürzer und idiom
cuonglm
@ Cuonglm, ich bezweifle es. In diesem Fall ist es immer noch ein Einzeiler, obwohl printdie Absicht klar ist. 1ist für alte awkprint
Hasen
Dies war die erste einfache Lösung, die ich fand und die problemlos auf mehr als 2 Zeilen konfigurierbar war. Ich habe sedeine Weile gekämpft, bevor ich gesucht habe, aber awkdas Kombinieren aller 4 Zeilen wurde einfacher. Sparte mir einen Ausflug ins $EDITOR!
Opello
0

Auch mit Perl möglich,

perl -pe 's/^\d+\.\s+//;$.&1?chomp:print","' file

Gänseblümchen
quelle
0

Beispielsweise:

seq 0 70 | xargs -L 2 | sed 's/ /,/g'

Ausgabe: (Anmerkung: xargs -L number_of_columnsfunktioniert gut mit fast jeder Anzahl von Spalten, nicht nur alle zwei Zeilen)

0,1
2,3
4,5
6,7
8,9
10,11
12,13
14,15
16,17
18,19
20,21
22,23
24,25
26,27
28,29
30,31
32,33
34,35
36,37
38,39
40,41
42,43
44,45
46,47
48,49
50,51
52,53
54,55
56,57
58,59
60,61
62,63
64,65
66,67
68,69
70
jmunsch
quelle