Ubah lebar kolom pertama dalam file dengan jumlah variabel bidang, menggunakan awk

10

Saya mengerti cara menggunakan fungsi printf awk, tetapi saya tidak ingin menentukan setiap bidang.

Misalnya, anggap ini file saya:

c1|c2|c3|c4|c5
c6|c7|c8|c9|c10
c11|c12|c13|c14|c15

Saya ingin memformatnya sehingga bidang pertama setiap rekaman adalah lebar c11 - sel terpanjang di bidang pertama:

c1 |c2|c3|c4|c5
c6 |c7|c8|c9|c10
c11|c12|c13|c14|c15

Saya mengerti bahwa saya dapat menentukan:

awk -F"|" '{printf "%-3s%s%s%s%s\n", $1, $2, $3, $4, $5}' file > newfile

Anggap saja saya tahu lebar kolom pertama yang saya inginkan, tetapi saya TIDAK tahu berapa banyak bidang dalam file. Pada dasarnya saya ingin melakukan sesuatu seperti:

... '{printf "%-3s|", $1}'

... dan kemudian cetak bidang lainnya dalam format aslinya.

Kayli O'Keefe
sumber
Cara lain untuk mengatasinya: sed 's/|/'' '' '' |/;s/\(...\) */\1/'(di sini menambahkan tanda kutip tambahan untuk menyisipkan 3 ruang tersebut ketika komentar SE memeras ruang yang berdekatan menjadi satu)
Stéphane Chazelas

Jawaban:

14

Anda dapat menggunakan sprintfuntuk memformat ulang $1saja.

Ex.

$ awk 'BEGIN{OFS=FS="|"} {$1 = sprintf("%-3s",$1)} 1' file
c1 |c2|c3|c4|c5
c6 |c7|c8|c9|c10
c11|c12|c13|c14|c15
Steeldriver
sumber
Ringkas, Anda dapat menggunakan pemformatan dinamis dengan sprintf juga: Misalnyaawk -vf1=3 'BEGIN{OFS=FS="|"}{$1=sprintf("%-*s",f1,$1)}1' test.txt
A.Danischewski
@ A.Danischewski - Baiklah, sial. Saya telah melakukan pemrograman awk yang ekstensif selama ~ 17 tahun, dan belum pernah menemukan yang sebelumnya. Memikirkan semua kerepotan itu akan menyelamatkanku.
Paul Sinclair
6

Untuk mengetahui panjang terbesar / terpanjang dari bidang pertama, dan kemudian memformat ulang nilai di bidang sesuai dengan panjang itu, Anda harus melakukan dua lintasan terpisah atas file.

awk 'BEGIN     { OFS = FS = "|" }
     FNR == NR { if (m < (n=length($1))) m = n; next }
               { $1 = sprintf("%-*s", m, $1); print }' file file

(perhatikan bahwa file input ditentukan dua kali pada baris perintah)

Untuk data yang Anda sajikan, ini akan menghasilkan

c1 |c2|c3|c4|c5
c6 |c7|c8|c9|c10
c11|c12|c13|c14|c15

Lulus pertama ditangani oleh FNR == NRblok, yang hanya melacak bidang terpanjang yang terlihat sejauh ini ( mberisi panjang maksimum yang terlihat), dan melompat ke baris berikutnya.

Lulus kedua ditangani oleh blok terakhir, yang memformat ulang bidang pertama yang digunakan sprintf(). String format %-*sberarti "string yang dibenarkan kiri yang lebarnya diberikan oleh argumen integer sebelum argumen yang menyimpan string sebenarnya".

Ini jelas dapat diperluas untuk melakukan semua kolom dengan mengubah skalar mmenjadi array yang memiliki lebar maksimum setiap kolom:

$ awk 'BEGIN     { OFS = FS = "|" }
       FNR == NR { for (i=1; i<=NF; ++i) if (m[i] < (n=length($i))) m[i] = n; next }
                 { for (i=1; i<=NF; ++i) $i = sprintf("%-*s", m[i], $i); print }' file file
c1 |c2 |c3 |c4 |c5
c6 |c7 |c8 |c9 |c10
c11|c12|c13|c14|c15
Kusalananda
sumber
1

Cara cerdas adalah apa yang disarankan steeldriver . Cara berbelit-belit yang tidak perlu adalah beralih ke setiap bidang:

$ awk -F'|' '{printf "%-3s|",$1; for(i=2;i<NF;i++){printf "%s|",$i} printf "%s\n", $i}' file
c1 |c2|c3|c4|c5
c6 |c7|c8|c9|c10
c11|c12|c13|c14|c15

Tapi adil sprintf $1dan selesai dengan itu.

terdon
sumber
1
Anda mendapatkannya agak mundur, pernyataan ringkas kecil umumnya lebih berbelit-belit. Iterasi pada bidang tidak terlalu berbelit-belit.
A.Danischewski
1

Di Awk Anda dapat menggunakan "*" untuk menghasilkan string format printf dinamis.

Jika Anda sudah tahu panjangnya, Anda bisa melewati panjang bidang untuk kolom pertama dengan -v.

awk -vcol1=3 'BEGIN{FS="|"}{for(i=1;i<=NF;i++){if(i==1)printf "%*-s%s",col1,$i,FS;else if(i!=NF)printf "%s%s",$i,FS;else printf "%s\n",$i;};}' test.txt

Catatan: jika Anda tidak tahu berapa panjang kolom pertama Anda, Anda bisa menyimpan nilai-nilai dalam array kemudian menemukan panjang max col sepanjang jalan dan mencetak semuanya di blok END.

A.Danischewski
sumber