diff --git a/zfs_send.zsh b/zfs_send.zsh index 0aa0401..78a7b30 100755 --- a/zfs_send.zsh +++ b/zfs_send.zsh @@ -1,5 +1,16 @@ #!/bin/zsh +#TODO list +# 1. Скрипт не определяет, что нужно остановиться +# 2. Возникают ошибки: +# sending main_pool/3D_Design@manual-20250327 (99%: 21.9G/21.9G) 25.6M/s date: invalid date ‘@0.’ +# date: invalid date ‘@954.’ +# sending main_pool/3D_Design@manual-20250327 (100%: 21.9G/21.9G) 25.6M/s date: invalid date ‘@-1.’ +# date: invalid date ‘@954.’ +# 3. Нужно автоматически определить, если копирование уже запущено то предложить остановить и вывести процесс резервирования +# смотреть нужно сразу на всей системе. Не будем запускать несколько параллельных резервирований. + + backup_server="192.168.0.162" # Сервер для резервирования backup_user="root" # Пользователь на сервере inc_snapshot="manual-$(date +%Y%m%d)" # Новый создаваемый снисмок @@ -123,10 +134,14 @@ identitiesOnly yes + +# ! # Если нужно вывести только прогресс, то пропускаем все этапы подготовки и резервирования if [[ $progress_only = false ]]; then + + # Завершение запущенного процесса резервирования if [[ $stop = true ]]; then pids=$(ps -t $(tty | sed 's|/dev/||') -o pid,args | grep -e zfs_send.zsh -e "zfs send" | grep -v -- '--stop') # Список PIDs с командой запуска @@ -369,6 +384,7 @@ if [[ $work = "y" || $work = "Y" ]]; then (( n_tasks++ )) echo "$key \t ${last_loc_snaps[$loc_ds]} \t -> \t${work_datasets[$loc_ds]}" >> "$LOGFILE" done + echo $n_tasks > /dev/shm/backup_n_tasks echo_log echo_log "--- Резервирую данные ---" @@ -391,74 +407,167 @@ if [[ $work = "y" || $work = "Y" ]]; then done & # Запустить резервирование в фоне fi + + +# ! # Конец блока условия progress_only fi + + # Вывод прогресса +# Это отдельный блок программы, который не имеет прямого доступа к переменным процесса в фоне. +# Обмен переменными проивится ерез временный файл if [[ $progress == true ]]; then echo "" - pids=$(ps -t $(tty | sed 's|/dev/||') -o pid,args | grep -e zfs_send.zsh -e "zfs send" ) # Список PIDs с командой запуска + pids=$(ps -t $(tty | sed 's|/dev/||') -o pid,args | grep -e zfs_send.zsh -e "zfs send" | grep -v -- 'grep' | grep -v -- 'progress-only' ) # Список PIDs с командой запуска if [[ $pids = "" ]]; then echo "Нет запущенного процесса резервирования в этом терминале." read "srch?Икать во всех процессах системы? (y/N) " if [[ $srch = y ]]; then - pids=$(ps -o pid,args | grep -e zfs_send.zsh -e "zfs send" ) + pids=$(ps a -o pid,args | grep -e zfs_send.zsh -e "zfs send" | grep -v -- 'grep' | grep -v -- 'progress-only' ) fi fi - +echo $pids # Выводить прогресс запрашивая список запущенных процессов if [[ $pids = "" ]]; then echo "Нет запущенного процесса резервирования" else echo "" + # инициализируем переменнные + TS0=$(< /dev/shm/backup_time_start) + TS1=$(< /dev/shm/backup_time_circle) + last_percent=0 - time_changed_percent_value=$(date +%s) + last_volume=0 + last_volume_time=$TS1 + speed=0 + reset_interval=10 # интревал сбрасывания скорости до нуля, в случае если нет именений + last_update_time=$TS1 # Время последнего обновления + + time_changed_percent_value=$TS1 + + n_tasks=$(< /dev/shm/backup_n_tasks) + i_task=$(< /dev/shm/backup_i_task) while [[ $i_task != $n_tasks ]]; do # выводим статус пока не завершены все задачи progress=$(ps -u | grep "sending" | grep -v "grep" | sed -r "s/(.*) zfs: (.*)/\2/") - percent=$(echo $progress | cut -d "(" -f2 | cut -d "%" -f1 ) + + # Извлекаем объем и процент + progress_part=$(echo "$progress" | grep -oP '\(\K.*(?=\))') # Извлекаем только часть в скобках + + if [[ -n $progress_part ]]; then + # Разбиваем часть на компоненты + # Процент определим по объёму + # percent=$(echo "$progress_part" | awk -F': |/' '{print $1}' | tr -d '%') + current_volume_str=$(echo "$progress_part" | awk -F': |/' '{print $2}') + total_volume_str=$(echo "$progress_part" | awk -F': |/' '{print $3}') + + # Определяем единицу для интервала сброса + unit=${current_volume_str: -1} + case $unit in + G) reset_interval=100;; + M) reset_interval=10;; + *) reset_interval=5;; + esac + + # Преобразуем в байты + current=$(echo "$current_volume_str" | awk '{sub(/[^0-9.]/,""); print $1}') + case $unit in + G) current_volume=$(( current * 1073741824 )) ;; + M) current_volume=$(( current * 1048576 )) ;; + K) current_volume=$(( current * 1024 )) ;; + *) current_volume=0 ;; + esac + + total=$(echo "$total_volume_str" | awk '{sub(/[^0-9.]/,""); print $1}') + case $unit in + G) total_volume=$(( total * 1073741824 )) ;; + M) total_volume=$(( total * 1048576 )) ;; + K) total_volume=$(( total * 1024 )) ;; + *) total_volume=0 ;; + esac + + percent=$(( current_volume * 100 / total_volume )) + else + percent=0 + current_volume=0 + fi + + # Вычисляем скорость + now=$(date +%s) + + # Если объем изменился + elapsed_from_vol_change=$((now - last_volume_time)) + if [[ $current_volume -ne $last_volume && elapsed_from_vol_change -ne 0 ]]; then + speed=$(awk "BEGIN { printf \"%.1f\", ($current_volume - $last_volume) / $elapsed_from_vol_change }") + last_volume=$current_volume + last_volume_time=$now + last_update_time=$now # Обновляем время последнего изменения + else + # Если объем не изменился, проверяем время с последнего обновления + time_since_update=$((now - last_update_time)) + if (( time_since_update >= reset_interval )); then + speed=0 # Обнуляем скорость, если нет изменений в течение интервала + last_update_time=$now # Обновляем время + fi + fi + + # Конвертируем скорость в человекочитаемый формат + if (( speed > 1073741824 )); then + speed_human=$(awk "BEGIN { printf \"%.1fG\", $speed / 1073741824 }") + elif (( speed > 1048576 )); then + speed_human=$(awk "BEGIN { printf \"%.1fM\", $speed / 1048576 }") + elif (( speed > 1024 )); then + speed_human=$(awk "BEGIN { printf \"%.1fK\", $speed / 1024 }") + else + speed_human="${speed}B" + fi + + + # Начинаем вывод + echo -n "\033[2K\r$progress $speed_human/s " + if (( percent > 0 )); then now=$(date +%s) if (( percent != last_percent )); then time_changed_percent_value=$now last_percent=$percent TS1=$(< /dev/shm/backup_time_circle) - elapsed=$(( $(date +%s) - TS1 )) + elapsed_total=$(( $(date +%s) - TS1 )) fi time_part_of_percent=$(( now - time_changed_percent_value )) # Время с последнего изменения процента - estimated_total=$(( 100 / percent * elapsed )) # Всего времени на задачу - estimated_remain=$(( estimated_total - elapsed - time_part_of_percent )) # Осталось времени на задачу + estimated_total=$(( 100 / percent * elapsed_total )) # Всего времени на задачу + estimated_remain=$(( estimated_total - elapsed_total - time_part_of_percent )) # Осталось времени на задачу if (( estimated_remain >= 86400 )) || (( estimated_total >= 86400 )); then # Если осталось более чем сутки, то отобразить дни estimated_remain_days=$(( estimated_remain / 86400 )); estimated_remain_time=$(( estimated_remain % 86400 )) estimated_total_days=$(( estimated_total / 86400 )); estimated_total_time=$(( estimated_total % 86400 )) - echo -n "\033[2K\r$progress $estimated_remain_days д. $(date -d@$estimated_remain_time -u '+%H:%M.%S') ост. / $estimated_total_days д. $(date -d@$estimated_total_time -u '+%H:%M.%S') " + echo -n " $estimated_remain_days д. $(date -d@$estimated_remain_time -u '+%H:%M.%S') ост. / $estimated_total_days д. $(date -d@$estimated_total_time -u '+%H:%M.%S') " else - echo -n "\033[2K\r$progress $(date -d@$estimated_remain -u '+%H:%M.%S') ост. / $(date -d@$estimated_total -u '+%H:%M.%S') " + echo -n " $(date -d@$estimated_remain -u '+%H:%M.%S') ост. / $(date -d@$estimated_total -u '+%H:%M.%S') " fi - sleep 1 else - echo -ne "\033[2K\r$progress " - sleep 1 - for ((i = 1; i < 3 ; i++)); do echo -n "."; sleep 1; done + for ((i = 1; i < 4 ; i++)); do echo -n "."; sleep 1; done fi - i_task=$(< /dev/shm/backup_i_task) + + sleep 1 + done + if [[ -f /dev/shm/backup_time_start ]]; then + TS0=$(< /dev/shm/backup_time_start) + echo_log "\n--- Все завершено за $(date -d@$(( $(date +%s) - $TS0 )) -u '+%H:%M.%S') ---\n" + else + echo_log "\n--- Все задания завершены ---\n" + fi + + # Удалить временные файлы состояний, если они существуют + [ -f /dev/shm/backup_i_task ] && rm /dev/shm/backup_i_task + [ -f /dev/shm//dev/shm/backup_time_start ] && rm /dev/shm/backup_time_start + [ -f /dev/shm//dev/shm/backup_time_circle ] && rm /dev/shm/backup_time_circle + fi - if [[ -f /dev/shm/backup_time_start ]]; then - TS0=$(< /dev/shm/backup_time_start) - echo_log "\n--- Все завершено за $(date -d@$(( $(date +%s) - $TS0 )) -u '+%H:%M.%S') ---\n" - else - echo_log "\n--- Все задания завершены ---\n" - fi - - # Удалить временные файлы состояний, если они существуют - [ -f /dev/shm/backup_i_task ] && rm /dev/shm/backup_i_task - [ -f /dev/shm//dev/shm/backup_time_start ] && rm /dev/shm/backup_time_start - [ -f /dev/shm//dev/shm/backup_time_circle ] && rm /dev/shm/backup_time_circle - - fi