четверг, 14 июля 2016 г.

Скрипт для закачки с помощью wget

Найдено на просторах интернета.

Задача.

1. Скачивание списка ссылок из файла
2. Скачивание одновременно нескольких файлов
3. Перенос неудавшихся закачек в отдельный список для дальнейших повторных попыток.


#!/bin/sh
log_dir="${PWD}/log"
list_dir="${PWD}/list"
output_dir=${PWD}
# download_list - файл ссылок для скачивания
download_list="${list_dir}/download.lst"
# В active_list записываются активные закачки
active_list="${list_dir}/active.lst"
# В done_list записываются скачанные ссылки
done_list="${list_dir}/done.lst"
# В error_list записываются неудавшиеся закачки
error_list="${list_dir}/error.lst"
# $timeout - время перед повторной попыткой скачивания неудавшейся закачки
timeout=5


# Перемещает строку $1 из файла $2 в файл $3, нужна для манипуляций со списками
# move_line line source_file dest_file
move_line()
{
  tmp_file=`mktemp -t downloader.XX`
  echo $1 >> $3
  cat $2 | grep -v $1 > $tmp_file
  mv $tmp_file $2
}

# Функция скачивания, в $1 передается номер потока скачивания
download_thread()
{
  thread=$1
  # Цикл скачивания, пока файлы download.lst и error.lst не станут пустыми
  while [ -s $download_list ] || [ -s $error_list ]
  do  
    # Если download.lst пустой - переносим в него строку из error.lst
    if [ ! -s $download_list ]  
    then
      read url < $error_list
      move_line $url $error_list $download_list
      sleep $timeout
    fi
    read url < $download_list
    move_line $url $download_list $active_list
    echo "[Thread ${thread}]Starting download: $url"
    # Старт закачки
    wget -c -o "${log_dir}/wget_thread${thread}.log" -O "${output_dir}/$(basename "$url")" $url
    # Проверка кода завершения wget (Если 0 - закачка успешная)
    if [ $? -eq 0 ]
    then
      # Закачка файла завершилась удачно
      move_line $url $active_list $done_list
      echo "[Thread ${thread}]Download successful: $url"
    else
      # Ошибка закачки - перемещаем в файл с ошибочными ссылками
      move_line $url $active_list $error_list
      echo "[Thread ${thread}]Error download: $url"
    fi
  done
  return 0
}

# Завершает ранее запущенные процессы скрипта и закачки из active.lst
stop_script()
{
  # Убиваем все процессы этого скрипта кроме текущего
  kill -9 `ps ax | grep $0 | grep -v "grep" | awk '{print $1}' | grep -v $$`
  # Убиваем все процессы закачек из active.lst
  while [ -s $active_list ]
  do
    read url < $active_list
    move_line $url $active_list $download_list
    kill -9 `ps ax | grep $url | grep -v "grep" | awk '{print $1}'`
  done
}

case "$1" in
"stop" ) 
  echo "Stoping downloader..."
  stop_script
  echo "Done..."
  ;;
"start" )
  # Проверка наналичие файла со ссылками для скачивания
  if [ ! -e $download_list ]; 
  then 
    echo "[Error] There is no ${list_dir}/download.lst file"
    exit
  fi
  echo "Starting downloader..."
  # На случай вторичного запуска скрипта останавливаем ранее запущенные процессы
  stop_script
  # Если не задано кол-во одновременных закачек в $2, устанавливаем 1 поток
  if [ -z $2 ]
  then
    threads=1
  else
    threads=$2
  fi
  # Запускаем в фоне закачки
  i=1
  while [ $i -le $threads ]
  do
    download_thread $i &
    downloader_pid="${downloader_pid} $!"
    sleep 1
    i=`expr $i + 1`
  done
  if [ ! -e $error_list ]; then touch $error_list; fi
  # Ждем окончания всех закачек
  wait $downloader_pid
  # Все скачали...
  echo "All completed"
  ;;
* )
  echo "Usage:"
  echo "\t$0 start [number of threads]"
  echo "\t$0 stop"
  ;;
esac

return 0


Для работы скрипта необходимо сделать его исполняемым и создать файл "./list/download.lst" со списком ссылок для скачивания.

Запуск:

./downloader start [количество одновременных скачиваний]

Параметр после 'start' необязательный (если его не указать — используется «1»).
Т.е. `./downloader start 2` запустит скрипт с одновременным скачиванием 2-х файлов.

Остановка:

./downloader stop

При завершении скрипта при помощи «Ctrl+C» закачки не завершаются, т.к. работают в фоне, поэтому необходимо выполнить вышеуказанную команду команду для остановки скачивания.

Я решил не загромождать скрипт, но в принципе, не сложно реализуется работа со списками (show — вывод на экран, add — добавление закачки, wipe — очистка). А так он рабочий хоть и с минимальной функциональностью.

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

В константах указаны:
log_dir — папка с логами wget'a (по умолчанию "./log")
list_dir — папка со списками download_list, active_list, done_list, error_list (по умолчанию "./list")
output_dir — папка куда будут сохраняться скачиваемые файлы (по умолчанию ".")
download_list список ссылок для скачивания
active_list — список активных закачек
done_list — список завершенных закачек
error_list — список неудавшихся закачек
timeout — время перед повторной попыткой скачивания неудавшейся закачки

В начале работы скрипт останавливает ранее запущенные его копии, а также закачки из active_list (конечно если такие имеются) с переносом их в download_list. Это делается на случай повторного запуска скрипта до завершения скачивания ранее запущенным процессом. Дальше в цикле создается необходимое количество фоновых закачек. Каждый такой фоновый поток реализуется функцией download_thread(). Ее работа заключается в скачивании файлов из списка пока списки download_list и error_list не станут пустыми. Таким образом основная часть скрипта, проверяя эти файлы узнает закончилась ли скачка. Перед запуском wget'a ссылка переносится из файла download_list в файл active_list. После завершения работы wget'a ссылка переносится, либо в done_list (если код возврата был '0'), либо в error_list (если код возврата был не равен '0').
После того как все скачано (списки download_list и error_list пусты) скрипт завершает свою работу.