Mengapa mengarahkan ulang output file ke dirinya sendiri menghasilkan file kosong?

19

Mengapa mengarahkan ulang output file ke dirinya sendiri menghasilkan file kosong?

Dinyatakan dalam Bash, mengapa

less foo.txt > foo.txt

dan

fold foo.txt > foo.txt

menghasilkan yang kosong foo.txt? Karena append seperti less eggs.py >> eggs.pymenghasilkan dua salinan teks eggs.py, seseorang mungkin berharap bahwa overwrite akan menghasilkan satu salinan teks.

Catatan, saya tidak mengatakan ini adalah bug, itu lebih cenderung penunjuk sesuatu yang mendalam tentang Unix.

seewalker
sumber
Ditujukan dalam kanonikal U&L Apa saja kontrol shell dan operator redirection? pertanyaan.
Scott

Jawaban:

20

Ketika Anda menggunakan >, file dibuka dalam mode pemotongan sehingga isinya dihapus sebelum perintah mencoba untuk membacanya.

Saat Anda menggunakan >>, file dibuka dalam mode tambahkan sehingga data yang ada dipertahankan. Namun masih cukup berisiko untuk menggunakan file yang sama dengan input dan output dalam kasus ini. Jika file cukup besar agar tidak sesuai dengan ukuran buffer input baca, ukurannya mungkin tumbuh tanpa batas hingga sistem file penuh (atau kuota disk Anda tercapai).

Jika Anda ingin menggunakan file sebagai input dan output dengan perintah yang tidak mendukung modifikasi tempat, Anda dapat menggunakan beberapa solusi:

  • Gunakan file perantara dan timpa yang asli ketika selesai dan hanya jika tidak ada kesalahan terjadi saat menjalankan utilitas (ini adalah cara paling aman dan lebih umum).

    fold foo.txt > fold.txt.$$ && mv fold.txt.$$ foo.txt
  • Hindari file perantara dengan mengorbankan potensi hilangnya sebagian atau seluruh data jika terjadi kesalahan atau gangguan. Dalam contoh ini, konten foo.txtdilewatkan sebagai input ke subkulit (di dalam tanda kurung) sebelum file dihapus. Inode sebelumnya tetap hidup karena subshell tetap terbuka saat membaca data. File yang ditulis oleh utilitas dalam (di sini fold) sambil memiliki nama yang sama (foo.txt) menunjuk ke inode yang berbeda karena entri direktori lama telah dihapus secara teknis, ada dua "file" berbeda dengan nama yang sama selama proses. Ketika subkulit berakhir, inode lama dilepaskan dan datanya hilang. Berhati-hatilah untuk memastikan Anda memiliki cukup ruang untuk menyimpan sementara file lama dan yang baru pada saat yang sama jika tidak Anda akan kehilangan data.

    (rm foo.txt; fold > foo.txt) < foo.txt
Jlliagre
sumber
3
spongedari moreutils juga dapat membantu. fold foo.txt | sponge foo.txt- atau fold foo.txt | sponge !$juga harus dilakukan.
slhck
@ Slhck Memang, spons bisa melakukan pekerjaan juga. Namun, karena tidak ditentukan oleh POSIX atau arus utama di Unix seperti OS, tidak mungkin ada.
jlliagre
Ini tidak seperti itu tidak dapat dilakukan sekarang meskipun;)
slhck
7

File dibuka untuk ditulis oleh shell sebelum aplikasi memiliki kesempatan untuk membacanya. Membuka file untuk menulis memotongnya.

Ignacio Vazquez-Abrams
sumber
0

Dalam bash, operator redirection aliran ... > foo.txtmengosongkan foo.txt sebelum mengevaluasi operan kiri .

Seseorang dapat menggunakan substitusi perintah dan mencetak hasilnya sebagai solusi. Solusi ini mengambil lebih sedikit karakter tambahan daripada di jawaban lain:

printf "%s\n" "$(less foo.txt)" > foo.txt

Hati-hati: Perintah ini tidak mempertahankan baris baru yang sedang berjalan di foo.txt. Lihat bagian komentar di bawah untuk informasi lebih lanjut

Di sini, subkulit $(...)dievaluasi sebelum operator pengalihan aliran >, maka pelestarian informasi.

Louis-Jacob Lebel
sumber
@KamilMaciorowski: Sebenarnya, ada tmp=$(cmd; printf q);  printf '%s' "${tmp%q}". Tapi Anda melewatkan masalah lain dengan jawaban ini: ia mengatakan "subkulit" ketika itu berarti "substitusi perintah". Ya, pergantian perintah umumnya subkulit, tetapi bukan sebaliknya, dan subkulit, secara umum, tidak membantu untuk masalah ini.
Scott
@ KamilMaciorowski Saya merasa sangat tidak enak karena melewatkan semua ini. Terima kasih telah menunjukkan semua ini. Untuk poin (4) Anda: akankah backquote melakukan trik yaitu mempertahankan trailing newline (s)?
Louis-Jacob Lebel
@Scott, terima kasih atas balasan Anda. Saya mengubah "subkulit" untuk "substitusi perintah". Ngomong-ngomong, aku bertanya-tanya apa perbedaan persis antara keduanya.
Louis-Jacob Lebel
Tidak, backquotes (backticks) juga mengupas karakter baris baru.
Kamil Maciorowski
Baiklah kalau begitu, saya menambahkan pesan peringatan untuk saat ini. Saya akan menghapusnya jika saya menemukan solusi.
Louis-Jacob Lebel