Pindahkan file tetapi hanya jika ditutup

10

Saya ingin memindahkan file besar yang dibuat oleh proses eksternal segera setelah ditutup.

Apakah perintah tes ini benar?

if lsof "/file/name"
then
        # file is open, don't touch it!
else
        if [ 1 -eq $? ]
        then
                # file is closed
                mv /file/name /other/file/name
        else
                # lsof failed for some other reason
        fi
fi

EDIT: file tersebut merupakan dataset dan saya harus menunggu sampai selesai untuk memindahkannya sehingga program lain dapat menindaklanjutinya. Itu sebabnya saya perlu tahu apakah proses eksternal dilakukan dengan file.

Peter Kovac
sumber
3
Catatan samping: setelah file dibuka, proses menggunakan deskriptor file dan data inode untuk memanipulasinya. Mengubah jalur (yaitu memindahkan file) tidak akan menyebabkan terlalu banyak masalah pada prosesnya.
John WH Smith
2
Apakah Anda memiliki kendali atas proses eksternal? Apakah mungkin untuk proses eksternal untuk membuat file sementara dan mengganti nama file setelah selesai menulis untuk itu?
Jenny D
@ Jenny Saya melakukan investigasi dan ternyata itu benar. Saya tidak perlu lsofsama sekali, saya hanya perlu memeriksa apakah ekstensi file tidak .tmp. Itu membuatnya sepele. Namun saya senang saya mengajukan pertanyaan saya karena saya belajar sedikit tentang lsofdan inotifydan sebagainya.
Peter Kovac
@ PeterKovac Saya belajar lebih banyak tentang mereka juga, dari membaca jawaban, jadi saya sangat senang Anda menanyakannya.
Jenny D
@JohnWHSmith - Itu biasanya benar jika memindahkan file dalam sistem file yang sama, jika dia memindahkan file ke sistem file baru sebelum penulis selesai menulis, dia akan kehilangan beberapa data.
Johnny

Jawaban:

11

Dari lsofhalaman manual

Lsof mengembalikan satu (1) jika kesalahan terdeteksi, termasuk kegagalan untuk menemukan nama perintah, nama file, alamat Internet atau file, nama login, file NFS, PID, PGID, atau UID yang diminta untuk didaftar. Jika opsi -V ditentukan, lsof akan menunjukkan item pencarian yang gagal tercantum.

Jadi itu akan menyarankan bahwa lsof failed for some other reasonklausa Anda tidak akan pernah dieksekusi.

Sudahkah Anda mencoba hanya memindahkan file sementara proses eksternal Anda masih terbuka? Jika direktori tujuan berada pada sistem file yang sama, maka seharusnya tidak ada masalah dengan melakukan itu kecuali Anda perlu mengaksesnya di jalur asli dari proses ketiga karena inode yang mendasarinya akan tetap sama. Kalau tidak, saya pikir mvakan gagal pula.

Jika Anda benar-benar harus menunggu sampai proses eksternal Anda selesai dengan file tersebut, Anda lebih baik menggunakan perintah yang memblokir daripada melakukan polling berulang kali. Di Linux, Anda dapat menggunakannya inotifywaituntuk ini. Misalnya:

 inotifywait -e close_write /path/to/file

Jika Anda harus menggunakan lsof(mungkin untuk portabilitas), Anda dapat mencoba sesuatu seperti:

until err_str=$(lsof /path/to/file 2>&1 >/dev/null); do
  if [ -n "$err_str" ]; then
    # lsof printed an error string, file may or may not be open
    echo "lsof: $err_str" >&2

    # tricky to decide what to do here, you may want to retry a number of times,
    # but for this example just break
    break
  fi

  # lsof returned 1 but didn't print an error string, assume the file is open
  sleep 1
done

if [ -z "$err_str" ]; then
  # file has been closed, move it
  mv /path/to/file /destination/path
fi

Memperbarui

Seperti dicatat oleh @JohnWHSmith di bawah ini, desain teraman akan selalu menggunakan lsofloop seperti di atas karena ada kemungkinan bahwa lebih dari satu proses akan membuka file untuk ditulis (contoh kasus mungkin adalah daemon pengindeksan yang ditulis dengan buruk yang membuka file dengan membaca / tulis flag padahal seharusnya hanya dibaca). inotifywaitmasih bisa digunakan daripada tidur, ganti saja garis tidur dengan inotifywait -e close /path/to/file.

Graeme
sumber
Terima kasih, saya tidak menyadarinya inotify. Sayangnya, itu tidak diinstal pada kotak saya tetapi saya yakin saya akan menemukan paket di suatu tempat. Lihat hasil edit saya untuk alasan mengapa saya perlu file ditutup: ini adalah dataset dan harus lengkap sebelum diproses lebih lanjut.
Peter Kovac
1
Catatan lain: sementara inotifywaitakan mencegah skrip dari "polling" dua sering, OP masih perlu memeriksa lsofdalam satu lingkaran: jika file dibuka dua kali, menutup sekali dapat memicu inotifyacara, meskipun file tidak siap untuk menjadi dimanipulasi (misalnya, dalam cuplikan kode terakhir Anda, sleeppanggilan Anda dapat diganti dengan inotifywait).
John WH Smith
@ John a close_writeharus ok karena hanya satu proses yang dapat membuat file terbuka untuk ditulis sekaligus. Itu mengasumsikan bahwa yang lain tidak akan membukanya langsung setelah ditutup, tetapi kemudian masalah yang sama muncul dengan lsofpolling.
Graeme
1
@Graeme Meskipun ini mungkin benar dengan desain dalam kasus OP, kernel memang memungkinkan file dibuka dua kali untuk penulisan (dalam hal ini, CLOSE_WRITEdipicu dua kali).
John WH Smith
@ John, diperbarui.
Graeme
4

Sebagai pendekatan alternatif, ini adalah kasus yang sempurna untuk pipa - proses kedua akan memproses output dari proses pertama segera setelah tersedia, daripada menunggu proses lengkap untuk selesai:

process1 input_file.dat | process2 > output_file.dat

Keuntungan:

  • Jauh lebih cepat secara umum:
    • Tidak harus menulis ke dan membaca dari disk (ini dapat dihindari jika Anda menggunakan ramdisk).
    • Harus menggunakan sumber daya mesin lebih lengkap.
  • Tidak ada file perantara untuk dihapus setelah selesai.
  • Tidak diperlukan penguncian yang rumit, seperti pada OP.

Jika Anda tidak memiliki cara untuk membuat pipa secara langsung tetapi Anda memiliki GNU coreutils, Anda dapat menggunakan ini:

tail -F -n +0 input_file.dat | process2 > output_file.dat

Ini akan mulai membaca file input dari awal, tidak peduli seberapa jauh proses pertama melalui penulisan file (bahkan jika itu belum dimulai atau sudah selesai).

l0b0
sumber
Ya, itu akan menjadi solusi "jelas". Sayangnya, proses menghasilkan data di luar kendali saya (dijalankan oleh pengguna lain).
Peter Kovac
@PeterKovac Itu tidak relevan: cat input_file.dat | process2 output_file.dat
MariusMatutiae
@MariusMatutiae tetapi catdan process2bisa selesai sebelum process1selesai. Mereka tidak akan memblokir.
cpugeniusmv