Awk kullanarak dosyadaki ilk sütunun genişliğini değişken sayıda alanla değiştirme


10

Awk'ın printf işlevini nasıl kullanacağımı anlıyorum, ancak her alanı belirtmek istemiyorum.

Örneğin, bunun benim dosyam olduğunu varsayalım:

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

Her kaydın ilk alanı, ilk alandaki en uzun hücre olan c11 genişliği olacak şekilde biçimlendirmek istiyorum:

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

Anlayabileceğimi anlıyorum:

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

İlk sütunun genişliğinin ne olmasını istediğimi bildiğimi varsayalım, ancak dosyada kaç alan olduğunu bilmiyorum. Temelde şöyle bir şey yapmak istiyorum:

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

... ve diğer alanları orijinal biçimlerinde yazdırın.


Bunu ele almanın başka bir yolu: sed 's/|/'' '' '' |/;s/\(...\) */\1/'(burada SE yorumları bitişik boşlukları bir
araya getirirken

Yanıtlar:


14

Yalnızca sprintfyeniden biçimlendirmek $1için kullanabilirsiniz.

Ör.

$ 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

Kısa, sprintf ile dinamik biçimlendirmeyi de kullanabilirsiniz: Örneğinawk -vf1=3 'BEGIN{OFS=FS="|"}{$1=sprintf("%-*s",f1,$1)}1' test.txt
A.Danischewski

@ A.Danischewski - Şey, dang. Yaklaşık 17 yıldır kapsamlı awk programlama yapıyorum ve daha önce hiç rastlamadım. Tüm güçlükleri düşünmek beni kurtarırdı.
Paul Sinclair

6

İlk alanın en büyük / en uzun uzunluğunu bulmak ve ardından alandaki değerleri bu uzunluğa göre yeniden biçimlendirmek için dosya üzerinde iki ayrı geçiş yapmanız gerekir.

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

(giriş dosyasının komut satırında iki kez belirtildiğine dikkat edin)

Sunduğunuz veriler için bu

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

İlk geçiş, FNR == NRşimdiye kadar görülen en uzun alanı (görülen mmaksimum uzunluğu içerir) izleyen ve bir sonraki satıra atlayan blok tarafından işlenir .

İkinci geçiş, ilk alanı kullanarak yeniden biçimlendiren son blok tarafından gerçekleştirilir sprintf(). Biçim dizesi %-*s"genişliği, gerçek dizeyi tutan bağımsız değişkenten önce tamsayı bağımsız değişkeni tarafından verilen sola dayalı bir dize" anlamına gelir.

Bu, skaler mher bir sütunun maksimum genişliğini tutan bir diziye dönüştürülerek tüm sütunları yapacak şekilde genişletilebilir :

$ 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

1

Akıllı yol, steeldriver'ın önerdiği şeydir . Gereksiz yere kıvrık yol, her alanda yinelemektir:

$ 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

Ama sadece sprintf $1ve onunla yap.


1
Biraz geriye doğru aldınız, küçük özlü ifadeler genellikle daha kıvrımlıdır. Tarlalar üzerinde yineleme daha az kıvrımlıdır.
A.Danischewski

1

Awk'ta dinamik bir printf biçiminde dize oluşturmak için "*" kullanabilirsiniz.

Uzunluğu zaten biliyorsanız, -v ile ilk sütunun alan uzunluğunu iletebilirsiniz.

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

Not: İlk sütun uzunluğunun ne olduğunu bilmiyorsanız, değerleri bir dizide saklayabilirsiniz, daha sonra yol boyunca maksimum col uzunluğunu bulun ve tümünü END bloğunda yazdırın.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.