Tentukan apakah file sedang dalam proses ditulis?

25

Saya perlu menggunakan proses otomatis (melalui skrip cron 1 menit) yang mencari file tar di direktori tertentu. Jika file tar ditemukan, itu tidak diarsipkan ke lokasi yang sesuai dan kemudian file tar dihapus.

File tar secara otomatis disalin ke server ini melalui SSH dari server lain. Dalam beberapa kasus, file tar sangat besar, dengan banyak file.

Masalah yang saya harapkan terjadi: Jika perlu> 1 menit untuk file tar untuk disalin ke server, dan skrip cron berjalan sekali setiap menit, itu akan melihat file .tar.gz dan mencoba untuk melakukan untar itu, meskipun file tar masih dalam proses untuk menulis.

Apakah ada cara (melalui perintah bash) untuk menguji apakah suatu file saat ini sedang ditulis, atau apakah itu hanya sebagian file, dll?

Salah satu alternatif yang saya pikirkan adalah memiliki file yang akan disalin sebagai ekstensi file yang berbeda (seperti .tar.gz.part) dan kemudian diganti namanya .tar.gzsetelah transfer selesai. Tapi saya pikir saya akan mencoba mencari tahu apakah ada cara untuk menentukan apakah file tersebut utuh pada baris perintah pertama ... Ada petunjuk?

Jake Wilson
sumber
2
Bagaimana sebenarnya file tersebut ditransfer? Misalnya, rsyncmenggunakan nama file sementara selama transfer (secara default), dan hanya setelah file sepenuhnya ditransfer, mengganti nama file itu menjadi nama file yang sebenarnya.
Piskvor

Jawaban:

12

Anda berada di jalur yang benar, mengganti nama file adalah operasi atom, jadi melakukan penggantian nama setelah pengunggahan sederhana, elegan, dan tidak rentan kesalahan. Pendekatan lain yang dapat saya pikirkan adalah menggunakan lsof | grep filename.tar.gzuntuk memeriksa apakah file sedang diakses oleh proses lain.

Alex
sumber
7
( lsof filename.tar.gzLebih efisien dan lebih akurat daripada lsof | grep filename.tar.gz)
Rich
BTW, itu harus menjadi path absolut dari nama file
DennisLi
14

Taruhan terbaik Anda adalah menggunakan lsofuntuk menentukan apakah suatu file telah dibuka oleh proses apa pun:

#  lsof -f -- /var/log/syslog
COMMAND   PID   USER   FD   TYPE DEVICE SIZE/OFF  NODE NAME
rsyslogd 1520 syslog    1w   REG  252,2    72692 16719 /var/log/syslog

Anda tidak dapat dengan mudah mengetahui apakah itu sedang dalam proses ditulis, tetapi jika sedang ditulis, itu HARUS terbuka.


Sunting: mari kita selesaikan masalah yang sebenarnya di sini daripada mencoba menerapkan solusi yang diusulkan!

Gunakan rsync untuk mentransfer file:

  rsync -e ssh remote:big.tar.gz .

Dengan cara ini, file tidak akan disalin di atas yang sudah ada tetapi disalin ke file sementara ( .big.tar.gz.XXXXXX) sampai transfer selesai, kemudian dipindahkan ke tempatnya.

MikeyB
sumber
6

Agak tua, tetapi sebagian besar jawaban benar-benar melewatkan inti pertanyaan:

Tapi saya pikir saya akan mencoba mencari tahu apakah ada cara untuk menentukan apakah file tersebut utuh pada baris perintah pertama ...

Secara umum, tidak ada. Anda tidak memiliki cukup informasi untuk menentukan hal itu.

Karena menentukan bahwa file ditutup tidak sama dengan menentukan apakah file tersebut utuh . Misalnya, sebuah file akan "ditutup" jika koneksi terputus di tengah jalan saat transfer.

Hanya jawaban Alex yang benar. Dan bahkan dia jatuh cinta untuk menggunakan lsofsedikit.

Untuk menentukan apakah file telah sepenuhnya, berhasil ditransfer membutuhkan lebih banyak data. Seperti:

Salah satu alternatif yang saya pikirkan adalah memiliki file yang akan disalin sebagai ekstensi file yang berbeda (seperti .tar.gz.part) dan kemudian diganti namanya .tar.gzsetelah transfer selesai.

Itu cara yang sangat baik untuk berkomunikasi bahwa file telah sepenuhnya dan berhasil ditransfer. Anda juga dapat memindahkan file dari satu direktori ke direktori lain selama Anda tetap berada dalam sistem file yang sama. Atau minta pengirim mengirim filename.donefile kosong ke sinyal penyelesaian.

Tetapi semua metode harus bergantung pada pengirim entah bagaimana menandakan bahwa transfer telah selesai dengan sukses. Karena hanya pengirim yang memiliki informasi itu.

Beberapa format file (seperti PDF) memiliki data di dalamnya yang memungkinkan Anda untuk menentukan apakah file tersebut selesai. Tetapi Anda harus membuka dan membaca hampir seluruh file untuk mengetahuinya.

lsofhanya akan memberi tahu Anda file tidak lagi terbuka - tidak akan memberi tahu Anda mengapa itu tidak lagi terbuka. Juga tidak akan memberi tahu Anda seberapa besar file yang seharusnya.

Andrew Henle
sumber
1
Saya tidak bisa cukup membuktikan hal ini. Kerja bagus menyelesaikan masalah XY di sini.
Beefster
5

Cara terbaik untuk melakukan ini adalah dengan menggunakan incron ("inotify cron system"). Ini memungkinkan Anda untuk menyetel arloji tidak sah di direktori yang kemudian akan memberi tahu Anda tentang operasi file. Dalam hal ini, Anda harus menonton dir untuk close_write. Itu akan memungkinkan Anda untuk kemudian menjalankan perintah Anda setelah file ditutup setelah menulis.

Kyle
sumber
2

Sepertinya lsof dapat mendeteksi mode apa file dibuka di bawah:

lsof -f -- a_file
COMMAND   PID  USER   FD   TYPE DEVICE SIZE/OFF     NODE NAME
cat     52391 bob    1w   REG    1,2       15 19545007 a_file

Lihat di mana dikatakan 1w? Itu berarti bahwa nomor deskriptor file adalah 1 dan mode adalah w, atau tulis.

Kevin Baragona
sumber
The FDlapangan menunjukkan 3rbagi saya ketika file terbuka untuk membaca.
Sopalajo de Arrierez
0

Menggunakan inotifywaitdapat mencapai apa yang Anda cari - ia memiliki kemampuan untuk menunggu sampai file selesai ditulis sebelum menjalankan perintah.

Berikut ini akan terus menonton folder untuk file baru dan menjalankan perintah dalam loop ketika menulis ke file telah selesai.

WATCH_DIR=/directory/to/monitor
DEST_DIR=/x/y/z

/usr/bin/inotifywait --recursive --monitor --quiet -e moved_to -e close_write --format '%w%f' "$WATCH_DIR" | while read -r INPUT_FILE; do

mv "$0" "$DEST_DIR"

done

Untuk opsi konfigurasi lainnya, lihat https://linux.die.net/man/1/inotifywatch

teeedubb
sumber