Sửa đổi độ rộng của cột đầu tiên trong tệp với số lượng trường thay đổi, sử dụng awk


10

Tôi hiểu cách sử dụng chức năng printf của awk, nhưng tôi không muốn chỉ định mọi trường.

Ví dụ: giả sử đây là tệp của tôi:

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

Tôi muốn định dạng nó sao cho trường đầu tiên của mỗi bản ghi là chiều rộng của c11 - ô dài nhất trong trường đầu tiên:

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

Tôi hiểu rằng tôi có thể chỉ định:

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

Giả sử tôi biết tôi muốn chiều rộng của cột đầu tiên là bao nhiêu, nhưng tôi KHÔNG biết có bao nhiêu trường trong tệp. Về cơ bản tôi muốn làm một cái gì đó như:

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

... Và sau đó in phần còn lại của các trường ở định dạng ban đầu của chúng.


Một cách khác để giải quyết nó: sed 's/|/'' '' '' |/;s/\(...\) */\1/'(ở đây thêm dấu ngoặc kép phụ để chèn những 3 không gian như các ý kiến SE bóp không gian liền kề thành một)
Stéphane Chazelas

Câu trả lời:


14

Bạn chỉ có thể sử dụng sprintfđể định dạng $1lại.

Ví dụ.

$ 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

Súc tích, bạn cũng có thể sử dụng định dạng động với sprintf: Egawk -vf1=3 'BEGIN{OFS=FS="|"}{$1=sprintf("%-*s",f1,$1)}1' test.txt
A.Danischewski

@ A.Danischewski - Vâng, dang. Tôi đã làm lập trình awk rộng rãi trong khoảng 17 năm và chưa bao giờ bắt gặp chương trình đó trước đây. Nghĩ đến tất cả những rắc rối nó sẽ cứu tôi.
Paul Sinclair

6

Để tìm ra độ dài lớn nhất / dài nhất của trường đầu tiên và sau đó định dạng lại các giá trị trong trường theo độ dài đó, bạn sẽ phải thực hiện hai lần chuyển riêng biệt qua tệp.

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

(lưu ý rằng tệp đầu vào được chỉ định hai lần trên dòng lệnh)

Đối với dữ liệu mà bạn trình bày, điều này sẽ tạo ra

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

Vượt qua đầu tiên được xử lý bởi FNR == NRkhối, đơn giản là theo dõi trường dài nhất được nhìn thấy cho đến nay ( mchứa độ dài tối đa nhìn thấy) và bỏ qua dòng tiếp theo.

Pass thứ hai được xử lý bởi khối cuối cùng, định dạng lại trường đầu tiên sử dụng sprintf(). Chuỗi định dạng %-*scó nghĩa là "một chuỗi bên trái có chiều rộng được cho bởi đối số nguyên trước đối số giữ chuỗi thực".

Điều này rõ ràng có thể được mở rộng để làm tất cả các cột bằng cách biến vô hướng mthành một mảng giữ chiều rộng tối đa của mỗi cột:

$ 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

Cách thông minh là những gì người thép đề nghị . Cách phức tạp không cần thiết là lặp đi lặp lại trên mọi lĩnh vực:

$ 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

Nhưng chỉ sprintf $1và được thực hiện với nó.


1
Bạn đã có một chút ngược, các tuyên bố ngắn gọn thường nói chung là phức tạp hơn. Lặp đi lặp lại trên các lĩnh vực là ít phức tạp hơn.
A.Danischewski

1

Trong Awk, bạn có thể sử dụng "*" để tạo chuỗi định dạng printf động.

Nếu bạn biết độ dài đã có, bạn có thể vượt qua độ dài trường cho cột đầu tiên với -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

Lưu ý: nếu bạn không biết chiều dài cột đầu tiên là gì, bạn có thể lưu trữ các giá trị trong một mảng sau đó tìm độ dài col tối đa trên đường đi và in tất cả ra trong khối END.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.