Wie kann ich mehrere Zeilen zu einer Zeile zusammenfügen, mit einem Trennzeichen, in dem sich die Zeichen für neue Zeilen befanden, und ein nachfolgendes Trennzeichen vermeiden und optional leere Zeilen ignorieren?
Beispiel. Stellen Sie sich eine Textdatei foo.txt
mit drei Zeilen vor:
foo
bar
baz
Die gewünschte Ausgabe ist:
foo,bar,baz
Der Befehl, den ich jetzt benutze:
tr '\n' ',' <foo.txt |sed 's/,$//g'
Im Idealfall wäre es ungefähr so:
cat foo.txt |join ,
Was ist:
- der tragbarste, prägnanteste und lesbarste Weg.
- Die präziseste Art, nicht standardmäßige Unix-Tools zu verwenden.
Natürlich könnte ich etwas schreiben oder einfach einen Alias verwenden. Aber ich bin interessiert, die Optionen zu kennen.
Antworten:
Vielleicht ein wenig überraschend,
paste
ist ein guter Weg, dies zu tun:paste -s -d","
Dies behandelt nicht die von Ihnen erwähnten Leerzeilen. Führen Sie dazu
grep
zuerst Ihren Text durch :grep -v '^$' | paste -s -d"," -
quelle
-
am Ende despaste
Befehls hinzufügen, wann immer er gelesen werden sollstdin
. (Einige Versionen vonpaste
solchenstdin
-
paste
! Mir ist aufgefallen, dass nur Einzelzeichen-Trennzeichen zulässig sind, und dies ist\t
standardmäßig der Fall. Um längere Begrenzer zu erreichen (z. B.,
):cat foo.txt | paste -s | sed 's/\t/, /g'
Diese
sed
einzeilige sollte funktionieren -sed -e :a -e 'N;s/\n/,/;ba' file
Prüfung:
[jaypal:~/Temp] cat file foo bar baz [jaypal:~/Temp] sed -e :a -e 'N;s/\n/,/;ba' file foo,bar,baz
Um leere Zeilen zu verarbeiten, können Sie die leeren Zeilen entfernen und an den obigen Einzeiler weiterleiten.
sed -e '/^$/d' file | sed -e :a -e 'N;s/\n/,/;ba'
quelle
sed -e ':a; N; s/\n/,/; ba'
. Dies ist jedoch immer noch eine O (n²) -Methode, da sed jedes Mal eine Substitution durchführt, wenn eine neue Zeile hinzugefügt wird.sed -e ':a; N; $!ba; s/\n/,/g'
ist linear und wird nur einmal ersetzt, nachdem alle Zeilen an den Musterraum von sed angehängt wurden.$!ba
bedeutet "wenn es die letzte Zeile ist ($), springe nicht (!) zu (b) Label: a (a), breche die Schleife"Wie wäre es mit xargs?
für Ihren Fall
$ cat foo.txt | sed 's/$/, /' | xargs
Achten Sie auf die maximale Länge der Eingabe des Befehls xargs. (Dies bedeutet, dass sehr lange Eingabedateien nicht verarbeitet werden können.)
quelle
-L
Flagge auf xargs hilfreich-L 50
für 50 Artikel pro Zeile.Perl:
cat data.txt | perl -pe 'if(!eof){chomp;$_.=","}'
oder doch überraschenderweise kürzer und schneller:
cat data.txt | perl -pe 'if(!eof){s/\n/,/}'
oder, wenn Sie möchten:
cat data.txt | perl -pe 's/\n/,/ unless eof'
quelle
perl -pe 's/\n/,/ unless eof' data.txt
(keine Notwendigkeit für die falsche Katze).Nur zum Spaß, hier ist eine integrierte Lösung
IFS=$'\n' read -r -d '' -a data < foo.txt ; ( IFS=, ; echo "${data[*]}" ; )
Sie können
printf
anstelle von verwenden,echo
wenn der nachfolgende Zeilenumbruch ein Problem darstellt.Dies funktioniert, indem
IFS
die Trennzeichen,read
die aufgeteilt werden, nur auf Zeilenumbruch und nicht auf andere Leerzeichen gesetzt werden. Anschließend wird angegeben, dassread
der Lesevorgang nicht beendet werden soll, bis einnul
Wert erreicht ist , anstatt der normalerweise verwendeten Zeilenumbruchlinie, und jedes gelesene Element in das Array (-a
) eingefügt wird. Daten. Dann wird in einem Subshell , um nicht die clobberIFS
des interaktiv Shell, setzten wirIFS
auf,
und erweitern das Array mit*
, die jedes Element in dem Array mit dem ersten Zeichen in abgrenztIFS
quelle
-d
reinesh
Shell-read
Befehl keine Option enthält .bash
kann auf vielen Systemen gefunden werden, so hat es einen gewissen Nutzen. Wenn Sie möchten, dass Portabilitäts-Arrays wahrscheinlich auch nicht verfügbar sind, können Sie einfach einewhile
Schleife verwenden, um das Fehlen von zu umgehen-d
. Für eine einwandfreie , tragbare All-builtins Version würde wollen Sie so etwas wie ,c= ; while IFS= read -r d ; do if ! [ -z "$d" ] ; then printf "$c$d" ; fi c=, ; done < foo.txt
aber es immer noch nicht fürread
das weiß-r
, aber das könnte weggelassen werden, und nimmt einen eingebautenprintf
, soecho
wahrscheinlich besser ist es , wenn die Effizienz wichtig ist. Trotzdem ist die akzeptierte Antwort viel besser!Ich musste etwas Ähnliches erreichen, indem ich eine durch Kommas getrennte Liste von Feldern aus einer Datei druckte, und war zufrieden damit, STDOUT an
xargs
undruby
wie folgt weiterzuleiten :cat data.txt | cut -f 16 -d ' ' | grep -o "\d\+" | xargs ruby -e "puts ARGV.join(', ')"
quelle
Ich hatte eine Protokolldatei, in der einige Daten in mehrere Zeilen aufgeteilt waren. In diesem Fall war das letzte Zeichen der ersten Zeile das Semikolon (;). Ich habe diese Zeilen mit den folgenden Befehlen verbunden:
for LINE in 'cat $FILE | tr -s " " "|"' do if [ $(echo $LINE | egrep ";$") ] then echo "$LINE\c" | tr -s "|" " " >> $MYFILE else echo "$LINE" | tr -s "|" " " >> $MYFILE fi done
Das Ergebnis ist eine Datei, in der Zeilen, die in der Protokolldatei geteilt wurden, eine Zeile in meiner neuen Datei waren.
quelle
Verwenden Sie Folgendes, um die Zeilen mit vorhandenem Leerzeichen zu verbinden
ex
(wobei auch Leerzeilen ignoriert werden):Wenn Sie die Ergebnisse in der Standardausgabe drucken möchten, versuchen Sie Folgendes:
Verwenden Sie
+%j!
anstelle von Zeilen ohne Leerzeichen+%j
.Um ein anderes Trennzeichen zu verwenden, ist es etwas schwieriger:
ex +"g/^$/d" +"%s/\n/_/e" +%p -scq! foo.txt
Dabei
g/^$/d
(oderv/\S/d
) werden Leerzeilen entfernt und ess/\n/_/
handelt sich um eine Ersetzung, die im Wesentlichen genauso funktioniert wie die Verwendungsed
, jedoch für alle Zeilen (%
). Wenn das Parsen abgeschlossen ist, drucken Sie den Puffer (%p
). Und schließlich wird der Befehl-cq!
viq!
ausgeführt, der im Grunde genommen ohne Speichern beendet wird (-s
um die Ausgabe stumm zu schalten ).Bitte beachten Sie, dass dies
ex
gleichbedeutend ist mitvi -e
.Diese Methode ist ziemlich portabel, da die meisten Linux / Unix-Geräte standardmäßig mit
ex
/ ausgeliefertvi
werden. Und es ist kompatibler als die Verwendung,sed
wenn in-place parameter (-i
) keine Standarderweiterung ist und das Dienstprogramm selbst stärker auf Streams ausgerichtet ist, daher ist es nicht so portabel.quelle
Meine Antwort lautet:
awk '{printf "%s", ","$0}' foo.txt
printf
reicht. Wir müssen das-F"\n"
Feldtrennzeichen nicht ändern.quelle