Stampa tutto tranne il primo campo con awk


108

Ho un file simile a questo:

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

E vorrei invertire l'ordine, stampando prima tutto tranne $ 1 e poi $ 1:

United Arab Emirates AE

Come posso fare il trucco "tutto tranne il campo 1"?


2
Ciao @cfisher, può essere fatto senza un loop ang senza lo spazio extra.
Juan Diego Godoy Robles

Risposte:


91

Assegnazione $1 funziona ma lascerà uno spazio iniziale:awk '{first = $1; $1 = ""; print $0, first; }'

Puoi anche trovare il numero di colonne in NFe usarlo in un ciclo.


2
Per i totalmente pigri; ecco il codice klashxx .
Serge Stroobandt

1
Grande. Mi sono sbarazzato dello spazio principale con sed: awk {'first = $1; $1=""; print $0'}|sed 's/^ //g'
Thyag

Lo spazio viene facilmente rimosso con VIM premendo 'Ctrl + V Gd' in modalità normale
Santi

107

$1=""lascia uno spazio come ha detto Ben Jackson, quindi usa un forciclo:

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

Quindi se la tua stringa era "uno due tre", l'output sarà:

due
tre

Se vuoi il risultato in una riga, puoi fare come segue:

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

Questo ti darà: "due tre"


4
e uno spazio finale extra
NeronLeVelu

2
meglio usare: awk '{for(i=2;i<=NF;i++){ printf("%s",( (i>2) ? OFS : "" ) $i) } ; print ;}' quale: stampa i campi da 2 a NF, aggiungi il separatore del campo di output se necessario (cioè, tranne prima di $ 2). L'ultima stampa aggiunge una nuova riga finale per terminare la stampa della riga corrente. Quello funzionerà se cambi FS / OFS (cioè, non sarà sempre "spazio")
Olivier Dulac

Il secondo ha funzionato davvero bene per me. Il primo, non tanto. Non sono proprio sicuro del perché. Ha tagliato l'intero testo.
voci

72

Usa il cutcomando con l' --complementopzione:

$ 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

2
Pur non rispondendo alla domanda specifica di awk, l'ho trovato molto utile poiché awk rimuoveva gli spazi duplicati e il taglio non lo fa.
Fmstrat

19
echo a b c | cut -d' ' -f 2- è un'alternativa
Luis

2
Bello: la soluzione @Luis funziona su Mac, che non supporta --complement
metadaddy

21

Forse il modo più conciso:

$ 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

Spiegazione:

$(NF+1)=$1: Generatore di un "nuovo" ultimo campo.

$1="": Imposta il primo campo originale su null

sub(FS,""): Dopo le prime due azioni, {$(NF+1)=$1;$1=""}eliminare il primo separatore di campo utilizzando sub. La stampa finale è implicita.


13
awk '{sub($1 FS,"")}7' YourFile

Rimuovi il primo campo e il separatore e stampa il risultato ( 7è un valore diverso da zero, quindi stampa $ 0).


Migliore risposta! Upvoted. In che modo è diverso dal semplice utilizzo 1? Mi chiedo l'uso di questo modello e volevo capirlo. Grazie!
Abhijeet Rastogi,

10
awk '{ saved = $1; $1 = ""; print substr($0, 2), saved }'

L'impostazione del primo campo su ""lascia una singola copia di OFSall'inizio di $0. Supponendo che OFSsia solo un singolo carattere (per impostazione predefinita, è un singolo spazio), possiamo rimuoverlo con substr($0, 2). Quindi aggiungiamo la copia salvata di $1.


6

Se sei aperto a una soluzione Perl ...

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

è una soluzione semplice con un separatore input / output di uno spazio, che produce:

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

Il prossimo è leggermente più complesso

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

e presuppone che il separatore di input / output sia composto da due spazi:

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

Vengono utilizzate queste opzioni della riga di comando:

  • -n ciclo attorno a ogni riga del file di input, non stampa automaticamente ogni riga

  • -l rimuove le nuove righe prima dell'elaborazione e le aggiunge nuovamente in seguito

  • -amodalità autosplit: suddivide le righe di input nell'array @F. Il valore predefinito è la divisione su spazi bianchi

  • -F modificatore autosplit, in questo esempio si divide su '' (due spazi)

  • -e eseguire il seguente codice perl

@Fè l'array di parole in ogni riga, indicizzato a partire da 0
$#Fè il numero di parole in @F
@F[1..$#F]è una porzione di array dell'elemento 1 fino all'ultimo elemento
@F[1..$#F,0]è una porzione di array dell'elemento 1 attraverso l'ultimo elemento più l'elemento 0


1
L'ho eseguito e avevo un numero in più alla fine, quindi ho usato questa versione: perl -lane 'shift @F; print join "", @F '
Hans Poo

2

Il separatore di campo in gawk (almeno) può essere una stringa così come un carattere (può anche essere una regex). Se i tuoi dati sono coerenti, funzionerà:

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

Sono due spazi tra le virgolette doppie.


La migliore risposta per la situazione a portata di mano, ma, tecnicamente, questo non risponde alla domanda su come stampare tutto tranne il primo campo.
Dan Molding

@DanMoulding: Fino a quando il file è coerente nell'uso di due spazi per separare il codice del paese e non ci sono altre occorrenze di due spazi insieme, la mia risposta non la questione degli indirizzi.
In pausa fino a nuovo avviso.

2
Le persone che arrivano su questa domanda arrivano qui perché vogliono sapere come stampare tutto tranne il primo campo (vedi il titolo della domanda). È così che sono atterrato qui. La tua risposta mostra come stampare il primo campo seguito dal secondo campo. Sebbene questa sia probabilmente la migliore soluzione alla particolare situazione dell'OP, non risolve il problema generale di come stampare tutto tranne il primo campo.
Dan Molding

2

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


2

Spostiamo tutti i record a quello successivo e impostiamo l'ultimo come primo:

$ 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

Spiegazione

  • a=$1 salva il primo valore in una variabile temporanea.
  • for (i=2; i<=NF; i++) $(i-1)=$i salvare l'ennesimo valore del campo nel (N-1) esimo campo.
  • $NF=asalva il primo valore ( $1) nell'ultimo campo.
  • {}1vera condizione per rendere awkeseguire l'azione di default: {print $0}.

In questo modo, se ti capita di avere un altro separatore di campo, anche il risultato è buono:

$ 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

1

Una prima pugnalata sembra funzionare per il tuo caso particolare.

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

1

opzione 1

C'è una soluzione che funziona con alcune versioni di awk:

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

Spiegazione:

       $(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.

Risultato:

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

Tuttavia ciò potrebbe non riuscire con le versioni precedenti di awk.


opzione 2

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

Questo è:

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

Nota che ciò che deve essere cancellato è l'OFS, non il FS. La linea viene ricalcolata quando viene assegnato il campo $ 1. Ciò cambia tutte le esecuzioni di FS in un OFS.


Ma anche questa opzione fallisce ancora con diversi delimitatori, come è chiaramente mostrato cambiando OFS:

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

Quella riga produrrà:

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

Ciò rivela che le serie di FS vengono cambiate in una OFS.
L'unico modo per evitarlo è evitare il ricalcolo del campo.
Una funzione che può evitare il ricalcolo è sub.
Il primo campo potrebbe essere catturato, quindi rimosso da $ 0 con sub, e poi entrambi ristampati.

Opzione 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

Anche se cambiamo FS, OFS e / o aggiungiamo più delimitatori, funziona.
Se il file di input viene modificato in:

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

E il comando cambia in:

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

L'output sarà (conservando ancora i delimitatori):

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

Il comando può essere espanso a diversi campi, ma solo con awk moderni e con l'opzione --re-interval attiva. Questo comando sul file originale:

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

Produrrà questo:

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


0

C'è anche un'opzione sed ...

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

Spiegato ...

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

Spiegato in modo più approfondito ...

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

0

Ancora un altro modo ...

... questo ricongiunge i campi da 2 a NF con FS e genera una riga per riga di input

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

Lo uso con git per vedere quali file sono stati modificati nella mia directory di lavoro:

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

-3

Un altro e semplice modo usando il comando cat

cat filename | awk '{print $2,$3,$4,$5,$6,$1}' > newfilename

Ho downvoted perché questo non è un approccio dinamico. Con questo è necessario conoscere il numero di argomenti e presumere che i dati siano coerenti. I dati non sono quasi mai coerenti e il tuo approccio deve tenerne conto per la maggior parte del tempo.
xh3b4sd
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.