Определить, находится ли файл в процессе записи?

25

Мне нужно развернуть автоматизированный процесс (через 1-минутный скрипт cron), который ищет файлы tar в определенной директории. Если файл tar найден, он не попадает в соответствующее место, а затем файл tar удаляется.

Файлы tar автоматически копируются на этот сервер через SSH с другого сервера. В некоторых случаях файлы tar очень большие, с большим количеством файлов.

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

Есть ли способ (с помощью команд bash) проверить, записывается ли файл в данный момент, или это только частичный файл и т. Д.?

Одна из альтернатив, о которой я думал, - это скопировать файл с другим расширением (например .tar.gz.part), а затем переименовать .tar.gzпосле завершения передачи. Но я решил, что постараюсь выяснить, есть ли просто способ определить, является ли файл первым в командной строке ... Есть какие-нибудь подсказки?

Джейк Уилсон
источник
2
Как именно файл передается? Например, rsyncиспользует временное имя файла во время передачи (по умолчанию) и только после того, как файл полностью передан, переименовывает его в фактическое имя файла.
Писквор

Ответы:

12

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

Alex
источник
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) до завершения передачи, а затем перемещен на место.

MikeyB
источник
6

Немного стар, но большинство ответов совершенно не соответствует сути вопроса:

Но я решил попытаться выяснить, есть ли просто способ определить, является ли файл первым в командной строке ...

В общем, нет. Вам просто не хватает информации, чтобы определить это.

Потому что определение того, что файл закрыт, - это не то же самое, что определение, является ли файл целым . Например, файл будет «закрыт», если соединение потеряно во время передачи.

Только @ Алекс ответ получил это право. И даже он упал на использование lsofнесколько.

Чтобы определить, был ли файл полностью передан, требуется больше данных. Такие как:

Одна из альтернатив, о которой я думал, - это скопировать файл с другим расширением (например .tar.gz.part), а затем переименовать .tar.gzпосле завершения передачи.

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

Но все методы должны полагаться на отправителя, каким-то образом сигнализируя об успешном завершении передачи. Потому что только отправитель имеет эту информацию.

Некоторые форматы файлов (например, PDF-файлы) содержат данные, позволяющие определить, завершен ли файл. Но вы должны открыть и прочитать почти весь файл, чтобы узнать.

lsofпросто скажет вам, что файл больше не открыт - он не скажет вам, почему он больше не открыт. Он также не скажет вам, насколько большим должен быть файл.

Эндрю Хенле
источник
1
Я не могу высказать это достаточно. Хорошая работа по решению проблемы XY здесь.
Бифстер
5

Лучший способ сделать это - использовать incron («inotify cron system»). Это позволяет вам установить отслеживание inotify в каталоге, который затем уведомит вас о файловых операциях. В этом случае вы должны смотреть каталог для 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 или write.

Кевин Барагона
источник
В FDполе отображается 3rдля меня , когда файл открыт для чтения.
Сопалахо де Арриерес
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.

teeedubb
источник