Page 1 of 3 123 LastLast
Results 1 to 15 of 33

Thread: Еще один скрипт для фонового управления однопоточной загрузкой через wget

  1. #1
    Join Date
    Feb 2007
    Location
    Moscow, Russia
    Posts
    3,805

    Еще один скрипт для фонового управления однопоточной загрузкой через wget

    Хотя в принципе в конфе вопрос уже не раз обсуждался и предлагались разные скрипты для этой цели ( например, здесь: http://www.mtdev.com/2002/05/linux-wget/ ), тем не менее поделюсь скриптом собственной разработки, который осуществляет управлять закачкой с помощью wget в 1 поток. Скрипт написан на tcl. Для его работы требуется установить tcl, а также внешний wget:
    PHP Code:
    ipkg install tcl
    ipkg install wget 
    Добавление закачки производится путем добавления URL в текстовый файл ( /opt/tmp/wget/wget.list ), содержащий список закачек.

    А вот собственно код
    /opt/local/bin/check.wget.tcl :
    PHP Code:
    #!/bin/sh
    # The next line is executed by /bin/sh, but not tcl \
    exec tclsh8.4 "$0" ${1+"$@"}

    #This script was created by me (al37919) in an effort that it will be useful for myself.
    #Use it on your ouwn risk. No warranty of any kind. No license of any kind. :)
    #Version 2.0 { added support of pid file }
    #Version 2.2 { bufixes }

    #REFRESH_TIME is measured in seconds
    set REFRESH_TIME         30

    set WGET_BIN             
    [file join opt bin wget]
    set WGET_BASE            [file join opt tmp wget]

    set WGET_LIST            [file join $WGET_BASE wget.list]
    set WGET_LIST_COMPLETED  [file join $WGET_BASE wget.completed.list]
    set WGET_LIST_NOT_FOUND  [file join $WGET_BASE wget.not_found.list]
    set WGET_LOG             [file join $WGET_BASE wget.log]

    set WGET_PARTIAL         [file join $WGET_BASE partial]
    set WGET_COMPLETED       [file join $WGET_BASE completed]

    catch { 
    exec sed -n 3p $argv0 awk { { print $} } } TCLSH_BIN

    #Returns list of lines read from the FileName
    #Empty lines are skipped. Whitespaces are trimmed from both sides
    proc FileToList {FileName} {
        if {[catch {
    open $FileName rf]} {
            
    puts "Unable to open \"$FileName\" for reading; error $f."
            
    exit
        }
        
    set lList {}
        
    set str ""
        
    while {![eof $f] } {
            
    gets $f str
            
    if {[string trim $strne ""} {
              
    lappend lList [string trim $str]
            }
        }
        
    close $f
        
    return $lList
    }

    #Writes lList of lines into the FileName
    #Whitespaces are trimmed from both sides
    proc ListToFile {FileName lList} {
        if {[catch {
    open $FileName wf]} {
            
    puts "Unable to open \"$FileName\" for writing; error $f."
            
    exit
        }
        foreach 
    i $lList {
          
    puts $f [string trim $i]
        }
        
    close $f
    }

    #Checks that ::PIDFILE entries are really running in the memory
    #In the ::PIDFILE there are 2 lines:
    #Line 1 is the pid of this daemon itself (started through ::TCLSH_BIN)
    #Line 2 is the pid of wget instance started from the daemon
    proc CheckRunning LineNum Name } {
      
    set Running 0
      
    if { [file exists $::PIDFILE] } {
        catch { 
    exec sed -n $LineNum\$::PIDFILE p1
        
    catch { exec pidof $Name p2
        set lP2 
    [split $p2]
        foreach 
    i $lP2 {
          if {
    $p1 == $i} {
            
    incr Running
            
    break
          }
        }
      }
      return 
    $Running
    }

    if { 
    $argc != || [lindex $argv 0ne "-p" } {
      
    puts "USAGE:"
      
    puts "$argv0 -p pidfile"
      
    exit
    }

    set PIDFILE [lindex $argv 1]

    if {[
    CheckRunning 1 $::TCLSH_BIN] > 0} { 
      
    puts "$::TCLSH_BIN $argv0 is already running"
      
    exit
    }

    set lList [FileToList $::WGET_LIST]
    set CurrentURL [lindex $lList 0]

    #This is just precaution. If these dirs exist nothing will happen.
    file mkdir $WGET_PARTIAL $WGET_COMPLETED

    #The following is useful if the $WGET_LIST is empty while starting of $argv0
    catch {open $PIDFILE wf
    puts $f 
    [pid]
    close $f

    while {1} {
    #  set CurrentConnections [regexp -all {bin/wget} [exec ps]]
      
    if {[CheckRunning 2 wget] == 0} {
        
    set FileName [lindex [file split $CurrentURLend]
        
    set lList [FileToList $::WGET_LIST]
        
    set Out [exec head -n3 $::WGET_LOG]
        
    set rule "^.*$FileName.*"
    #Do nothing if the line is empty (this includes also empty input file)
        
    if { "X$CurrentURLne "X"} {
    #Let's check that log corresponds to the top line of wget.list
          
    if {[regexp $rule $Out]} {
            
    set Out [exec tail -n3 $::WGET_LOG]
            
    set rule "^.*$FileName.*saved.*"
    #Check if the file is completed
            
    if {[regexp $rule $Out] || [regexp {The file is already fully retrievednothing to do} $Out]} {
              
    file rename -force [file join $::WGET_PARTIAL $FileName] [file join $::WGET_COMPLETED]
              for {
    set i 0} {$i < [llength $lList]} {incr i} {
                if {[
    lindex $lList $ieq $CurrentURL} {
                  
    set lList [lreplace $lList $i $i]
                  
    incr i -1
                
    }
              }
              
    ListToFile $::WGET_LIST $lList
              set f 
    [open $::WGET_LIST_COMPLETED a]
              
    puts $f $CurrentURL
              close $f
              set CurrentURL 
    ""
            
    }
    #Check if URL is not found
            
    set rule "^.*ERROR 404.*Not Found.*|^.*Resolving.*failed: Name or service not known.*|^.*not an http or ftp url.*"
            
    if {[regexp $rule $Out]} {
              for {
    set i 0} {$i < [llength $lList]} {incr i} {
                if {[
    lindex $lList $ieq $CurrentURL} {
                  
    set lList [lreplace $lList $i $i]
                  
    incr i -1
                
    }
              }
              
    ListToFile $::WGET_LIST $lList
              set f 
    [open $::WGET_LIST_NOT_FOUND a]
              
    puts $f $CurrentURL
              close $f
              set CurrentURL 
    ""
            
    }
          }
        }
        if {[
    llength $lList] != 0} {
          
    set CurrentURL [lindex $lList 0]
    #      puts "starting new instance of wget"
    #     set WgetPid [exec $::WGET_BIN $CurrentURL --continue --directory-prefix=$::WGET_PARTIAL --output-file=$::WGET_LOG &]
          
    set WgetPid [exec $::WGET_BIN $CurrentURL --$::WGET_PARTIAL -$::WGET_LOG &]
          catch {
    open $PIDFILE wf
          puts $f 
    [pid]
          
    puts $f $WgetPid
          close $f
        
    }
      }
      
    after [expr {$::REFRESH_TIME 1000}]

    Этот скрипт сидит в оперативке и каждые 30 сек проверяет запущен ли wget. Если нет, то он запускает загрузку файла, который записан в первой строке файла /opt/tmp/wget/wget.list Первая версия использовала cron, однако, после достаточно длительного использования было обнаружено, что некоторые процессы не завершаются и остаются сидеть в памяти. Почему --- не знаю. Меня это несколько достало и я переделал этот скрипт и превратил его в демона.

    второй скрипт под названием /opt/etc/init.d/S99wget запускает check.wget.tcl при перезагрузках, а также служит для запуска/остановки его вручную.
    PHP Code:
    #!/bin/sh
    PATH=/sbin:/bin:/usr/bin:/usr/sbin:/opt/bin:/opt/sbin:/opt/local/bin

    NAME
    =check.wget.tcl
    DAEMON
    ="/opt/local/bin/$NAME"
    PIDFILE="/var/run/$NAME.pid"

    start() {
      echo -
    "Starting $NAME... "
      
    if [ ! -e $PIDFILE ] || [ -$(ps awk '{print $1}' grep `sed -n 1p $PIDFILE`) ] 
      
    then
        $DAEMON 
    -p $PIDFILE &
        echo 
    "done."
      
    else
        echo 
    "already running."
      
    fi
    }

    stop() {
      echo -
    "Shutting down $NAME... "
      
    if [ -e $PIDFILE ]
      
    then
        kill 
    "`sed -n 2p $PIDFILE`" 2> /dev/null
        kill 
    "`sed -n 1p $PIDFILE`" 2> /dev/null
        rm $PIDFILE
        
    echo "done."
      
    else
        echo 
    "not started."
      
    fi
    }

    reload() {
      echo -
    "Reloading $NAME... "
      
    if [ -e $PIDFILE ]
      
    then
        kill 
    "`sed -n 2p $PIDFILE`" 2> /dev/null
        
    echo "done."
      
    else
        echo 
    "not started."
      
    fi
    }

    case 
    "$1" in
        start
    )
            
    start
            
    ;;
        
    stop)
            
    stop
            
    ;;
        
    restart)
            
    stop
            sleep 1
            start
            
    ;;
        
    reload)
            
    reload
            
    ;;
        *)
            echo 
    "Usage: $0 (start|stop|reload|restart)"
            
    exit 1
            
    ;;
    esac
    exit 
    Продолжение см. в следующем посте --- этот стал слишком длинным и его форум не переваривает
    Last edited by al37919; 23-10-2008 at 06:17.

  2. #2
    Join Date
    Feb 2007
    Location
    Moscow, Russia
    Posts
    3,805
    Для работоспособности всего этого создаем вручную директории, делаем оба вышеуказанных файла исполняемыми, а также создаем пару файлов:
    PHP Code:
    mkdir -/opt/tmp/wget/completed
    mkdir 
    -/opt/tmp/wget/partial
    chmod 755 
    /opt/local/bin/check.wget.tcl
    chmod 755 
    /opt/etc/init.d/S99wget
    touch 
    /opt/tmp/wget/wget.list
    touch /opt/tmp/wget/wget.log 
    Первый файл запускать не требуется. Все управление сосредоточено в S99wget.
    Синтаксис /opt/etc/init.d/S99wget command . Поддерживаются следующие значения параметра command:
    start --- запуск демона
    stop --- остановка демона и загрузки
    reload --- повторно считать wget.list и запустить первую строку на закачку без остановки демона
    restart --- перезапуск демона и загрузки

    В течение пары последних дней оба скрипта были переработаны и теперь используют pid-файл. Таким образом, можно быть уверенным, что stop, reload, restart оказывают действие только на те процессы wget и tclsh8.4, которые были запущены демоном. Именно поэтому, для управления должен использоваться только скрипт S99wget

    Используемые файлы:
    /opt/tmp/wget/wget.list : список URL-ов предназначеных к закачке. Единственный файл, который имеет смысл трогать вручную --- добавлять сюда новые URL-ы. Если надо изменить последовательность закачиваний --- меняйте местами строки в этом файле. Закачка идет построчно сверху вниз. При замене первой строчки загрузка предыдущего файла будет продолжаться до конца, потом пойдет новый первый. Если надо запустить загрузку нового первого файла не дожидаясь окончания предыдущего, надо выполнить S99wget reload При этом неоконченый предыдущий сохранится и когда до него дойдет очередь будет загружаться с того места на котором остановился.
    /opt/tmp/wget/wget.log : процесс закачки текущего файла.
    /opt/tmp/wget/wget.completed.list : список закачаных URL-ов
    /opt/tmp/wget/wget.not_found.list : список ненайденных URL-ов
    /opt/tmp/wget/completed : директория, содержащая закачаные файлы
    /opt/tmp/wget/partial : директория, содержащая частично-закачаные файлы

    Наблюдается и некоторая странность --- ps показывает, что в памяти сидят 3 процесса check.wget.tcl Памяти они лишней вроде не занимают. Будем считать, что это не баг, а фича. Хотя если кто значет почему так и можно ли сделать иначе, будет любопытно узнать.
    P.S. Оказалось, что это действительно фича. При переходе на tcl-8.4.14 (откомпиленный нативно, хотя скорее всего дело не в этом) три процеса в памяти исчезают, оставтеся один.

    Если Вам больше по душе curl, то переделать этот скрипт под него не составит труда, по сути надо изменить только имя сервиса, путь и одну коммандную строку вызова.

    У меня все это живет уже месяца два с лишним и лично меня вполне удовлетворяет.

    Тут поступило следующее пожелание --- ограничение времени работы загрузчика определенным временным интервалом. Т.е., скажем с 22.00 до 8.00 работать, остальное время не работать. Для этого предлагаю сделать следующее

    1) Установить cron и отконфигурить его, согласно инструкции в этой конференции.
    2) дописать 2 строки в /opt/etc/crontab
    PHP Code:
    cat >> /opt/etc/crontab << EOF
    0 22 
    * * * root /opt/etc/cron.d/start_wget
    0 8 
    * * * root /opt/etc/cron.d/stop_wget
    EOF 
    3) создать файлы start_wget и stop_wget и сделать оба файла исполняемыми
    PHP Code:
    cat > /opt/etc/cron.d/start_wget << EOF
    #! /bin/sh
    /opt/etc/init.d/S99wget start
    EOF

    cat 
    > /opt/etc/cron.d/stop_wget << EOF
    #! /bin/sh
    /opt/etc/init.d/S99wget stop
    EOF

    chmod 600 
    /opt/etc/crontab
    chmod 700 
    /opt/etc/cron.d/start_wget
    chmod 700 
    /opt/etc/cron.d/stop_wget 
    По-моему, все.
    Last edited by al37919; 15-06-2007 at 09:15.

  3. #3
    1. Как установить внешний wget? ipkg install wget пишет, что такого пакета нет (да и в файле http://ipkg.nslu2-linux.org/feeds/un...l500g/Packages он отсутствует).
    2. Команда /opt/etc/init.d/S99wget start приводит к ошибке:
    /bin/sh: Can't open
    Это из-за неустановленого wget?
    3. А встроеный в прошивку wget не подойдет?

  4. #4
    Join Date
    Feb 2007
    Location
    Moscow, Russia
    Posts
    3,805
    1) правильный репозиторий пакетов не unslung/wl500g , а optware/oleg . Там их существенно больше. К сожалению, при переключении на него надо переустанавливать все пакеты. Правильная строка в /opt/etc/ipkg.conf
    src optware http://ipkg.nslu2-linux.org/feeds/op...g/cross/stable

    В общем, см: http://wl500g.info/showthread.php?t=8970

    2) Символы конца строки во всех исполняемых скриптах должны быть не CR+LF как в винде, а только LF (стандарт unix).

    Как этого добиться.
    а) Если установлен внешний busybox (проверял только на optware, хотя в unslung вроде та же версия), то в нем есть утилита dos2unix, которая может конвертить в правильный формат.
    б) Если установлено nano, то при сохранении ( ctrl-X Y ) (т.е. в тот момент, когда запрашивается имя файла для сохранения), по крайней мере у меня показывается внизу, что это файл типа DOS и можно этот режим выключить (Esc D).
    в) Кстати, только что посмотрел --- есть даже 2 плугина к far, которые обещают делать эту самую конверсию. Пока не пробовал как это работает.
    г) Вот, предложили еще один вариант, пожалуй наиболее универсальный:
    PHP Code:
    cat filename tr -"\r" filename.new; mv -f filename.new filename 
    3) Встроенный не пойдет, т.к. он не поддерживает опцию -o (запись лога закачки). Решение об окончании закачки (а также об ошибке закачки) и переходу к следующей принимается на основании анализа лога.
    Last edited by al37919; 20-04-2007 at 11:02.

  5. #5
    почему-то после первой же скачки демон вываливается с ошибкой, приходится постоянно перезапускать

    Code:
    error renaming "/opt/tmp/wget/partial/fat.rar": no such file or directory
        while executing
    "file rename -force [file join $::WGET_PARTIAL $FileName] [file join $::WGET_COMPLETED]"
        ("while" body line 16)
        invoked from within
    "while {1} {
    #  set CurrentConnections [regexp -all {bin/wget} [exec ps]]
        if {[CheckRunning 2 wget] == 0} {
            set FileName [lindex [file spli..."
        (file "/opt/local/bin/check.wget.tcl" line 91)
    что это может быть?
    Last edited by alex_b; 27-05-2007 at 18:41.

  6. #6
    Join Date
    Mar 2007
    Location
    Russia
    Posts
    170
    Quote Originally Posted by alex_b View Post
    почему-то после первой же скачки демон вываливается с ошибкой, приходится постоянно перезапускать
    Ага. Я не разбираюсь в tcl, но глюк пофиксил. Нужно добавить одну строчку в скрипт.
    Code:
             puts $f $CurrentURL
              close $f
              set CurrentURL ""
    Правда есть еще какие-то косяки со стартом/стопом. Буду разбираться.

  7. #7
    Join Date
    Mar 2007
    Location
    Russia
    Posts
    170
    И еще одно маленькое усовершенствование. Если добавить перед while{1} строки

    catch {open $PIDFILE w} f
    puts $f [pid]
    close $f

    то скрипт будет корректно останавливаться, если он был запущен с пустой очередью на закачку. Надо бы еще немного доработать скрипт запуска, чтобы он не ругался на отсутствующий pid файл.

  8. #8
    Было бы здорово еще файл со списком URL-ов и логи размещать не на винте - зачем его раскручивать каждые 30 сек? где бы их безопаснее разместить...

  9. #9
    Join Date
    Mar 2007
    Location
    Russia
    Posts
    170
    Quote Originally Posted by alex_b View Post
    Было бы здорово еще файл со списком URL-ов и логи размещать не на винте - зачем его раскручивать каждые 30 сек? где бы их безопаснее разместить...
    Можно в /tmp. В начале первого файла поменяй WGET_LIST на что-нибудь вроде set WGET_LIST [file join / tmp wget.list] Но помни, что при рестарте рутера этот файл будет потерян.

    P.S. Новая версия стартового скрипта. Изменения косметические, но все-таки...
    Attached Files Attached Files

  10. #10
    Quote Originally Posted by Megarem View Post
    Ага. Я не разбираюсь в tcl, но глюк пофиксил. Нужно добавить одну строчку в скрипт.
    Правда я не понял куда добавить...

  11. #11
    Join Date
    Mar 2007
    Location
    Russia
    Posts
    170
    В аттачменте находится последняя версия скрипта, модифицированная мной. Скопировать wget.tcl в /opt/bin, S20wget в /etc/init.d. Поправить при необходимости в первом файле пути к директории и файлам, где лежат закачки (у меня они отличаются от тех, что были в скрипте от al37919). Также я сделал небольшое усовершенствование для тех, что часто льет файлы с одних и тех же серверов, требующих авторизации. Поищите в скрипте строчку secret.com. Надеюсь понятно, что надо сделать?
    Attached Files Attached Files

  12. #12
    Join Date
    Feb 2007
    Location
    Moscow, Russia
    Posts
    3,805
    Приятно видеть, что кому-то этот мой опус оказался полезным. Спасибо за замечания и багфиксы.
    В коде первого поста добавил:
    1.
    PHP Code:
    catch {open $PIDFILE wf
    puts $f 
    [pid]
    close $f 
    2.
    PHP Code:
    set CurrentURL "" 
    3. автосоздание директорий partial и completed
    4. Обновил скрипт для запуска.

    Насчет раскрутки винта каждые 30 сек --- лично я считаю, что для винта здоровее не останавливаться совсем

    Здесь ( http://wl500g.info/showthread.php?t=9426 ) имеется набор cgi скриптов от венгерского коллеги для наблюдения за процессом закачки, добавления закачки, запуска/остановки демона через браузер. Может окажется кому-нибудь полезным.

  13. #13
    Спасибо за замечания и багфиксы.
    вам спасибо!
    Насчет раскрутки винта каждые 30 сек --- лично я считаю, что для винта здоровее не останавливаться совсем
    для винта может быть и здоровее, только громко работает, ночью не заснуть =)

  14. #14

    Как обсстоит дело с русскими ссылками.

    Здраствуйте. Мне тоже скрипт понравился, только с русскими буквами в ссылках работа не получается. То есть закачивается только первый файл. Я так понимаю что при переносе программа его не находит. Что можно сделать? Заранее спасибо.

  15. #15
    Join Date
    Feb 2007
    Location
    Moscow, Russia
    Posts
    3,805
    скорее всего дело в несоответствии кодировок.
    unix (а следовательно и роутер) в файловой системе сейчас использует UTF-8 (unicode), а винда по-прежнему 1251.

    Что бы решить проблему надо протестить следующее:

    1) через telnet/ssh разобраться в какой кодировке wget корректно грузит URL-ы (к сожалению, я в этой теории не подкован --- если никто не посоветует как правильно, значит надо экспериментировать).

    2) После этого надо научиться записывать имя файла в той кодировке, которую хочет видеть wget.

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

    Могу подкинуть две идеи (для решения второго вопроса, да и для тестирования первого могут оказаться полезными):

    Здесь http://wl500g.info/showthread.php?t=2376 Олег пишет как настроить самбу, чтобы она корректно отображала русские имена файлов, сохраненные на роутере в кодировке UTF-8 (причем только отображать, при копировании перекодировка не производится, хотя именно это может оказаться и полезным).

    Здесь http://wl500g.info/showpost.php?p=53426&postcount=7 приводится рецепт модификации vsftpd (в т.ч. готовый бинарник), чтобы он делал на лету перекодировку 1251-UTF8. Таким макаром можно корректно как просматривать содержимое роутера, так и копировать кириллифицированные файлы с винды на роутер и обратно.

    Для работы с файловой системой роутера очень удобно использовать FAR, т.к. в нем очень легко изменять кодовую страницу русского текста, а также можно иметь удаленный доступ по протоколам SMB, FTP, SSH (последнее с доп. плагином, например WinSCP).

Page 1 of 3 123 LastLast

Similar Threads

  1. Установка OpenVPN в основную память для НОВИЧКОВ
    By Mirage-net in forum Russian Discussion - РУССКИЙ (RU)
    Replies: 241
    Last Post: 24-05-2011, 20:48
  2. Веб интерфейс для wget
    By KOCTET in forum Russian Discussion - РУССКИЙ (RU)
    Replies: 12
    Last Post: 26-04-2009, 10:23

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •