Alles außer dem ersten Feld mit awk drucken

108

Ich habe eine Datei, die so aussieht:

AE  United Arab Emirates
AG  Antigua & Barbuda
AN  Netherlands Antilles
AS  American Samoa
BA  Bosnia and Herzegovina
BF  Burkina Faso
BN  Brunei Darussalam

Und ich möchte die Reihenfolge umkehren und zuerst alles außer 1 $ und dann 1 $ drucken:

United Arab Emirates AE

Wie kann ich den Trick "Alles außer Feld 1" machen?

cfischer
quelle
2
Hallo @cfisher, es kann ohne Schleife ohne zusätzlichen Platz gemacht werden.
Juan Diego Godoy Robles

Antworten:

91

Zuweisen von $1Werken, aber es bleibt ein führender Raum:awk '{first = $1; $1 = ""; print $0, first; }'

Sie können auch die Anzahl der Spalten NFin einer Schleife ermitteln und diese verwenden.

Ben Jackson
quelle
2
Für die total Faulen; Hier ist der Code von klashxx .
Serge Stroobandt
1
Toll. awk {'first = $1; $1=""; print $0'}|sed 's/^ //g'
Wurde
Das Leerzeichen kann leicht entfernt werden, indem VIM im normalen Modus 'Strg + V Gd' drückt
Santi
107

$1=""Lässt ein Leerzeichen, wie Ben Jackson erwähnt hat. Verwenden Sie also eine forSchleife:

awk '{for (i=2; i<=NF; i++) print $i}' filename

Wenn Ihre Zeichenfolge also "eins zwei drei" war, lautet die Ausgabe:

zwei
drei

Wenn Sie das Ergebnis in einer Zeile wünschen, können Sie wie folgt vorgehen:

awk '{for (i=2; i<NF; i++) printf $i " "; print $NF}' filename

Dies gibt Ihnen: "zwei drei"

7winkie
quelle
4
und ein zusätzlicher nachlaufender Raum
NeronLeVelu
1
Besser zu verwenden: awk '{for(i=2;i<=NF;i++){ printf("%s",( (i>2) ? OFS : "" ) $i) } ; print ;}' welche: Felder 2 bis NF drucken, nach Bedarf den Ausgabefeldtrenner hinzufügen (dh außer vor $ 2). Beim letzten Druck wird eine letzte neue Zeile hinzugefügt, um den aktuellen Zeilendruck zu beenden. Das wird funktionieren, wenn Sie FS / OFS ändern (dh es wird nicht immer "Leerzeichen" sein)
Olivier Dulac
Der zweite hat wirklich gut für mich funktioniert. Der erste, nicht so sehr. Ich weiß nicht genau warum. Es hat den ganzen Text in Würfel geschnitten.
Stimmen
72

Verwenden Sie den cutBefehl mit der --complementOption:

$ echo a b c | cut -f 1 -d ' '
a
$ echo a b c | cut -f 1,2 -d ' '
a b
$ echo a b c | cut -f 1 -d ' ' --complement
b c
zeleniy
quelle
2
Obwohl ich die für awk spezifische Frage nicht beantwortete, fand ich dies am nützlichsten, da awk doppelte Leerzeichen entfernte und cut nicht.
Fmstrat
19
echo a b c | cut -d' ' -f 2- ist eine Alternative
Luis
2
Nice - @Luis Lösung funktioniert auf dem Mac, der --complement
metadaddy
21

Vielleicht der prägnanteste Weg:

$ awk '{$(NF+1)=$1;$1=""}sub(FS,"")' infile
United Arab Emirates AE
Antigua & Barbuda AG
Netherlands Antilles AN
American Samoa AS
Bosnia and Herzegovina BA
Burkina Faso BF
Brunei Darussalam BN

Erläuterung:

$(NF+1)=$1: Generator eines "neuen" letzten Feldes.

$1="": Setzen Sie das ursprüngliche erste Feld auf null

sub(FS,""): Nach den ersten beiden Aktionen wird {$(NF+1)=$1;$1=""}das erste Feldtrennzeichen mithilfe von sub entfernt. Der endgültige Druck ist implizit.

Juan Diego Godoy Robles
quelle
13
awk '{sub($1 FS,"")}7' YourFile

Entfernen Sie das erste Feld und das Trennzeichen und drucken Sie das Ergebnis ( 7ist ein Wert ungleich Null, drucken Sie also $ 0).

NeronLeVelu
quelle
Beste Antwort! Upvoted. Wie unterscheidet es sich von der Verwendung 1? Ich frage mich, wie dieses Muster verwendet wird und wollte das verstehen. Vielen Dank!
Abhijeet Rastogi
10
awk '{ saved = $1; $1 = ""; print substr($0, 2), saved }'

Wenn Sie das erste Feld so ""einstellen, dass OFSzu Beginn eine einzelne Kopie von übrig bleibt $0. Angenommen, es OFShandelt sich nur um ein einzelnes Zeichen (standardmäßig handelt es sich um ein einzelnes Leerzeichen), können wir es mit entfernen substr($0, 2). Dann hängen wir die gespeicherte Kopie von an $1.

zweifelhaft
quelle
6

Wenn Sie offen für eine Perl-Lösung sind ...

perl -lane 'print join " ",@F[1..$#F,0]' file

ist eine einfache Lösung mit einem Eingabe- / Ausgabetrennzeichen aus einem Raum, die Folgendes erzeugt:

United Arab Emirates AE
Antigua & Barbuda AG
Netherlands Antilles AN
American Samoa AS
Bosnia and Herzegovina BA
Burkina Faso BF
Brunei Darussalam BN

Das nächste ist etwas komplexer

perl -F`  ` -lane 'print join "  ",@F[1..$#F,0]' file

und nimmt an, dass das Eingabe- / Ausgabetrennzeichen zwei Leerzeichen ist:

United Arab Emirates  AE
Antigua & Barbuda  AG
Netherlands Antilles  AN
American Samoa  AS
Bosnia and Herzegovina  BA
Burkina Faso  BF
Brunei Darussalam  BN

Diese Befehlszeilenoptionen werden verwendet:

  • -n Schleife um jede Zeile der Eingabedatei, drucke nicht automatisch jede Zeile

  • -l Entfernt Zeilenumbrüche vor der Verarbeitung und fügt sie anschließend wieder hinzu

  • -aAutosplit-Modus - Teilen Sie die Eingabezeilen in das @ F-Array. Standardmäßig wird auf Leerzeichen aufgeteilt

  • -F Autosplit-Modifikator, in diesem Beispiel auf '' aufgeteilt (zwei Leerzeichen)

  • -e Führen Sie den folgenden Perl-Code aus

@Fist das Array von Wörtern in jeder Zeile, indiziert beginnend mit 0
$#Fist die Anzahl der Wörter in @F
@F[1..$#F]ist ein Array-Slice von Element 1 bis zum letzten Element
@F[1..$#F,0]ist ein Array-Slice von Element 1 bis zum letzten Element plus Element 0

Chris Koknat
quelle
1
Ich habe es ausgeführt und hatte am Ende eine zusätzliche Nummer, also habe ich diese Version verwendet: perl -lane 'shift @F; print join "", @F '
Hans Poo
2

Das Feldtrennzeichen in gawk kann (zumindest) sowohl eine Zeichenfolge als auch ein Zeichen sein (es kann auch eine Regex sein). Wenn Ihre Daten konsistent sind, funktioniert dies:

awk -F "  " '{print $2,$1}' inputfile

Das sind zwei Leerzeichen zwischen den doppelten Anführungszeichen.

Bis auf weiteres angehalten.
quelle
Beste Antwort für die jeweilige Situation, aber technisch gesehen beantwortet dies nicht die Frage, wie alles außer dem ersten Feld gedruckt werden soll.
Dan Moulding
@DanMoulding: Solange die Datei konsistent zwei Leerzeichen zum Trennen des Ländercodes verwendet und keine zwei Leerzeichen zusammen vorkommen, wird die Frage in meiner Antwort beantwortet.
Bis auf weiteres angehalten.
2
Leute, die auf dieser Frage landen, kommen hierher, weil sie wissen wollen, wie man alles außer dem ersten Feld druckt (siehe den Titel der Frage). So bin ich hier gelandet. Ihre Antwort zeigt, wie das erste Feld gefolgt vom zweiten Feld gedruckt wird. Dies ist wahrscheinlich die beste Lösung für die spezielle Situation des OP, löst jedoch nicht das allgemeine Problem, wie alles außer dem ersten Feld gedruckt werden soll.
Dan Moulding
2

awk '{ tmp = $1; sub(/^[^ ]+ +/, ""); print $0, tmp }'

Arkku
quelle
2

Verschieben wir alle Datensätze zum nächsten und setzen den letzten als ersten:

$ awk '{a=$1; for (i=2; i<=NF; i++) $(i-1)=$i; $NF=a}1' file
United Arab Emirates AE
Antigua & Barbuda AG
Netherlands Antilles AN
American Samoa AS
Bosnia and Herzegovina BA
Burkina Faso BF
Brunei Darussalam BN

Erläuterung

  • a=$1 Speichern Sie den ersten Wert in einer temporären Variablen.
  • for (i=2; i<=NF; i++) $(i-1)=$i Speichern Sie den N-ten Feldwert im (N-1) -ten Feld.
  • $NF=aSpeichern Sie den ersten Wert ( $1) im letzten Feld.
  • {}1wahre Bedingung, awkum die Standardaktion auszuführen : {print $0}.

Auf diese Weise ist das Ergebnis auch gut, wenn Sie zufällig ein anderes Feldtrennzeichen haben:

$ cat c
AE-United-Arab-Emirates
AG-Antigua-&-Barbuda
AN-Netherlands-Antilles
AS-American-Samoa
BA-Bosnia-and-Herzegovina
BF-Burkina-Faso
BN-Brunei-Darussalam

$ awk 'BEGIN{OFS=FS="-"}{a=$1; for (i=2; i<=NF; i++) $(i-1)=$i; $NF=a}1' c
United-Arab-Emirates-AE
Antigua-&-Barbuda-AG
Netherlands-Antilles-AN
American-Samoa-AS
Bosnia-and-Herzegovina-BA
Burkina-Faso-BF
Brunei-Darussalam-BN
fedorqui 'SO hör auf zu schaden'
quelle
1

Ein erster Versuch scheint für Ihren speziellen Fall zu funktionieren.

awk '{ f = $1; i = $NF; while (i <= 0); gsub(/^[A-Z][A-Z][ ][ ]/,""); print $i, f; }'
Wesley Rice
quelle
1

Option 1

Es gibt eine Lösung, die mit einigen Versionen von awk funktioniert:

awk '{ $(NF+1)=$1;$1="";$0=$0;} NF=NF ' infile.txt

Erläuterung:

       $(NF+1)=$1                          # add a new field equal to field 1.
                  $1=""                    # erase the contents of field 1.
                        $0=$0;} NF=NF      # force a re-calc of fields.
                                           # and use NF to promote a print.

Ergebnis:

United Arab Emirates AE
Antigua & Barbuda AG
Netherlands Antilles AN
American Samoa AS
Bosnia and Herzegovina BA
Burkina Faso BF
Brunei Darussalam BN

Dies kann jedoch bei älteren Versionen von awk fehlschlagen.


Option 2

awk '{ $(NF+1)=$1;$1="";sub(OFS,"");}1' infile.txt

Das ist:

awk '{                                      # call awk.
       $(NF+1)=$1;                          # Add one trailing field.
                  $1="";                    # Erase first field.
                        sub(OFS,"");        # remove leading OFS.
                                    }1'     # print the line.

Beachten Sie, dass das OFS gelöscht werden muss, nicht das FS. Die Zeile wird neu berechnet, wenn das Feld $ 1 zugewiesen wird. Dadurch werden alle FS-Läufe in einen OFS geändert.


Aber selbst diese Option schlägt bei mehreren Trennzeichen immer noch fehl, wie die Änderung des OFS deutlich zeigt:

awk -v OFS=';' '{ $(NF+1)=$1;$1="";sub(OFS,"");}1' infile.txt

Diese Zeile gibt Folgendes aus:

United;Arab;Emirates;AE
Antigua;&;Barbuda;AG
Netherlands;Antilles;AN
American;Samoa;AS
Bosnia;and;Herzegovina;BA
Burkina;Faso;BF
Brunei;Darussalam;BN

Dies zeigt, dass FS-Läufe in einen OFS geändert werden.
Die einzige Möglichkeit, dies zu vermeiden, besteht darin, die Neuberechnung des Feldes zu vermeiden.
Eine Funktion, die eine Neuberechnung vermeiden kann, ist sub.
Das erste Feld konnte erfasst, dann mit sub von $ 0 entfernt und dann beide neu gedruckt werden.

Option 3

awk '{ a=$1;sub("[^"FS"]+["FS"]+",""); print $0, a;}' infile.txt
       a=$1                                   # capture first field.
       sub( "                                 # replace: 
             [^"FS"]+                         # A run of non-FS
                     ["FS"]+                  # followed by a run of FS.
                            " , ""            # for nothing.
                                  )           # Default to $0 (the whole line.
       print $0, a                   # Print in reverse order, with OFS.


United Arab Emirates AE
Antigua & Barbuda AG
Netherlands Antilles AN
American Samoa AS
Bosnia and Herzegovina BA
Burkina Faso BF
Brunei Darussalam BN

Selbst wenn wir den FS, den OFS ändern und / oder weitere Trennzeichen hinzufügen, funktioniert dies.
Wenn die Eingabedatei geändert wird in:

AE..United....Arab....Emirates
AG..Antigua....&...Barbuda
AN..Netherlands...Antilles
AS..American...Samoa
BA..Bosnia...and...Herzegovina
BF..Burkina...Faso
BN..Brunei...Darussalam

Und der Befehl ändert sich zu:

awk -vFS='.' -vOFS=';' '{a=$1;sub("[^"FS"]+["FS"]+",""); print $0,a;}' infile.txt

Die Ausgabe wird sein (wobei Trennzeichen erhalten bleiben):

United....Arab....Emirates;AE
Antigua....&...Barbuda;AG
Netherlands...Antilles;AN
American...Samoa;AS
Bosnia...and...Herzegovina;BA
Burkina...Faso;BF
Brunei...Darussalam;BN

Der Befehl kann auf mehrere Felder erweitert werden, jedoch nur mit modernen awks und mit aktivierter Option --re-interval. Dieser Befehl in der Originaldatei:

awk -vn=2 '{a=$1;b=$2;sub("([^"FS"]+["FS"]+){"n"}","");print $0,a,b;}' infile.txt

Wird dies ausgeben:

Arab Emirates AE United
& Barbuda AG Antigua
Antilles AN Netherlands
Samoa AS American
and Herzegovina BA Bosnia
Faso BF Burkina
Darussalam BN Brunei
Gemeinschaft
quelle
1

Wenn Sie offen für eine andere Perl-Lösung sind:

perl -ple 's/^(\S+)\s+(.*)/$2 $1/' file
Kjetil S.
quelle
0

Es gibt auch eine Sed-Option ...

 sed 's/\([^ ]*\)  \(.*\)/\2 \1/' inputfile.txt

Erklärt ...

Swap
\([^ ]*\) = Match anything until we reach a space, store in $1
\(.*\)    = Match everything else, store in $2
With
\2        = Retrieve $2
\1        = Retrieve $1

Ausführlicher erklärt ...

s    = Swap
/    = Beginning of source pattern
\(   = start storing this value
[^ ] = text not matching the space character
*    = 0 or more of the previous pattern
\)   = stop storing this value
\(   = start storing this value
.    = any character
*    = 0 or more of the previous pattern
\)   = stop storing this value
/    = End of source pattern, beginning of replacement
\2   = Retrieve the 2nd stored value
\1   = Retrieve the 1st stored value
/    = end of replacement
ZeBadger
quelle
0

Noch ein anderer Weg ...

... verbindet die Felder 2 bis NF wieder mit dem FS und gibt eine Zeile pro Eingabezeile aus

awk '{for (i=2;i<=NF;i++){printf $i; if (i < NF) {printf FS};}printf RS}'

Ich benutze dies mit git, um zu sehen, welche Dateien in meinem Arbeitsverzeichnis geändert wurden:

git diff| \
    grep '\-\-git'| \
    awk '{print$NF}'| \
    awk -F"/" '{for (i=2;i<=NF;i++){printf $i; if (i < NF) {printf FS};}printf RS}'
Rondo
quelle
-3

Eine andere und einfache Möglichkeit, den Befehl cat zu verwenden

cat filename | awk '{print $2,$3,$4,$5,$6,$1}' > newfilename
Skorpion
quelle
Ich habe abgelehnt, weil dies kein dynamischer Ansatz ist. Dazu müssen Sie die Anzahl der Argumente kennen und davon ausgehen, dass Ihre Daten konsistent sind. Daten sind fast nie konsistent und Ihr Ansatz muss dies die meiste Zeit berücksichtigen.
xh3b4sd