Sắp xếp lại các cột bằng awk


12

Tôi đang cố gắng di chuyển cột thứ 7 của tệp csv của mình đến cuối bằng cách sử dụng

awk -F '{print $1,$2,$3,$4,$5,$6,$8,$9,$10,$11,$7}',OFS= "$file"

trong đó tệp $ là tệp .csv trong thư mục. Tuy nhiên, đầu ra là

awk:                          ^ syntax error

Có ai biết làm thế nào để sửa lỗi này?


7
Khi hiển thị lỗi awk, bạn cần hiển thị toàn bộ. Dấu ^hiệu cho biết phần cụ thể của lệnh gặp lỗi.
terdon

Câu trả lời:


10

Các -Ftùy chọn cần một cuộc tranh cãi: -F,ví dụ.

Phần cuối của awktập lệnh phải được phân tách bằng dấu cách (dấu cách) với phần còn lại của các tham số.

Nếu dấu tách trường là ,và bạn muốn giữ nó, và nếu số cột không đổi và thấp hơn hoặc bằng 11, hãy thử điều này:

awk -F, '{print $1,$2,$3,$4,$5,$6,$8,$9,$10,$11,$7}' OFS=, "$file"

8
@anuribs rất ít chương trình cho phép điều đó. Cách tiêu chuẩn là command file > newfile && mv newfile file. Điều đó nói rằng, phiên bản mới hơn của GNU awkđể hỗ trợ này : gawk -i inplace '{blah blah}' file.
terdon

1
cách khác, thay vì mv newfile filebạn có thể sử dụng cat newfile > file ; rm -f newfile- điều này bảo tồn inode và quyền của file.
cas

và nói chung đó là một ý tưởng tốt để sử dụng mktempthay vì tên tệp tạm thời được mã hóa cứng thành tập lệnh. ví dụtf=$(mktemp) ; command file > "$tf" ; cat "$tf" > file ; rm -f "$tf"
cas

7

Giải pháp ngắn hơn sẽ là

awk -F',+' -v OFS=, '{$(NF+1)=$7; $7=""; $0=$0; $1=$1}1' file

Tôi không chắc liệu ,+sẽ hoạt động trong tất cả các awkphiên bản hay không, nhưng hoạt động ít nhất là trong GNU awk, cũng với -cchế độ tương thích.

Giải trình:

  • $(NF+1)=$7: đầu tiên chúng ta thêm trường thứ 7 vào cuối dòng (có thể $12=$7trong trường hợp này)
  • $7="": trong bước tiếp theo, trường thứ 7 bị xóa (nhưng các dấu phân cách xung quanh vẫn ở lại)
  • để xóa dấu phân cách, chúng ta cần thiết lập lại toàn bộ bản ghi (thông qua $0=$0) coi nhiều dấu phẩy là dấu tách trường (điều này được thực hiện thông qua -F',+', ở đây +có nghĩa là một hoặc nhiều lần) và cũng sắp xếp lại bản ghi hiện tại thông qua $1=$1để buộc xây dựng lại dòng bằng cách sử dụng trường đầu ra được thiết lập trước đó dấu phân cách (được đặt bởi một tùy chọn -v OFS=,)
  • sau khi hoàn thành việc xáo trộn, chúng tôi sẵn sàng in kết quả với 1

Ví dụ đầu vào:

1,2,3,4,5,6,7,8,9,10,11

đầu ra

1,2,3,4,5,6,8,9,10,11,7

Nếu các cột khác trống thì sao? Nhưng, vâng, FS là một biểu thức chính quy trong POSIX (nếu nó có nhiều ký tự), vì vậy ,+sẽ hoạt động.
Random832

(1) Tôi hiểu rằng làm cho cột thứ bảy của dữ liệu đầu vào biến mất, và không chỉ đặt nó thành null, là một phần khó khăn của vấn đề này. Nhưng, như Random832 nói, giải pháp của bạn chặn các cột trống (ví dụ: all,ball,call,,,fallall,ball,call,fall). (2)  $(NF+1)=$7là một cách tiếp cận thông minh. IMHO, $0 = $0 OFS $7rõ ràng hơn một chút, chỉ có một vài nhân vật dài hơn và dường như nó cũng làm điều tương tự. Bạn có thể nghĩ về một tình huống trong đó $0 = $0 OFS $7không làm giống như mã của bạn không?
G-Man nói 'Phục hồi Monica'

@ Random832 @ G-Man có, một số trường hợp cạnh như trường trống, dòng trống hoặc NF <7 nên được xử lý riêng hoặc người ta nên sắp xếp lại mã. Đây chỉ là một ý tưởng, không phải là "giải pháp hoàn chỉnh" cho tất cả các trường hợp chung, cần phải rõ ràng. $0=$0 OFS $7có lẽ giống hệt $(NF+1)=$7, nhưng chỉ với phần còn lại của mã không thay đổi, không nói chung.
jimmij

5

Nếu bạn đang in OFS=, do đó, không có dấu phân cách giữa các trường, bạn chỉ cần lưu giá trị của $7một biến, đặt $7thành trống và in trực tiếp dòng và biến. Bạn không cần chỉ định tất cả các trường:

$ cat file
1,2,3,4,5,6,7,8
$ awk -F, -vOFS= '{k=$7; $7=""; print $0,k}' file 
12345687


3

Bạn không nói cụ thể là bạn muốn sử dụng awk và bạn đã nói rằng bạn muốn sử dụng chỉnh sửa tại chỗ như được cung cấp bởi sed -i, vì vậy đây là một sed -ibiến thể. Thường awklà tốt hơn để làm việc với các cột, nhưng đây là một trường hợp mà tôi thích sed, bởi vì nó tự nhiên xử lý số lượng cột tùy ý.

MOVECOL=7
N=$((MOVECOL-1))
sed -r -e "s/^(([^,]*,){$N})([^,]*),(.*)/\1\4,\3/" -i test.csv

Giải trình:

  • -r chọn regexps mở rộng để chúng tôi tránh nhiều dấu gạch chéo ngược
  • nhóm đầu tiên là sự lặp lại $ N của các chuỗi kết thúc bằng dấu phẩy, nói cách khác là các cột trước chuỗi chúng ta muốn di chuyển, với dấu phẩy cuối cùng
  • nhóm thứ hai là lặp lại $ N-th, chúng ta quên nó
  • nhóm thứ ba là cột chúng tôi muốn di chuyển, không có dấu phẩy cuối cùng
  • nhóm thứ tư bao gồm tất cả các cột sau nhóm chúng tôi muốn di chuyển, không có dấu phẩy trước
  • chúng tôi thay thế bằng nhóm đầu tiên, nhóm cuối cùng và cột chúng tôi trích xuất, chèn dấu phẩy khi cần.

Tất nhiên điều này sẽ không hoạt động với các tệp ẩn dấu phẩy trong dấu ngoặc kép (hoặc tệ hơn là thoát chúng), nhưng awk sẽ không xử lý việc đó nếu không có một số màn nhào lộn nghiêm trọng. Nếu bạn gặp vấn đề đó, bạn nên sử dụng perlmô-đun Text:CSVhoặc pythonmô-đun csv.


2

Một vài awkbiến thể (giả sử tệp của bạn nằm trong biến $file)

  • Tại đây, bạn có thể quay vòng cho tất cả các cột màu, in bằng dấu tách trường (OFS) và in dấu kết thúc bản ghi (ORS) ở cuối dòng.

    awk  -F',' -v OFS=,                                \
    '{for(i=1;i<=NF;i++) if (i!=7) printf "%s",$i OFS; \
    printf "%s",$7;printf ORS}' "$file"
  • Ở đây với việc sử dụng regex và gensub()hàm

    gawk -F',+' -v OFS=, '{$0=gensub(/\s*\S+/,"",7) OFS $7}1' "$file"

    giết trường thứ 7 và in nó ở cuối dòng.

    • $0 là toàn bộ hồ sơ
    • $nlà kỷ lục thứ n
    • NF là số lượng các lĩnh vực của dòng hiện tại
    • OFS đầu ra nộp dấu phân cách
    • ORS bộ kết thúc bản ghi đầu ra
    • 1là mẹo để nói với awk truevà in mặc định ( $0).

Cập nhật ...

Tôi gần như quên mất, có thể thay đổi tất cả các cột theo sau cột thứ 7 .

awk  -F',' -v OFS=, '{tmp=$7; for(i=7;i<=NF;i++) $i=$(i+1); $NF=tmp}1 ' "$file"

(1) Có thể cho rằng, OFS $7sẽ mạnh mẽ hơn "," $7. (2) Tôi tin rằng điều đó ", " $7là sai, trong chừng mực vì câu hỏi chỉ ra rằng OP không muốn có khoảng trắng sau dấu phẩy. (Và, nếu dữ liệu đầu vào có khoảng trắng sau dấu phẩy, thì $7nó sẽ bắt đầu bằng một khoảng trắng và bạn sẽ thêm một khoảng trống.)
G-Man nói 'Phục hồi' '

@ G-Man Chủ yếu đề xuất một số ý tưởng, một số biến thể. Cảm ơn, vì đã đồng ý, tôi OFS $7không chỉ mạnh mẽ hơn, mà thậm chí còn chung chung hơn ( "sự vội vàng làm lãng phí" )
Hastur
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.