Ordnen Sie die Spalten mithilfe des Schnitts neu an


134

Ich habe eine Datei im folgenden Format

Spalte1 Spalte2
str1 1
str2 2
str3 3

Ich möchte, dass die Spalten neu angeordnet werden. Ich habe es unter dem Befehl versucht

cut -f2,1 file.txt

Der Befehl ordnet die Spalten nicht neu an. Irgendeine Idee, warum es nicht funktioniert?

Danke dir.

Antworten:


148

Für die cut(1)Manpage:

Verwenden Sie eins und nur eines von -b, -c oder -f. Jede LISTE besteht aus einem Bereich oder mehreren durch Kommas getrennten Bereichen. Die ausgewählte Eingabe wird in derselben Reihenfolge geschrieben, in der sie gelesen wurde, und genau einmal geschrieben.

Es erreicht zuerst Feld 1, so dass es gedruckt wird, gefolgt von Feld 2.

Verwenden Sie awkstattdessen:

awk '{ print $2 " " $1}' file.txt

12
Es ist schade, cutdass dieser intuitive Befehl zum Nachbestellen nicht unterstützt wird. Ein weiterer Tipp: Sie können awk's -FSund -OFSOptionen verwenden, um benutzerdefinierte Eingabe- und Ausgabefeldtrennzeichen (wie -dund --output-delimiterfür cut) zu verwenden.
Malana

12
Sorry, FSist eine Option, OFSist eine Variable. zBawk -v OFS=";" -F"\t" '{print $2,$1}'
Malana

2
Hinweis für Windows-Benutzer von Git Bash: Wenn Sie eine seltsame Ausgabe des obigen Befehls haben, die aussieht, als würden sich Spalten überschreiben, ist der Wagenrücklauf schuld. Ändern Sie EOL in Ihrer Datei von CRLF in LF.
Jakub.g

1
Wenn Sie die Eingabedatei nicht ändern möchten, können Sie sie auch | sed 's/\r//' | vor dem awk
Weiterleiten

2
Dieser ist sehr einfach, kann aber für einige nützlich sein. Ersetzen Sie einfach das Leerzeichen durch \ t, um es durch Registerkarten neu zu awk '{print $4 "\t" $2 "\t" $6 "\t" $7}' file
ordnen. Wenn

63

Sie können auch kombinieren cutund paste:

paste <(cut -f2 file.txt) <(cut -f1 file.txt)

über Kommentare: Es ist möglich, Bashismen zu vermeiden und eine Instanz des Schnitts zu entfernen, indem Sie Folgendes tun:

paste file.txt file.txt | cut -f2,3

3
Ich bin mir nicht sicher, ob dies als "clever" qualifiziert ist, aber: f = file.txt paste <(cut -f2 $ f) <(cut -f1 $ f). Außerdem stelle ich fest, dass diese Methode am einfachsten ist, wenn Sie viele Spalten haben und sich in großen Blöcken davon bewegen möchten.
Michael Rusch

funktioniert nicht mit Zellen variabler Länge in derselben Spalte
kraymer

2
@kraymer Was meinst du? cutfunktioniert gut für Spalten mit variabler Länge, solange Sie ein eindeutiges Spaltentrennzeichen haben.
Tripleee

1
Um die redundante Datei zu beseitigen, könnten Sie wahrscheinlich tee verwenden:
JJW5432

2
Es ist möglich, bashIsmen zu vermeiden und eine Instanz zu entfernen, cutindem Sie paste file.txt file.txt | cut -f2,3
Folgendes

7

mit nur der Schale,

while read -r col1 col2
do
  echo $col2 $col1
done <"file"

Dies ist sehr oft ineffizient. In der Regel werden Sie beispielsweise feststellen, dass das entsprechende Awk-Skript viel schneller ist. Sie sollten auch darauf achten, die Werte anzugeben "$col2"und "$col1"- die Daten können Shell-Metazeichen oder andere Spielereien enthalten.
Tripleee

7

Sie können Perl dafür verwenden:

perl -ane 'print "$F[1] $F[0]\n"' < file.txt
  • -e Option bedeutet, den Befehl danach auszuführen
  • -n bedeutet zeilenweise lesen (Datei öffnen, in diesem Fall STDOUT, und Zeilen durchlaufen)
  • -a bedeutet, solche Linien in einen Vektor mit dem Namen @F ("F" - like Field) zu teilen. Perl indiziert Vektoren ab 0, im Gegensatz zu cut, das Felder ab Form 1 indiziert.
  • Sie können -F- Muster hinzufügen (ohne Leerzeichen zwischen -F und Muster ), um Muster als Feldtrennzeichen beim Lesen der Datei anstelle des Standard-Leerzeichens zu verwenden

Der Vorteil von Perl besteht darin, dass Sie (wenn Sie Perl kennen) viel mehr Berechnungen für F durchführen können, als Spalten neu anzuordnen.


perlrun (1) behauptet, -a setzt implizit -n, aber wenn ich ohne -n ​​setze, scheint es keine Schleife zu geben. seltsam.
Trenton

Welche Version? perl -ae printfunktioniert wie catbei mir
pwes

5

Verwenden von join:

join -t $'\t' -o 1.2,1.1 file.txt file.txt

Anmerkungen:

  • -t $'\t'In GNU join das intuitivere -t '\t' ohne das $Versagen ( coreutils v8.28 und früher?); Es ist wahrscheinlich ein Fehler, dass eine Problemumgehung wie $notwendig sein sollte. Siehe: Unix Join Separator char .

  • joinbenötigt zwei Dateinamen, obwohl nur eine Datei bearbeitet wird. Wenn Sie zweimal denselben Namen verwenden join, wird die gewünschte Aktion ausgeführt.

  • Für Systeme mit geringen Ressourcen ist der joinPlatzbedarf geringer als bei einigen der in anderen Antworten verwendeten Tools:

    wc -c $(realpath `which cut join sed awk perl`) | head -n -1
      43224 /usr/bin/cut
      47320 /usr/bin/join
     109840 /bin/sed
     658072 /usr/bin/gawk
    2093624 /usr/bin/perl

3

Ich habe gerade an etwas sehr Ähnlichem gearbeitet, bin kein Experte, aber ich dachte, ich würde die Befehle, die ich verwendet habe, teilen. Ich hatte eine mehrspaltige CSV, für die ich nur 4 Spalten benötigte, und dann musste ich sie neu anordnen.

Meine Datei war Pipe '|' abgegrenzt, aber das kann ausgetauscht werden.

LC_ALL=C cut -d$'|' -f1,2,3,8,10 ./file/location.txt | sed -E "s/(.*)\|(.*)\|(.*)\|(.*)\|(.*)/\3\|\5\|\1\|\2\|\4/" > ./newcsv.csv

Zugegeben, es ist wirklich rau und fertig, aber es kann angepasst werden!


Dies beantwortet die gestellte Frage nicht. Im Sinne eines Stapelüberlaufs nehmen Sie sich bitte die Zeit, um ein Problem zu beantworten, bevor Sie posten.
Bill Gale

0

Mit sed

Verwenden Sie sed mit den verschachtelten Unterausdrücken grundlegender regulärer Ausdrücke, um den Spalteninhalt zu erfassen und neu zu ordnen. Dieser Ansatz eignet sich am besten, wenn wie in diesem Fall nur eine begrenzte Anzahl von Schnitten zum Neuordnen von Spalten vorhanden ist.

Die Grundidee besteht darin, interessante Teile des Suchmusters mit \(und zu umgeben \), die im Ersatzmuster wiedergegeben werden können, wobei \#wobei #die sequentielle Position des Unterausdrucks im Suchmuster dargestellt wird.

Beispielsweise:

$ echo "foo bar" | sed "s/\(foo\) \(bar\)/\2 \1/"

Ausbeuten:

bar foo

Text außerhalb eines Unterausdrucks wird gescannt, aber nicht für die Wiedergabe in der Ersatzzeichenfolge beibehalten.

Obwohl in der Frage keine Spalten mit fester Breite erörtert wurden, werden wir hier darauf eingehen, da dies ein würdiges Maß für jede gestellte Lösung ist. Nehmen wir der Einfachheit halber an, dass die Datei durch Leerzeichen getrennt ist, obwohl die Lösung für andere Trennzeichen erweitert werden kann.

Reduzierende Räume

Um die einfachste Verwendung zu veranschaulichen, nehmen wir an, dass mehrere Leerzeichen zu einzelnen Leerzeichen zusammengefasst werden können und die Werte der zweiten Spalte mit EOL (und nicht mit Leerzeichen aufgefüllt) abgeschlossen werden.

Datei:

bash-3.2$ cat f
Column1    Column2
str1       1
str2       2
str3       3
bash-3.2$ od -a f
0000000    C   o   l   u   m   n   1  sp  sp  sp  sp   C   o   l   u   m
0000020    n   2  nl   s   t   r   1  sp  sp  sp  sp  sp  sp  sp   1  nl
0000040    s   t   r   2  sp  sp  sp  sp  sp  sp  sp   2  nl   s   t   r
0000060    3  sp  sp  sp  sp  sp  sp  sp   3  nl 
0000072

Verwandeln:

bash-3.2$ sed "s/\([^ ]*\)[ ]*\([^ ]*\)[ ]*/\2 \1/" f
Column2 Column1
1 str1
2 str2
3 str3
bash-3.2$ sed "s/\([^ ]*\)[ ]*\([^ ]*\)[ ]*/\2 \1/" f | od -a
0000000    C   o   l   u   m   n   2  sp   C   o   l   u   m   n   1  nl
0000020    1  sp   s   t   r   1  nl   2  sp   s   t   r   2  nl   3  sp
0000040    s   t   r   3  nl
0000045

Spaltenbreiten erhalten

Erweitern wir die Methode nun auf eine Datei mit Spalten konstanter Breite, während Spalten unterschiedliche Breiten haben können.

Datei:

bash-3.2$ cat f2
Column1    Column2
str1       1
str2       2
str3       3
bash-3.2$ od -a f2
0000000    C   o   l   u   m   n   1  sp  sp  sp  sp   C   o   l   u   m
0000020    n   2  nl   s   t   r   1  sp  sp  sp  sp  sp  sp  sp   1  sp
0000040   sp  sp  sp  sp  sp  nl   s   t   r   2  sp  sp  sp  sp  sp  sp
0000060   sp   2  sp  sp  sp  sp  sp  sp  nl   s   t   r   3  sp  sp  sp
0000100   sp  sp  sp  sp   3  sp  sp  sp  sp  sp  sp  nl
0000114

Verwandeln:

bash-3.2$ sed "s/\([^ ]*\)\([ ]*\) \([^ ]*\)\([ ]*\)/\3\4 \1\2/" f2
Column2 Column1
1       str1      
2       str2      
3       str3      
bash-3.2$ sed "s/\([^ ]*\)\([ ]*\) \([^ ]*\)\([ ]*\)/\3\4 \1\2/" f2 | od -a
0000000    C   o   l   u   m   n   2  sp   C   o   l   u   m   n   1  sp
0000020   sp  sp  nl   1  sp  sp  sp  sp  sp  sp  sp   s   t   r   1  sp
0000040   sp  sp  sp  sp  sp  nl   2  sp  sp  sp  sp  sp  sp  sp   s   t
0000060    r   2  sp  sp  sp  sp  sp  sp  nl   3  sp  sp  sp  sp  sp  sp
0000100   sp   s   t   r   3  sp  sp  sp  sp  sp  sp  nl 
0000114

Obwohl das Beispiel der Frage keine ungleich langen Zeichenfolgen enthält, unterstützt dieser sed-Ausdruck diesen Fall.

Datei:

bash-3.2$ cat f3
Column1    Column2
str1       1      
string2    2      
str3       3      

Verwandeln:

bash-3.2$ sed "s/\([^ ]*\)\([ ]*\) \([^ ]*\)\([ ]*\)/\3\4 \1\2/" f3
Column2 Column1   
1       str1      
2       string2   
3       str3    
bash-3.2$ sed "s/\([^ ]*\)\([ ]*\) \([^ ]*\)\([ ]*\)/\3\4 \1\2/" f3 | od -a
0000000    C   o   l   u   m   n   2  sp   C   o   l   u   m   n   1  sp
0000020   sp  sp  nl   1  sp  sp  sp  sp  sp  sp  sp   s   t   r   1  sp
0000040   sp  sp  sp  sp  sp  nl   2  sp  sp  sp  sp  sp  sp  sp   s   t
0000060    r   i   n   g   2  sp  sp  sp  nl   3  sp  sp  sp  sp  sp  sp
0000100   sp   s   t   r   3  sp  sp  sp  sp  sp  sp  nl 
0000114

Vergleich mit anderen Methoden der Spaltenumordnung unter der Schale

  • Überraschenderweise eignet sich awk für ein Dateimanipulationswerkzeug nicht zum Schneiden von einem Feld bis zum Ende der Aufzeichnung. In sed kann dies mit regulären Ausdrücken erreicht werden, z. B. \(xxx.*$\)wo xxxist der Ausdruck, der mit der Spalte übereinstimmt.

  • Das Verwenden von Einfügen und Ausschneiden von Subshells wird bei der Implementierung in Shell-Skripten schwierig. Code, der über die Befehlszeile funktioniert, kann nicht analysiert werden, wenn er in ein Shell-Skript eingefügt wird. Zumindest war dies meine Erfahrung (die mich zu diesem Ansatz geführt hat).

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.