Mengatur ulang kolom menggunakan awk


12

Saya mencoba untuk memindahkan kolom ke 7 dari file csv saya ke ujung dengan menggunakan

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

di mana $ file adalah file .csv dalam direktori. Namun, hasilnya adalah

awk:                          ^ syntax error

Adakah yang tahu cara memperbaiki kesalahan ini?


7
Saat menunjukkan kesalahan awk, Anda harus menunjukkan semuanya. Itu ^menunjukkan bagian spesifik dari perintah di mana kesalahan itu terjadi.
terdon

Jawaban:


10

The -Fpilihan membutuhkan sebuah argumen: -F,misalnya.

Akhir awkskrip harus dipisahkan dengan (spasi) dengan parameter lainnya.

Jika pemisah bidang adalah ,dan Anda ingin menyimpannya, dan jika jumlah kolom konstan dan lebih rendah dari atau sama dengan 11, coba ini:

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

8
@ anuribs sangat sedikit program yang mengizinkannya. Cara standarnya adalah command file > newfile && mv newfile file. Yang mengatakan, versi terbaru dari GNU awkuntuk mendukung ini: gawk -i inplace '{blah blah}' file.
terdon

1
sebagai alternatif, alih-alih mv newfile fileAnda dapat menggunakan cat newfile > file ; rm -f newfile- ini mempertahankan inode dan izin dari file.
Kasus

dan itu umumnya ide yang baik untuk menggunakan mktempalih-alih nama file sementara hard-coding ke dalam skrip. misalnyatf=$(mktemp) ; command file > "$tf" ; cat "$tf" > file ; rm -f "$tf"
cas

8

Solusi yang lebih pendek adalah

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

Saya tidak yakin apakah ,+akan berfungsi di semua awkversi, tetapi berfungsi setidaknya di GNU awk, juga dengan -cmode ompatibilitas.

Penjelasan:

  • $(NF+1)=$7: pertama kita tambahkan bidang ke-7 ke akhir baris (bisa $12=$7dalam hal ini)
  • $7="": pada langkah berikutnya bidang ke-7 dihapus (tetapi pembatas di sekitarnya tetap)
  • untuk menghapus pembatas, kita perlu mengatur ulang seluruh catatan (via $0=$0) memperlakukan beberapa koma sebagai pemisah lapangan (ini dilakukan melalui -F',+', di sini +berarti satu atau lebih kali), dan juga mengatur ulang catatan saat ini $1=$1untuk memaksa membangun kembali garis menggunakan kekuatan yang ditetapkan sebelumnya bidang output pemisah (diatur oleh opsi -v OFS=,)
  • setelah semua pengocokan selesai kami siap untuk mencetak hasilnya 1

Input contoh:

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

keluaran

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

Bagaimana jika kolom lain kosong? Tapi, ya, FS adalah ekspresi reguler dalam POSIX (jika lebih dari satu karakter), maka ,+seharusnya berfungsi.
Acak 832

(1) Saya mengerti bahwa membuat kolom input data ketujuh “menghilang”, dan tidak hanya mengaturnya menjadi nol, adalah bagian rumit dari masalah ini. Tetapi, seperti yang dikatakan Random832, solusi Anda akan mengosongkan kolom kosong (misalnya, all,ball,call,,,fallall,ball,call,fall). (2)  $(NF+1)=$7adalah pendekatan yang cerdas. IMHO, $0 = $0 OFS $7sedikit lebih jelas, hanya beberapa karakter lebih lama, dan sepertinya melakukan hal yang sama. Bisakah Anda memikirkan situasi di mana $0 = $0 OFS $7tidak melakukan hal yang sama dengan kode Anda?
G-Man Mengatakan 'Reinstate Monica'

@ Random832 @ G-Man ya, beberapa kasus tepi seperti bidang kosong, garis kosong atau NF <7 harus diperlakukan secara terpisah atau seseorang harus mengatur ulang kode. Ini hanya sebuah ide, bukan "solusi lengkap" untuk semua kasus umum, yang harus jelas. $0=$0 OFS $7mungkin identik dengan $(NF+1)=$7, tetapi hanya dengan sisa kode tidak berubah, tidak secara umum.
jimmij

5

Jika Anda mencetak dengan OFS=, jadi tanpa pemisah di antara bidang, Anda cukup menyimpan nilai $7dalam suatu variabel, mengatur $7untuk mengosongkan dan mencetak garis dan variabel secara langsung. Anda tidak perlu menentukan semua bidang:

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


3

Anda tidak secara khusus mengatakan Anda ingin menggunakan awk, dan Anda tidak mengatakan Anda ingin menggunakan editing di-tempat seperti yang disediakan oleh sed -i, jadi di sini adalah sed -ivarian. Biasanya awklebih baik untuk bekerja dengan kolom, tetapi ini adalah satu kasus di mana saya lebih suka sed, karena secara alami menangani jumlah kolom yang berubah-ubah.

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

Penjelasan:

  • -r memilih regexps yang diperluas sehingga kami menghindari banyak garis miring terbalik
  • grup pertama adalah $ N pengulangan string yang diakhiri dengan koma, dengan kata lain kolom sebelum yang ingin kita pindahkan, dengan koma terakhir
  • grup kedua adalah $ N-th repeat, kita lupakan saja
  • grup ketiga adalah kolom yang ingin kita pindahkan, tanpa koma akhir
  • grup keempat terdiri dari semua kolom setelah yang ingin kita pindahkan, tanpa koma sebelumnya
  • kami ganti dengan grup pertama, grup terakhir, dan kolom yang kami ekstrak, memasukkan koma sesuai kebutuhan.

Tentu saja ini tidak akan berfungsi dengan file yang menyembunyikan tanda koma dalam tanda kutip (atau lebih buruk, luput dari mereka), tetapi awk tidak akan menanganinya tanpa akrobat yang serius. Jika Anda memiliki masalah itu, Anda akan lebih baik dengan perlmodul Text:CSVatau pythonmodul csv.


2

Beberapa awkvarian (dengan asumsi file Anda ada di dalam variabel $file)

  • Di sini Anda dapat menggilir semua kolom, mencetak dengan pemisah bidang (OFS), dan mencetak record terminator (ORS) di akhir baris.

    awk  -F',' -v OFS=,                                \
    '{for(i=1;i<=NF;i++) if (i!=7) printf "%s",$i OFS; \
    printf "%s",$7;printf ORS}' "$file"
  • Di sini dengan menggunakan regex dan gensub()fungsinya

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

    membunuh bidang ke- 7 dan mencetaknya di akhir baris.

    • $0 adalah seluruh catatan
    • $nadalah catatan ke- n
    • NF adalah Jumlah Bidang dari baris saat ini
    • OFS pemisah yang diajukan keluaran
    • ORS terminator catatan keluaran
    • 1adalah trik untuk mengatakan awk truedan mencetak default ( $0).

Perbarui ...

Aku hampir lupa, itu mungkin untuk menggeser semua kolom berikut 7 th satu.

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

(1) Bisa dibilang, OFS $7akan lebih kuat dari "," $7. (2) Saya percaya itu ", " $7salah, sejauh pertanyaan menunjukkan bahwa OP tidak ingin spasi setelah koma. (Dan, jika data input memiliki spasi setelah koma, maka $7sudah akan dimulai dengan spasi, dan Anda akan menambahkan satu spasi.)
G-Man Mengatakan 'Reinstate Monica'

@ G-Man Itu terutama untuk mengusulkan beberapa ide, beberapa varian. Terima kasih, untuk tempat ini, saya setuju OFS $7, tidak hanya lebih kuat, tetapi bahkan lebih umum ( "tergesa-gesa membuat sampah" )
Hastur
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.