Визначте, чи записується файл у файл?


25

Мені потрібно розгорнути автоматизований процес (через 1 хв. Cron script), який шукає файли tar у певному каталозі. Якщо файл tar-файлу знайдений, він не позначається відповідною локацією, а потім файл tar-файлу видаляється.

Файли tar автоматично копіюються на цей сервер через SSH з іншого сервера. У деяких випадках файли смоли надзвичайно великі, з великою кількістю файлів.

Проблема, яку я очікую для вирішення: Якщо для копіювання файлу tar на копію файлу потрібен> 1 хвилина, а сценарій cron запускається раз на хвилину, він перегляне файл .tar.gz і спробує зробити untar це, навіть незважаючи на те, що файл tar все ще знаходиться в процесі запису.

Чи є якийсь спосіб (за допомогою команд bash) перевірити, чи в даний момент файл записується, чи це лише частковий файл тощо?

Однією з альтернатив, про які я думав, було те, щоб файл було скопійовано як інше розширення файлу (як .tar.gz.part), а потім перейменований на .tar.gzпісля завершення передачі. Але я подумав, що я спробую розібратися, чи просто існує спосіб визначити, чи файл спочатку цілий у командному рядку ... Будь-які підказки?


2
Як саме файл передається? Наприклад, rsyncвикористовується тимчасове ім’я файлу під час передачі (за замовчуванням), і лише після того, як файл буде повністю перенесений, перейменовує його у власне ім'я файлу.
Пісквор

Відповіді:


12

Ви на правильному шляху, перейменування файлу - атомна операція, тому виконання перейменування після завантаження є простим, елегантним та не схильним до помилок. Інший підхід, який я можу придумати, - це використати, lsof | grep filename.tar.gzщоб перевірити, чи доступ до файлу іншим процесом.


7
( lsof filename.tar.gzє більш ефективним і точнішим, ніж lsof | grep filename.tar.gz)
Багатий

До речі, це повинен бути абсолютний шлях до імені файлу
DennisLi

14

Краще всього скористатися, lsofщоб визначити, чи файл був відкритий будь-яким процесом:

#  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

Ви не можете легко сказати, чи це процес написання, але якщо це записується, ОБОВ'ЯЗКОВО бути відкритим.


Редагувати: давайте вирішимо фактичну проблему тут, а не намагатимемось реалізувати запропоноване рішення!

Використовуйте rsync для передачі файлу:

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

Таким чином, файл не буде скопійовано поверх існуючого, а скопіюється у тимчасовий файл ( .big.tar.gz.XXXXXX) до завершення передачі, а потім переміститься на місце.


6

Трохи старий, але більшість відповідей повністю пропускає суть питання:

Але я подумав, що спробую розібратися, чи просто існує спосіб визначити, чи є файл спочатку цілим у командному рядку ...

Взагалі, немає. Вам просто не вистачає інформації, щоб це визначити.

Тому що визначення того, що файл закритий , не те саме, що визначення, чи файл цілий . Наприклад, файл буде "закритий", якщо з'єднання втрачено частково через передачу.

Лише відповідь @ Alex отримала це право. І навіть він впав за lsofдеяке використання .

Щоб визначити, чи файл був повністю, для успішного перенесення потрібно більше даних. Як от:

Однією з альтернатив, про які я думав, було те, щоб файл було скопійовано як інше розширення файлу (як .tar.gz.part), а потім перейменований на .tar.gzпісля завершення передачі.

Це ідеально чудовий спосіб повідомити, що файл передано повністю та успішно. Ви також можете переміщувати файли з одного каталогу в інший, поки ви знаходитесь в одній файловій системі. Або відправник відправить порожній filename.doneфайл, щоб подати сигнал.

Але всі методи повинні покладатися на те, що відправник якось сигналізує про те, що передача завершена успішно. Тому що лише відправник має таку інформацію.

Деякі формати файлів (наприклад, PDF) містять у собі дані, які дозволяють визначити, чи файл повний. Але вам доведеться відкрити і прочитати майже весь файл, щоб дізнатися це.

lsofпросто скаже вам, що файл більше не відкритий - він не скаже вам, чому він більше не відкритий. Також це не скаже вам, який файл має бути великим.


1
Я не можу достатньо підтвердити це. Тут добре вирішується проблема XY.
Beefster

5

Найкращий спосіб зробити це - використовувати incron ("ініціювати систему кронів"). Це дозволяє встановити інотифікований годинник на каталог, який потім сповістить вас про файлові операції. У цьому випадку вам слід поспостерігати за режимом close_write. Це дозволить вам запустити свою команду після закриття файлу після запису.


2

Схоже, що lsof може виявити, у якому режимі файл відкрито під:

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

Подивіться, де написано 1w? Це означає, що номер дескриптора файлу дорівнює 1, а режим - w, або запис.


В FDполе відображається 3rдля мене , коли файл відкритий для читання.
Sopalajo de Arrierez

0

Використовуючи inotifywaitможна досягти того, що ви хочете - він має можливість чекати, поки запис файлу закінчиться перед виконанням команди.

Далі нижче буде постійно переглядати папку для нових файлів та виконувати команду в циклі, коли запис у файл закінчиться.

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

Для отримання додаткових параметрів конфігурації див. Https://linux.die.net/man/1/inotifywatch

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.