ปรับเปลี่ยนความกว้างของคอลัมน์แรกในไฟล์ด้วยจำนวนตัวแปรที่หลากหลายโดยใช้ awk


10

ฉันเข้าใจวิธีใช้ฟังก์ชั่น printf ของ awk แต่ฉันไม่ต้องการระบุทุกฟิลด์

ตัวอย่างเช่นสมมติว่านี่เป็นไฟล์ของฉัน:

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

ฉันต้องการจัดรูปแบบเพื่อให้ทุกเขตข้อมูลแรกของระเบียนคือความกว้างของ c11 - เซลล์ที่ยาวที่สุดในเขตข้อมูลแรก:

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

ฉันเข้าใจว่าฉันสามารถระบุ:

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

สมมติว่าฉันรู้ว่าฉันต้องการให้ความกว้างของคอลัมน์แรกเป็นอย่างไร แต่ฉันไม่ทราบว่ามีกี่ฟิลด์ในไฟล์ โดยทั่วไปฉันต้องการทำสิ่งที่ชอบ:

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

... จากนั้นพิมพ์ส่วนที่เหลือของฟิลด์ในรูปแบบดั้งเดิม


อีกวิธีหนึ่งในการจัดการกับมัน: sed 's/|/'' '' '' |/;s/\(...\) */\1/'(ที่นี่เพิ่มราคาพิเศษเพื่อแทรก 3 ช่องว่างในขณะที่ความคิดเห็น SE บีบช่องว่างที่ต่อเนื่องกันเป็นหนึ่ง)
Stéphane Chazelas

คำตอบ:


14

คุณสามารถใช้sprintfเพื่อฟอร์แมตใหม่$1เท่านั้น

อดีต

$ 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

กระชับคุณสามารถใช้การจัดรูปแบบไดนามิกที่มี sprintf เกินไป: เช่นawk -vf1=3 'BEGIN{OFS=FS="|"}{$1=sprintf("%-*s",f1,$1)}1' test.txt
A.Danischewski

@ A.Danischewski - ดีแล้ว ฉันได้ทำการเขียนโปรแกรม awk อย่างกว้างขวางมาเป็นเวลาประมาณ 17 ปีและไม่เคยเจอโปรแกรมนี้มาก่อน เมื่อนึกถึงเรื่องยุ่งยากทั้งหมดมันคงช่วยฉันได้
Paul Sinclair

6

หากต้องการค้นหาความยาวที่ใหญ่ที่สุด / ยาวที่สุดของฟิลด์แรกจากนั้นทำการฟอร์แมตค่าในฟิลด์ตามความยาวนั้นคุณจะต้องทำการส่งผ่านสองไฟล์แยกกัน

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

(โปรดทราบว่าไฟล์อินพุตถูกระบุสองครั้งบนบรรทัดคำสั่ง)

สำหรับข้อมูลที่คุณนำเสนอสิ่งนี้จะสร้างขึ้น

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

FNR == NRบล็อกแรกจะถูกจัดการโดยบล็อกซึ่งจะติดตามสนามที่ยาวที่สุดเท่าที่เห็น ( mประกอบด้วยความยาวสูงสุดที่เห็น) และข้ามไปที่บรรทัดถัดไป

sprintf()ผ่านสองจะถูกจัดการโดยบล็อกที่ผ่านมาซึ่งฟอร์แมตสนามครั้งแรกที่ใช้ สตริงรูปแบบ%-*sหมายถึง "สตริงที่จัดชิดซ้ายซึ่งความกว้างถูกกำหนดโดยอาร์กิวเมนต์จำนวนเต็มก่อนอาร์กิวเมนต์ที่เก็บสตริงที่แท้จริง"

สิ่งนี้สามารถขยายได้อย่างชัดเจนในการทำคอลัมน์ทั้งหมดโดยการเปลี่ยนสเกลาร์mเป็นอาร์เรย์ที่มีความกว้างสูงสุดของแต่ละคอลัมน์:

$ 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

วิธีที่ฉลาดเป็นสิ่งที่ชี้ให้เห็น steeldriver วิธีที่ซับซ้อนโดยไม่จำเป็นคือการวนซ้ำทุก ๆ ฟิลด์:

$ 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

แต่เพียงsprintf $1และจะทำกับมัน


1
คุณได้รับมันไปข้างหลังนิดหน่อยโดยทั่วไปแล้วข้อความสั้น ๆ ที่ซับซ้อนจะมีความซับซ้อนมากขึ้น การวนซ้ำในทุ่งนามีความซับซ้อนน้อยกว่า
A.Danischewski

1

ใน Awk คุณสามารถใช้ "*" เพื่อสร้างสตริงรูปแบบ printf แบบไดนามิก

หากคุณทราบความยาวแล้วคุณสามารถส่งผ่านความยาวของฟิลด์สำหรับคอลัมน์แรกด้วย -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

หมายเหตุ: หากคุณไม่ทราบว่าความยาวของคอลัมน์แรกคืออะไรคุณสามารถเก็บค่าไว้ในอาร์เรย์ได้จากนั้นค้นหาความยาวสูงสุดของคอลัมน์ระหว่างทางแล้วพิมพ์ออกทั้งหมดในบล็อก END

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.