Simpan modifikasi di tempat dengan awk

135

Saya sedang belajar awkdan saya ingin tahu apakah ada opsi untuk menulis perubahan ke file, mirip dengan di sedmana saya akan menggunakan -iopsi untuk menyimpan modifikasi ke file.

Saya mengerti bahwa saya bisa menggunakan pengalihan untuk menulis perubahan. Namun apakah ada opsi awkuntuk melakukan itu?

Deano
sumber
Juga lihat serverfault.com/a/547331/313521 untuk jawaban yang lebih umum untuk "mengedit file di tempat dengan pengalihan".
Wildcard
@Wildcard. Solusinya sangat rapuh. Sama sekali tidak ada jaminan pada pemesanan acara, dan menggunakan solusi itu dapat memotong data Anda. Selain itu, saya tidak dapat mengomentari situs itu secara langsung karena saya perlu 50 perwakilan di situs itu untuk melakukannya. Saya tidak akan pernah mengerti mengapa SO terfragmentasi menjadi Unix / Linux dan admin server, dkk. IMO, itu kesalahan.
William Pursell
@ WilliamPursell, "tidak ada jaminan pemesanan acara" - itu sebenarnya salah. Satu-satunya kerapuhan yang dimiliki solusi adalah jika panjang konten lebih besar dari panjang maksimum untuk sebuah perintah. Namun, pemesanan acara dijamin.
Wildcard
@ Kartu Kredit Apa jaminan standar pemesanan itu?
William Pursell
@WilliamPursell dijamin oleh dokumentasi bash. Untuk kerang lain saya tidak tahu. (Omong-omong, jika Anda menautkan akun Anda, Anda akan mendapat 100 bonus asosiasi rep dan akan dapat berkomentar.)
Wildcard

Jawaban:

142

Dalam GNU Awk terbaru (sejak 4.1.0 dirilis ), ia memiliki opsi untuk mengedit file "inplace" :

[...] Ekstensi "inplace", dibangun menggunakan fasilitas baru, dapat digunakan untuk mensimulasikan sed -ifitur " " GNU . [...]

Contoh penggunaan:

$ gawk -i inplace '{ gsub(/foo/, "bar") }; { print }' file1 file2 file3

Untuk menyimpan cadangan:

$ gawk -i inplace -v INPLACE_SUFFIX=.bak '{ gsub(/foo/, "bar") }
> { print }' file1 file2 file3
lind
sumber
1
@sudo_O - Terima kasih atas demonstrasi "inplace". Terbalik jawaban Anda!
lind
Sepertinya opsi tersebut mungkin telah dihapus? Dengan 4.1.3, saya memiliki "-i Includedefile --include = Includedefile"
Keith Hughitt
1
@Keith saya punya pertanyaan yang sama. Saya baru saja mencobanya dan berfungsi pada 4.1.3 saya. inplacesebenarnya perpustakaan yang disertakan dengan gawkmenurut jawaban iiSeymour , jadi inplaceadalah sesuatu yang dapat dimasukkan sebagai includefile.
cxw
Peringatan penting di sini: array 'terlihat' akan diisi dengan garis duplikat dari SEMUA file yang termasuk dalam perintah. Jadi, jika setiap file memiliki mis. Header umum, itu akan dihapus di setiap file setelah yang pertama. Jika Anda ingin memperlakukan setiap file secara independen, Anda harus melakukan sesuatu seperti untuk f di * .txt; do gawk -i inplace '! seen [$ 0] ++' "$ f"; dilakukan
Nick K9
136

Kecuali Anda memiliki GNU awk 4.1.0 atau yang lebih baru ...

Anda tidak akan memiliki opsi seperti opsi sed, -imelainkan lakukan:

$ awk '{print $0}' file > tmp && mv tmp file

Catatan: -iini bukan sihir, itu juga membuat file sementara sedhanya menanganinya untuk Anda.


Pada GNU awk 4.1.0 ...

GNU awkmenambahkan fungsionalitas ini dalam versi 4.1.0 (dirilis 10/05/2013) . Ini tidak lurus ke depan seperti hanya memberikan -iopsi seperti yang dijelaskan dalam catatan yang dirilis:

Opsi -i baru (dari xgawk) digunakan untuk memuat file perpustakaan awk. Ini berbeda dari -f di mana argumen non-opsi pertama diperlakukan sebagai skrip.

Anda harus menggunakan inplace.awkfile include yang dibundel untuk menjalankan ekstensi dengan benar seperti:

$ cat file
123 abc
456 def
789 hij

$ gawk -i inplace '{print $1}' file

$ cat file
123
456
789

Variabel INPLACE_SUFFIXdapat digunakan untuk menentukan ekstensi untuk file cadangan:

$ gawk -i inplace -v INPLACE_SUFFIX=.bak '{print $1}' file

$ cat file
123
456
789

$ cat file.bak
123 abc
456 def
789 hij

Saya senang fitur ini telah ditambahkan tetapi bagi saya, pelaksanaannya sangat tidak awkish sebagai kekuatan berasal dari keringkasan bahasa dan -i inplaceadalah 8 karakter terlalu lama imo .

Ini adalah tautan ke manual untuk kata resmi.

Chris Seymour
sumber
Bukankah seharusnya contoh 'pertama' Anda lebih seperti awk '{ gsub(/foo/, "bar" ) } ; { print $0 }' file > tmp.txt && mv -v tmp.txt file:?
Tony Barganski
Yang mengejutkan saya, pada April 2019, masih di gawk 4.0.2. Jangan biarkan siapa pun memberi tahu Anda versi ini dan itu akan tersedia.
John Lunzer
Litte awk '{print $0}' file | sponge filemenggunakan lebih pendek spongedari moreutils.
brablc
15

@sudo_O memiliki jawaban yang benar .

Ini tidak bisa:

someprocess < file > file

Shell melakukan pengalihan sebelum menyerahkan kontrol ke beberapa proses ( pengalihan ). The >redirection akan memotong file ke nol ukuran ( mengalihkan keluaran ). Oleh karena itu, pada saat suatu proses diluncurkan dan ingin membaca dari file, tidak ada data untuk dibaca.

glenn jackman
sumber
14

hanya sedikit retasan yang berfungsi

echo "$(awk '{awk code}' file)" > file
Yuri G.
sumber
Bekerja seperti pesona! Tetapi apakah mungkin untuk menyimpan perintah awk ke dalam variabel dan hanya menggunakannya dalam trik bagus Anda?
ashrasmun
13

Alternatifnya adalah menggunakan sponge:

awk '{print $0}' your_file | sponge your_file

Di mana Anda mengganti '{print $0}'dengan skrip awk Anda dan your_filedengan nama file yang ingin Anda sunting.

sponge menyerap sepenuhnya input sebelum menyimpannya ke file.

Codoscope
sumber
Bagaimana standar / portabel spons?
Thomas
2
spongeadalah bagian dari moreutils. Jadi itu tidak akan ada secara default di sebagian besar sistem. Tapi sepertinya setidaknya spongeitu sendiri cukup portabel dan dapat dijalankan hampir di mana-mana.
MarSoft
1
Kelemahan dari solusi ini dibandingkan dengan tee-berbasis adalah bahwa spongeakan membaca segalanya untuk RAM sebelum menulis, maka itu akan membeku pada file besar.
MarSoft
5

mengikuti tidak akan berhasil

echo $(awk '{awk code}' file) > file

ini seharusnya bekerja

echo "$(awk '{awk code}' file)" > file
Flowmix Leonsio
sumber
3

Jika Anda menginginkan solusi awk-only tanpa membuat file sementara dan dapat digunakan dengan versi! = (Gawk 4.1.0):

awk '{a[b++]=$0} END {for(c=0;c<=b;c++)print a[c]>ARGV[1]}' file
Elang
sumber
4
Tetapi apakah ini buffer seluruh file ke memori? Pertimbangkan file 20GB.
Amit Naidu
0

Menggunakan tee

 awk '{awk code}' file | tee file

yang teetempat perintah take dan dieksekusi setelah awkperintah selesai karena |.

shaiki siegal
sumber
5
Ini salah. Kedua perintah dieksekusi secara paralel, dan data segera dialirkan melintasi pipa. File apa pun yang lebih besar dari buffer (8192 byte pada mesin saya) akan terpotong dan Anda akan kehilangan data.
tripflag