Drukowanie wszystkiego oprócz pierwszego pola za pomocą awk

108

Mam plik, który wygląda tak:

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

Chciałbym odwrócić kolejność, drukując najpierw wszystko oprócz 1 $, a potem 1 $:

United Arab Emirates AE

Jak mogę zrobić sztuczkę „wszystko oprócz pola 1”?

cfischer
źródło
2
Cześć @cfisher, można to zrobić bez pętli bez dodatkowej przestrzeni.
Juan Diego Godoy Robles

Odpowiedzi:

91

Przypisanie $1działa, ale pozostawi spację wiodącą:awk '{first = $1; $1 = ""; print $0, first; }'

Możesz także znaleźć liczbę kolumn NFi użyć jej w pętli.

Ben Jackson
źródło
2
Dla całkowicie leniwych; tutaj jest kod klashxx ' .
Serge Stroobandt
1
Wspaniały. Pozbyłem się wiodącej pozycji dzięki awk {'first = $1; $1=""; print $0'}|sed 's/^ //g'
sedowi
Spację można łatwo usunąć za pomocą VIM-a naciskając 'Ctrl + V Gd' w trybie normalnym
Santi
107

$1=""pozostawia miejsce, o czym wspomniał Ben Jackson, więc użyj forpętli:

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

Więc jeśli twój ciąg to „jeden, dwa, trzy”, wynik będzie taki:

dwa
trzy

Jeśli chcesz, aby wynik był w jednym wierszu, możesz wykonać następujące czynności:

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

To da ci: „dwa, trzy”

7winkie
źródło
4
i dodatkową spację na końcu
NeronLeVelu
2
lepiej użyć: awk '{for(i=2;i<=NF;i++){ printf("%s",( (i>2) ? OFS : "" ) $i) } ; print ;}' co: drukuje pola 2 do NF, dodaje separator pól wyjściowych w razie potrzeby (tj. z wyjątkiem przed $ 2). Ostatni wydruk dodaje ostatnią nową linię, aby zakończyć drukowanie bieżącej linii. Ten zadziała, jeśli zmienisz FS / OFS (tj. Nie zawsze będzie to „spacja”)
Olivier Dulac,
Drugi okazał się dla mnie naprawdę niezły. Pierwszy, nie tak bardzo. Nie bardzo wiem dlaczego. Cały tekst pokroił w kostkę.
głosy
72

Użyj cutpolecenia z --complementopcją:

$ 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
źródło
2
Nie odpowiadając na pytanie specyficzne dla awk, uznałem to za najbardziej przydatne, ponieważ awk usuwa zduplikowane spacje, a cut nie.
Fmstrat
19
echo a b c | cut -d' ' -f 2- jest alternatywą
Luis
2
Nieźle - rozwiązanie @Luis działa na Macu, które nie obsługuje --complement
metadaddy
21

Może najbardziej zwięzły sposób:

$ 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

Wyjaśnienie:

$(NF+1)=$1: Generator „nowego” ostatniego pola.

$1="": Ustaw pierwotne pierwsze pole na wartość null

sub(FS,""): Po pierwszych dwóch akcjach {$(NF+1)=$1;$1=""}pozbądź się pierwszego separatora pól za pomocą sub. Ostateczny wydruk jest niejawny.

Juan Diego Godoy Robles
źródło
13
awk '{sub($1 FS,"")}7' YourFile

Usuń pierwsze pole i separator, a następnie wydrukuj wynik ( 7jest to wartość różna od zera, więc wypisuje się $ 0).

NeronLeVelu
źródło
Najlepsza odpowiedź! Głosowano za. Czym to się różni od zwykłego używania 1? Zastanawiam się nad zastosowaniem tego wzoru i chciałem to zrozumieć. dzięki!
Abhijeet Rastogi
10
awk '{ saved = $1; $1 = ""; print substr($0, 2), saved }'

Ustawienie pierwszego pola na ""pozostawienie pojedynczej kopii OFSna początku $0. Zakładając, że OFSjest to tylko jeden znak (domyślnie jest to pojedyncza spacja), możemy go usunąć za pomocą substr($0, 2). Następnie dołączamy zapisaną kopię pliku $1.

wątpliwyjim
źródło
6

Jeśli jesteś otwarty na rozwiązanie Perla ...

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

to proste rozwiązanie z separatorem wejścia / wyjścia jednej spacji, które daje:

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

Ten następny jest nieco bardziej złożony

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

i zakłada, że ​​separatorem wejścia / wyjścia są dwie spacje:

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

Używane są następujące opcje wiersza polecenia:

  • -n pętla wokół każdego wiersza pliku wejściowego, nie drukuj automatycznie każdego wiersza

  • -l usuwa nowe wiersze przed przetworzeniem i dodaje je później

  • -atryb autosplit - dzieli linie wejściowe na tablicę @F. Domyślnie dzielenie na białe znaki

  • -F modyfikator autosplit, w tym przykładzie dzieli się na „” (dwie spacje)

  • -e wykonaj następujący kod Perla

@Fto tablica słów w każdym wierszu indeksowana od 0
$#Fto liczba słów w @F
@F[1..$#F]tablicy kawałek od elementu 1 do ostatniego elementu
@F[1..$#F,0]to tablica od elementu od 1 do ostatniego elementu plus element 0

Chris Koknat
źródło
1
Uruchomiłem go i na końcu miałem dodatkowy numer, więc użyłem tej wersji: perl -lane 'shift @F; print join "", @F '
Hans Poo
2

Separatorem pól w gawk (przynajmniej) może być zarówno łańcuch, jak i znak (może to być również wyrażenie regularne). Jeśli Twoje dane są spójne, to zadziała:

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

To dwie spacje między podwójnymi cudzysłowami.

Wstrzymano do odwołania.
źródło
Najlepsza odpowiedź na obecną sytuację, ale technicznie nie jest to odpowiedź na pytanie, jak wydrukować wszystko oprócz pierwszego pola.
Dan Molding,
@DanMoulding: Dopóki plik jest konsekwentny w stosowaniu dwóch spacji aby rozdzielić kod kraju i nie ma żadnych innych wystąpień dwóch przestrzeniach razem, moja odpowiedź ma odpowiedzieć na pytanie.
Wstrzymano do odwołania.
2
Osoby, które trafiają na to pytanie, trafiają tutaj, ponieważ chcą wiedzieć, jak wydrukować wszystko oprócz pierwszego pola (patrz tytuł pytania). Tak tu wylądowałem. Twoja odpowiedź pokazuje, jak wydrukować pierwsze pole, a następnie drugie pole. Chociaż jest to prawdopodobnie najlepsze rozwiązanie dla konkretnej sytuacji PO, nie rozwiązuje to ogólnego problemu, jak wydrukować wszystko oprócz pierwszego pola.
Dan Molding
2

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

Arkku
źródło
2

Przenieśmy wszystkie rekordy do następnego i ustawmy ostatni jako pierwszy:

$ 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

Wyjaśnienie

  • a=$1 zapisz pierwszą wartość w zmiennej tymczasowej.
  • for (i=2; i<=NF; i++) $(i-1)=$i zapisz wartość N-tego pola w (N-1)-tym polu.
  • $NF=azapisz pierwszą wartość ( $1) w ostatnim polu.
  • {}1prawdziwy warunek, aby awkwykonać domyślną akcję: {print $0}.

W ten sposób, jeśli masz inny separator pól, wynik jest również dobry:

$ 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 przestań szkodzić'
źródło
1

Pierwsza próba wydaje się działać w twoim konkretnym przypadku.

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

opcja 1

Istnieje rozwiązanie, które działa z niektórymi wersjami awk:

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

Wyjaśnienie:

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

Wynik:

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

Jednak może się to nie powieść w przypadku starszych wersji awk.


Opcja 2

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

To jest:

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

Zwróć uwagę, że to, co należy usunąć, to OFS, a nie FS. Linia jest obliczana ponownie, gdy pole $ 1 jest przypisane. To zmienia wszystkie przebiegi FS na jeden OFS.


Ale nawet ta opcja nadal zawodzi z kilkoma ogranicznikami, co wyraźnie widać po zmianie OFS:

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

Ta linia wyświetli:

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

To pokazuje, że przebiegi FS są zmieniane na jeden OFS.
Jedynym sposobem uniknięcia tego jest uniknięcie ponownego obliczania pola.
Jedną z funkcji, która pozwala uniknąć ponownego obliczania, jest sub.
Pierwsze pole może zostać przechwycone, następnie usunięte z 0 za pomocą sub, a następnie oba ponownie wydrukowane.

Opcja 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

Nawet jeśli zmienimy FS, OFS i / lub dodamy więcej ograniczników, to działa.
Jeśli plik wejściowy zostanie zmieniony na:

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

A polecenie zmienia się na:

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

Wynik będzie (nadal zachowując ograniczniki):

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

Polecenie można rozszerzyć do kilku pól, ale tylko z nowoczesnymi awksami i aktywną opcją --re-interval. To polecenie w oryginalnym pliku:

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

Wyświetli to:

Arab Emirates AE United
& Barbuda AG Antigua
Antilles AN Netherlands
Samoa AS American
and Herzegovina BA Bosnia
Faso BF Burkina
Darussalam BN Brunei
Społeczność
źródło
1

Jeśli jesteś otwarty na inne rozwiązanie Perla:

perl -ple 's/^(\S+)\s+(.*)/$2 $1/' file
Kjetil S.
źródło
0

Jest też opcja sed ...

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

Wyjaśnione...

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

Dokładniej wyjaśnione ...

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
źródło
0

Jeszcze inny sposób ...

... to łączy pola od 2 do NF z FS i wyprowadza jedną linię na linię wejścia

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

Używam tego z gitem, aby zobaczyć, jakie pliki zostały zmodyfikowane w moim katalogu roboczym:

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
źródło
-3

Kolejny i łatwy sposób za pomocą polecenia cat

cat filename | awk '{print $2,$3,$4,$5,$6,$1}' > newfilename
Skorpion
źródło
Głosowałem w dół, ponieważ nie jest to dynamiczne podejście. Dzięki temu musisz znać liczbę argumentów i założyć, że twoje dane są spójne. Dane prawie nigdy nie są spójne, a Twoje podejście musi to uwzględniać przez większość czasu.
xh3b4sd