Как стать автором
Обновить

Расширяем возможности wget

Настройка Linux
Здравствуйте.

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

Не так давно я начал постигать работу с Linux (в частности Ubuntu 8.10) и у меня появилась задача автоматического скачивания файлов по списку. «wget -i» конечно вещь хорошая, но мне хотелось большего, а именно:
  1. Скачивание списка ссылок из файла
  2. Скачивание одновременно нескольких файлов
  3. Перенос неудавшихся закачек в отдельный список для дальнейших повторных попыток.

Так что появилась необходимость в чуть более продвинутом инструменте для скачивания файлов, чем может предложить wget. Я решил реализовать его с помощью bash. Правда может помешать отсутствие опыта написания bash-скриптов, но как раз пришли выходные и часы проведенные за материалом по теме не прошли зря.

Результатом моей работы стал такой скрипт:

Update: Благодаря совету zencd использовал команду wait для ожидания завершения закачек.
Update 2: shulc указал на ошибку: заменил #!/binbash на #!/bin/sh. darkk подсказал о существовании mktemp для создания временных файлов.
#!/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


* This source code was highlighted with Source Code Highlighter.

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

Запуск:
sh downloader start [количество одновременных скачиваний]
или, как правильно заметил Mezomish, так:
./downloader start [количество одновременных скачиваний]

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

Остановка:
sh downloader stop
или
./downloader stop

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

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

Т.к. это мой первый bash-скрипт, то любые замечания/пожелания/рекомендации очень приветствуются.

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

В константах указаны:
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 пусты) скрипт завершает свою работу.

На этом все. При желании любой, кто немного знаком со скриптописанием, может добавить в него нужные для себя функции.
Теги:linuxbashскриптwget
Хабы: Настройка Linux
Всего голосов 79: ↑70 и ↓9 +61
Просмотры21.4K

Похожие публикации

Linux администратор
до 120 000 ₽Строительный холдинг «ТИТАН-2»Санкт-ПетербургМожно удаленно
Системный администратор Linux/Senior
от 50 000 до 120 000 ₽NetPingМожно удаленно
Старший системный администратор Linux/DevOps
до 250 000 ₽Онлайн-кинотеатр iviМоскваМожно удаленно
C++ Embedded Developer (Linux)
от 150 000 до 250 000 ₽MicroAviaСанкт-Петербург
C++ Developer (Linux)
от 150 000 до 250 000 ₽MicroAviaСанкт-Петербург

Лучшие публикации за сутки