Bisakah saya membuat `cut` mengubah file pada tempatnya?

17

The manhalaman tidak memberikan banyak harapan, tapi aku berharap itu adalah didokumentasikan (dan / atau GNU-spesifik) fitur.

Hank Gay
sumber

Jawaban:

14

Kamu tidak bisa Baik menggunakan ed atau GNU sed atau perl, atau melakukan apa yang mereka lakukan di belakang layar, yaitu membuat file baru untuk konten.

ed, portabel:

ed foo <<EOF
1,$s/^\([^,]*\),\([^,]*\),\([^,]*\).*/\1,\3/
w
q
EOF

GNU sed:

sed -i -e 's/^\([^,]*\),\([^,]*\),\([^,]*\).*/\1,\3/' foo

Perl:

perl -i -l -F, -pae 'print @F[1,3]' foo

cut, membuat file baru (disarankan, karena jika skrip Anda terganggu, Anda dapat menjalankannya lagi):

cut -d , -f 1,3 <foo >foo.new &&
mv -f foo.new foo

cut, mengganti file pada tempatnya (mempertahankan kepemilikan dan izin foo, tetapi membutuhkan perlindungan terhadap interupsi):

cp -f foo foo.old &&
cut -d , -f 1,3 <foo.old >foo &&
rm foo.old

Saya sarankan menggunakan salah satu cutmetode berbasis. Dengan begitu Anda tidak bergantung pada alat non-standar, Anda dapat menggunakan alat terbaik untuk pekerjaan itu, dan Anda mengontrol perilaku pada interupsi.

Gilles 'SANGAT berhenti menjadi jahat'
sumber
Lebih baik daripada .oldmetode untuk perubahan di tempat,echo "$(cut -d , -f 1,3 <foo)" > foo
GypsyCosmonaut
1
@ GypsyCosmonaut Tidak, ini bukan "lebih baik". Itu lebih rapuh. Satu-satunya keuntungan adalah lebih pendek untuk mengetik. Masalah utama dengan metode Anda adalah bahwa jika terjadi kesalahan saat memproses file input atau menulis output, data akan hilang. Ini juga tidak bekerja dengan data biner: output akan dipotong pada byte nol pertama. Bahkan dengan file teks, itu menghapus baris kosong dari akhir file. Dengan file besar, itu mungkin gagal karena data harus disimpan sebagai string dalam memori shell (dan ingat, jika ini terjadi, data tersebut hilang).
Gilles 'SANGAT berhenti menjadi jahat'
oO Terima kasih, saya tidak tahu mungkin ada masalah dengan byte nol
GypsyCosmonaut
Saya pikir ini lebih sederhana:cut -d , -f 1,3 foo > foo.new rm foo mv foo.new foo
LoMaPh
@ LoMaPh Memang, saya tidak tahu mengapa saya mengganti nama file lama: tidak memiliki kelebihan dibandingkan mengganti nama file baru. Ini juga lebih sederhana karena Anda tidak memerlukan langkahnya rm foo. Dan Anda tidak boleh menelepon rm foo, karena mv foo.new foobersifat atomik: menghapus versi lama dan menempatkan versi baru pada saat yang bersamaan.
Gilles 'SO- berhenti menjadi jahat'
23

The MoreUtils paket dari ubuntu (dan juga debian ) memiliki program yang disebut sponge, yang semacam-of juga memecahkan masalah Anda.

Dari man sponge:

spons membaca input standar dan menuliskannya ke file yang ditentukan. Tidak seperti pengalihan shell, spons menyerap semua inputnya sebelum membuka file output. Ini memungkinkan pipa penyusun yang membaca dari dan menulis ke file yang sama.

Yang memungkinkan Anda melakukan sesuatu seperti:

cut -d <delim> -f <fields> somefile | sponge somefile
Kjetil Jorgensen
sumber
9

Saya tidak berpikir itu mungkin menggunakan cutsendirian. Saya tidak dapat menemukannya di halaman manual atau info. Anda dapat melakukan sesuatu seperti

mytemp=$(mktemp) && cut -d" " -f1 file > $mytemp && mv $mytemp file

mktempmembuat Anda menjadi file sementara yang relatif aman yang dapat Anda gunakan untuk mem-pipe cutoutput.

Steven D
sumber
1

Coba dengan cara vim:

$ ex -s +'%!cut -c 1-10' -cxa file.txt

Ini akan mengedit file di tempat (begitu juga cadangannya terlebih dahulu).

Atau gunakan grep, sedatau gawk.

kenorb
sumber
0

Anda dapat menggunakan slurp dengan POSIX Awk:

cut -b1 file | awk 'BEGIN{RS="";getline<"-";print>ARGV[1]}' file

Contoh

Steven Penny
sumber
0

Nah, karena cutmenghasilkan output lebih sedikit daripada yang dibaca, Anda dapat melakukan:

cut -c1 < file 1<> file

Yaitu, buat stdin menjadi fileopen dalam mode read-only dan stdout fileopen dalam mode read + write tanpa pemotongan ( <>).

Dengan begitu, cuthanya akan menimpa file itu sendiri. Namun, itu akan membuat sisa file tidak tersentuh. Misalnya, jika filemengandung:

foo
bar

Outputnya akan menjadi:

f
b
bar

The f\nb\ntelah menggantikan foo\n, tapi barmasih ada. Anda harus memotong file setelah cutselesai.

Dengan ksh93, Anda dapat melakukannya dengan <>;operatornya yang bertindak seperti <>kecuali bahwa jika perintah berhasil, ftruncate()dipanggil pada deskriptor file. Begitu:

cut -c1 < file 1<>; file

Dengan kerang lain, Anda harus melakukannya ftruncate()melalui beberapa cara lain seperti:

{ cut -c1 < file; perl -e 'truncate STDOUT, tell STDOUT';} 1<> file

meskipun memohon perlhanya untuk itu sedikit berlebihan di sini terutama mengingat yang perldapat dengan mudah melakukan cutpekerjaan seperti:

perl -pi -e '$_ = substr($_, 0, 1)' file

Hati-hati dengan semua metode yang melibatkan penulisan ulang di tempat yang sebenarnya, jika operasi terputus di tengah jalan, Anda akan berakhir dengan file yang rusak. Menggunakan file kedua sementara menghindari masalah ini.

Stéphane Chazelas
sumber