Saya mencoba mengedit .hgignore
file dengan cepat dari bash shell Cygwin hari ini, dan saya menambahkan baris yang merupakan kesalahan. Saya tidak yakin apakah ini cara terbaik untuk melakukannya, tetapi saya segera berpikir untuk menggunakan head -1 .hgignore
untuk menghapus baris yang menyinggung (saya sebelumnya hanya memiliki satu baris dalam file). Benar saja, ketika dieksekusi itu memberikan baris pertama sebagai satu-satunya output.
Tetapi ketika saya mencoba mengarahkan output dan menulis ulang menggunakan file head -1 .hgignore > .hgignore
, file itu kosong. Mengapa ini terjadi? Jika saya mencoba menambahkan head -1 .hgignore >> .hgignore
, itu menambahkan dengan benar tetapi ini jelas bukan hasil yang diinginkan. Mengapa pengalihan truncating tidak berfungsi dalam kasus ini?
sumber
cut
perubahan file di tempat? , Bagaimana saya bisa membuat iconv mengganti file input dengan output yang dikonversi?Jawaban:
Ketika shell mendapat baris perintah seperti:
command > file.out
shell itu sendiri membuka (dan mungkin membuat) file bernamafile.out
. Shell menyetel file descriptor 0 ke deskriptor file file yang didapat dari open. Begitulah cara pengalihan I / O bekerja: setiap proses tahu tentang deskriptor file 0, 1 dan 2.Bagian tersulit dari ini adalah cara membuka
file.out
. Sebagian besar waktu, Anda inginfile.out
dibuka untuk menulis pada offset 0 (yaitu terpotong) dan inilah yang shell lakukan untuk Anda. Itu terpotong .hgignore, membukanya untuk menulis, dup'ed yang diajukan ke 0, kemudian dieksekusihead
. Clobbering file instan.Di bash shell, Anda melakukan
set noclobber
untuk mengubah perilaku ini.sumber
Saya pikir Bruce menjawab apa yang terjadi di sini dengan pipa shell.
Salah satu utilitas kecil favorit saya adalah
sponge
perintah dari moreutils . Ini memecahkan masalah ini dengan "menyerap" semua input yang tersedia sebelum membuka file output target dan menulis data. Ini memungkinkan Anda untuk menulis saluran pipa persis seperti yang Anda harapkan:Solusi poor-man adalah mem-pipe output ke file sementara, kemudian setelah pipline selesai (misalnya perintah berikutnya yang Anda jalankan) adalah memindahkan file temp kembali ke lokasi file asli.
sumber
head -1 .hgignore | tee .hgignore
?tee
ada di coreutils, dan sebagai efek samping, ini juga menulis ke STDOUTtee
membuka dan memotong file yang sedang ditulisnya saat instantiated sama seperti yang lainnya sehingga tidak menyelesaikan masalah utama di sini tentang kondisi balapan pada membaca konten file sebelum Anda memotongnya dengan menulis.tee
tampaknya melakukan hal yang diinginkan. Saya punya versi8.13
di komputer saya.tee
Program akan memotong file Anda, tidak setup untuk menggandakan penyangga mereka.Di
file
terpotong sebelumhead
dimulai, tetapi jika Anda menulisnya:tidak seperti
file
yang dibuka dalam mode baca-tulis. Namun, ketikahead
selesai menulis, itu tidak memotong file, jadi baris di atas akan menjadi no-op (head
hanya akan menulis ulang baris pertama di atas dirinya sendiri dan membiarkan yang lain tidak tersentuh).Namun, setelah
head
kembali dan ketikafd
masih terbuka, Anda dapat memanggil perintah lain yang melakukantruncate
.Contohnya:
Yang penting di sini adalah bahwa di
truncate
atas,head
hanya memindahkan kursor untuk fd 1 di dalam file tepat setelah baris pertama. Itu menulis ulang baris pertama yang tidak kita butuhkan, tapi itu tidak berbahaya.Dengan kepala POSIX, kita benar-benar bisa pergi tanpa menulis ulang baris pertama itu:
Di sini, kami menggunakan fakta yang
head
menggerakkan posisi kursor di stdin-nya. Walaupunhead
biasanya akan membaca inputnya dengan potongan besar untuk meningkatkan kinerja, POSIX akan mengharuskannya (jika mungkin) untukseek
kembali tepat setelah baris pertama jika sudah melampaui itu. Namun perlu dicatat bahwa tidak semua implementasi melakukannya.Atau, Anda dapat menggunakan perintah shell
read
sebagai gantinya dalam hal ini:sumber
STDIN
mirip dengan apa yang telah Anda capai menggunakan diperl
atasdd
dapat memotong pada offset absolut sewenang-wenang dalam file sekalipun. Jadi Anda dapat menentukan byte offset dari baris kedua dan memotong dari sana dengandd bs=1 seek="$offset" of=file
Solusi Pria Sejati adalah
atau sebagai one-liner
Atau dengan sed GNU:
(Tidak, saya bercanda. Saya akan menggunakan editor interaktif.
vi .hgignore
GddZZ
)sumber
:wq
lebihZZ
?:x
itulah yang jari saya lakukan secara otomatisZQ
sama dengan:q!
Anda dapat menggunakan Vim dalam mode Ex:
2,
pilih garis 2 sampai akhird
menghapusx
Simpan dan tutupsumber
Untuk mengedit file di tempat Anda juga dapat menggunakan trik menangani file terbuka seperti yang ditunjukkan oleh Jürgen Hötzel di Redirect output dari sed 's / c / d /' myFile ke myFile .
sumber
rm .hgignore
kekuatan Anda gagal, mengambil jam kerja keras. Oke, itu tidak penting.hgignore
, tetapi mengapa Anda tetap melakukan sesuatu yang rumit? Jadi downvote saya: secara teknis benar tetapi ide yang sangat buruk.perl -i
(untuk mengedit di tempat), dan saya tidak akan terkejut jika beberapa implementasised -i
melakukannya juga (meskipun versi terbaru dari GNUsed
tampaknya tidak).