Mencetak semuanya kecuali bidang pertama dengan awk

108

Saya memiliki file yang terlihat seperti ini:

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

Dan saya ingin membalik pesanan, pertama-tama mencetak semuanya kecuali $ 1 lalu $ 1:

United Arab Emirates AE

Bagaimana saya bisa melakukan trik "semuanya kecuali bidang 1"?

cfischer
sumber
2
Hai @cfisher, ini dapat dilakukan tanpa loop ang tanpa spasi ekstra.
Juan Diego Godoy Robles

Jawaban:

91

Penetapan $1berfungsi tetapi akan meninggalkan ruang utama:awk '{first = $1; $1 = ""; print $0, first; }'

Anda juga dapat menemukan jumlah kolom dalam NFdan menggunakannya dalam satu lingkaran.

Ben Jackson
sumber
2
Untuk yang benar-benar malas; di sini adalah kode klashxx .
Serge Stroobandt
1
Bagus. Singkirkan ruang terdepan dengan sed: awk {'first = $1; $1=""; print $0'}|sed 's/^ //g'
Thyag
Ruang dengan mudah dihilangkan dengan VIM menekan 'Ctrl + V Gd' dalam mode normal
Santi
107

$1=""sisakan ruang seperti yang disebutkan Ben Jackson, jadi gunakan forloop:

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

Jadi jika string Anda adalah "satu dua tiga", hasilnya akan menjadi:

dua
tiga

Jika Anda menginginkan hasil dalam satu baris, Anda dapat melakukan hal berikut:

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

Ini akan memberi Anda: "dua tiga"

7winkie
sumber
4
dan ruang tambahan ekstra
NeronLeVelu
2
lebih baik menggunakan: awk '{for(i=2;i<=NF;i++){ printf("%s",( (i>2) ? OFS : "" ) $i) } ; print ;}' which: print fields 2 to NF, tambahkan Output Field Separator seperlunya (yaitu, kecuali sebelum $ 2). Cetakan terakhir menambahkan baris baru terakhir untuk mengakhiri pencetakan baris saat ini. Yang itu akan berfungsi jika Anda mengubah FS / OFS (yaitu, tidak selalu menjadi "ruang")
Olivier Dulac
Yang kedua bekerja sangat baik untuk saya. Yang pertama, tidak terlalu. Tidak begitu yakin mengapa. Itu memotong seluruh teks.
suara
72

Gunakan cutperintah dengan --complementopsi:

$ 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
sumber
2
Meskipun tidak menjawab pertanyaan khusus untuk awk, menurut saya ini paling berguna karena awk menghapus spasi duplikat, dan cut tidak.
Fmstrat
19
echo a b c | cut -d' ' -f 2- adalah alternatif
Luis
2
Bagus - Solusi @Luis berfungsi di Mac, yang tidak mendukung --complement
metadaddy
21

Mungkin cara paling ringkas:

$ 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

Penjelasan:

$(NF+1)=$1: Generator kolom terakhir "baru".

$1="": Setel bidang pertama yang asli ke nol

sub(FS,""): Setelah dua tindakan pertama {$(NF+1)=$1;$1=""}singkirkan pemisah bidang pertama dengan menggunakan sub. Hasil cetak terakhir tersirat.

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

Hapus bidang pertama dan pemisah, dan cetak hasilnya ( 7adalah nilai bukan nol jadi mencetak $ 0).

NeronLeVelu
sumber
Jawaban Terbaik! Suara positif. Apa bedanya dengan hanya menggunakan 1? Saya ingin tahu penggunaan pola ini dan ingin memahaminya. Terima kasih!
Abhijeet Rastogi
10
awk '{ saved = $1; $1 = ""; print substr($0, 2), saved }'

Menyetel bidang pertama untuk ""menyisakan satu salinan OFSdi awal $0. Dengan asumsi itu OFShanya satu karakter (secara default, itu adalah satu spasi), kita dapat menghapusnya dengan substr($0, 2). Kemudian kami menambahkan salinan yang disimpan dari $1.

meragukanjim
sumber
6

Jika Anda terbuka untuk solusi Perl ...

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

adalah solusi sederhana dengan pemisah input / output satu spasi, yang menghasilkan:

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

Yang berikut ini sedikit lebih kompleks

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

dan mengasumsikan bahwa pemisah input / output adalah dua spasi:

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

Opsi baris perintah ini digunakan:

  • -n memutar di sekitar setiap baris file input, jangan mencetak setiap baris secara otomatis

  • -l menghapus baris baru sebelum diproses, dan menambahkannya kembali setelahnya

  • -amode autosplit - memisahkan baris input ke dalam larik @F. Secara default, pemisahan di spasi kosong

  • -F pengubah pemisahan otomatis, dalam contoh ini dibagi menjadi '' (dua spasi)

  • -e jalankan kode perl berikut

@Fadalah larik kata di setiap baris, diindeks mulai dengan 0
$#Fadalah jumlah kata dalam @F
@F[1..$#F]potongan larik elemen 1 sampai elemen terakhir
@F[1..$#F,0]adalah irisan larik elemen 1 sampai elemen terakhir ditambah elemen 0

Chris Koknat
sumber
1
Saya menjalankannya dan memiliki nomor tambahan di akhir jadi saya telah menggunakan versi ini: perl -lane 'shift @F; cetak gabung "", @F '
Hans Poo
2

Pemisah bidang di gawk (setidaknya) bisa berupa string serta karakter (bisa juga berupa regex). Jika data Anda konsisten, ini akan berfungsi:

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

Itu dua spasi di antara tanda kutip ganda.

Dijeda sampai pemberitahuan lebih lanjut.
sumber
Jawaban terbaik untuk situasi yang dihadapi, tetapi, secara teknis, ini tidak menjawab pertanyaan tentang bagaimana mencetak semuanya kecuali bidang pertama.
Dan Moulding
@DanMoulding: Selama file konsisten dalam penggunaan dua spasi untuk memisahkan kode negara dan tidak ada kejadian lain dari dua ruang bersama-sama, jawaban saya tidak alamat pertanyaan.
Dijeda sampai pemberitahuan lebih lanjut.
2
Orang-orang yang mendapatkan pertanyaan ini sampai di sini karena mereka ingin tahu cara mencetak semuanya kecuali kolom pertama (lihat judul pertanyaan). Begitulah cara saya mendarat di sini. Jawaban Anda menunjukkan cara mencetak kolom pertama diikuti dengan kolom kedua. Meskipun ini mungkin solusi terbaik untuk situasi khusus OP, ini tidak menyelesaikan masalah umum tentang cara mencetak semuanya kecuali bidang pertama.
Dan Moulding
2

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

Arkku
sumber
2

Mari pindahkan semua record ke record berikutnya dan atur yang terakhir sebagai yang pertama:

$ 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

Penjelasan

  • a=$1 simpan nilai pertama ke dalam variabel sementara.
  • for (i=2; i<=NF; i++) $(i-1)=$i simpan nilai bidang ke-N ke dalam bidang (N-1).
  • $NF=asimpan nilai pertama ( $1) ke dalam bidang terakhir.
  • {}1Kondisi benar untuk make awkmelakukan tindakan default: {print $0}.

Dengan cara ini, jika Anda kebetulan memiliki pemisah bidang lain, hasilnya juga bagus:

$ 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 'JADI berhenti merugikan'
sumber
1

Tusukan pertama tampaknya berhasil untuk kasus khusus Anda.

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

Pilihan 1

Ada solusi yang berfungsi dengan beberapa versi awk:

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

Penjelasan:

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

Hasil:

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

Namun itu mungkin gagal dengan versi awk yang lebih lama.


pilihan 2

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

Itu adalah:

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

Perhatikan bahwa yang perlu dihapus adalah OFS, bukan FS. Garis akan dihitung ulang saat bidang $ 1 ditentukan. Itu mengubah semua proses FS menjadi salah satu OFS.


Tetapi bahkan opsi itu masih gagal dengan beberapa pembatas, seperti yang ditunjukkan secara jelas dengan mengubah OFS:

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

Baris itu akan menampilkan:

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

Itu menunjukkan bahwa run dari FS sedang diubah menjadi salah satu OFS.
Satu-satunya cara untuk menghindarinya adalah dengan menghindari penghitungan ulang bidang.
Salah satu fungsi yang dapat menghindari penghitungan ulang adalah sub.
Bidang pertama dapat diambil gambarnya, lalu dihapus dari $ 0 dengan sub, dan kemudian keduanya dicetak ulang.

Pilihan 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

Bahkan jika kita mengubah FS, OFS dan / atau menambahkan lebih banyak pembatas, itu berhasil.
Jika file input diubah menjadi:

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

Dan perintah berubah menjadi:

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

Outputnya adalah (masih mempertahankan pembatas):

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

Perintah dapat diperluas ke beberapa bidang, tetapi hanya dengan awks modern dan dengan opsi --re-interval active. Perintah ini pada file asli:

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

Akan menampilkan ini:

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

Jika Anda terbuka untuk solusi Perl lain:

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

Ada pilihan sed juga ...

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

Dijelaskan ...

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

Lebih jelasnya dijelaskan ...

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
sumber
0

Namun cara lain ...

... ini menggabungkan kembali bidang 2 melalui NF dengan FS dan mengeluarkan satu baris per baris masukan

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

Saya menggunakan ini dengan git untuk melihat file apa yang telah dimodifikasi di direktori kerja saya:

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
sumber
-3

Cara lain dan mudah menggunakan perintah cat

cat filename | awk '{print $2,$3,$4,$5,$6,$1}' > newfilename
Scorpio
sumber
Saya tidak memilih karena ini bukan pendekatan dinamis. Dengan ini, Anda perlu mengetahui jumlah argumen dan menganggap data Anda konsisten. Data hampir tidak pernah konsisten dan pendekatan Anda harus memperhitungkan hal ini hampir sepanjang waktu.
xh3b4sd