Мне нужно развернуть автоматизированный процесс (с помощью 1-минутного скрипта cron), который ищет файлы tar в определенном каталоге. Если файл tar найден, он распаковывается в соответствующее место, а затем файл tar удаляется.
Файлы tar автоматически копируются на этот сервер по SSH с другого сервера. В некоторых случаях файлы tar очень большие и содержат много файлов.
Проблема, с которой я ожидаю столкнуться: если для копирования файла tar на сервер требуется> 1 минуты, а сценарий cron запускается раз в минуту, он увидит файл .tar.gz и попытается выполнить распакуйте его, даже если tar-файл все еще находится в процессе записи.
Есть ли способ (с помощью команд bash) проверить, записывается ли файл в данный момент, или это только частичный файл и т. Д.?
Одна альтернатива, о которой я думал, - это скопировать файл с другим расширением (например, .tar.gz.part
), а затем переименовали в .tar.gz
после завершения передачи. Но я подумал, что попробую выяснить, есть ли просто способ определить, является ли файл целым, в командной строке сначала ... Есть какие-то подсказки?
Лучше всего использовать 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
) до завершения переноса, затем перемещается на место.
Вы на правильном пути, переименование файла - это атомарная операция, поэтому выполнение переименования после загрузки простое, элегантное и не подвержено ошибкам. Другой подход, который я могу придумать, - это использовать lsof | grep filename.tar.gz
чтобы проверить, обращается ли к файлу другой процесс.
Немного устарел, но большинство ответов полностью упускают суть вопроса:
Но я подумал, что попробую выяснить, есть ли просто способ определить, цел ли файл, сначала в командной строке ...
В общем, нет. У вас просто недостаточно информации, чтобы это определить.
Поскольку определение того, что файл закрыто это не то же самое, что определить, является ли файл все. Например, файл будет «закрыт», если соединение будет потеряно во время передачи.
Только ответ @ Alex понял это правильно. И даже он упал на использование lsof
в некотором роде.
Чтобы определить, был ли файл полностью передан, требуется больше данных. Такие как:
Одна альтернатива, о которой я думал, - это скопировать файл с другим расширением (например,
.tar.gz.part
), а затем переименовали в.tar.gz
после завершения передачи.
Это прекрасный способ сообщить, что файл был полностью и успешно передан. Вы также можете перемещать файлы из одного каталога в другой, если вы остаетесь в одной файловой системе. Или попросите отправителя отправить пустой filename.done
файл, чтобы сигнализировать о завершении.
Но все методы должны полагаться на отправителя, каким-то образом сигнализирующего об успешном завершении перевода. Потому что эта информация есть только у отправителя.
Некоторые форматы файлов (например, PDF) содержат данные, которые позволяют определить, является ли файл полным. Но вам нужно открыть и прочитать почти весь файл, чтобы узнать.
lsof
просто скажет вам, что файл больше не открыт - он не скажет вам Зачем он больше не открыт. Также он не скажет вам, насколько большим должен быть файл.
Лучший способ сделать это - использовать Incron ("система inotify cron"). Это позволяет вам установить inotify наблюдайте за каталогом, который затем уведомит вас о файловых операциях. В этом случае вы должны следить за каталогом на предмет close_write. Это позволит вам запустить вашу команду после закрытия файла после записи.
Похоже, что 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, или запись.
С помощью 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
Я использую скрипт python, который выполняет итерацию проверки размера до тех пор, пока он не будет одинаковым на двух итерациях в разное время (в моем случае с 0,05 с разницы между проверками работа выполнена!):
dict={}
for filename in os.listdir(basepath+'/in'+stage):
fullInFilename=myfile
try:
if not filename in dict:
#nuevo item...
time.sleep(0.05)
dict = {filename: os.stat(fullInFilename).st_size}
break
else: # ya existe en dict, terminó de copiar?
time.sleep(0.05)
sizeRegistrado = dict[filename]
sizeActual = os.stat(fullInFilename).st_size
if sizeActual != sizeRegistrado:
# sigue copiando...
dict[filename] = sizeActual
print(sizeActual)
break
else:
# Terminada
#print("pop!")
dict.pop(filename)