Эффективная параллельная загрузка и распаковка с соответствующим шаблоном для списка файлов на сервере

Каждый день каждые 6 часов мне приходится скачивать bz2 файлов с веб-сервера, распаковывать их и объединять в один файл. Это должно быть максимально эффективным и быстрым, поскольку мне нужно дождаться завершения фазы загрузки и распаковки, прежде чем продолжить слияние.

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

parallelized_extraction(){
    i=0
    until [ `ls -1 ${1}.bz2 2>/dev/null | wc -l ` -gt 0 -o $i -ge 30 ]; do
        ((i++))
        sleep 1
    done
    while [ `ls -1 ${1}.bz2 2>/dev/null | wc -l ` -gt 0 ]; do
        ls ${1}.bz2| parallel -j+0 bzip2 -d '{}' 
        sleep 1
    done
}
download_merge_2d_variable()
{
    filename="file_${year}${month}${day}${run}_*_${1}.grib2"
    wget -b -r -nH -np -nv -nd -A "${filename}.bz2" "url/${run}/${1,,}/"
    parallelized_extraction ${filename}
    # do the merging 
    rm ${filename}
} 

который я называю download_merge_2d_variable name_of_variable. Я смог ускорить код, написав функцию parallelized_extraction, которая занимается распаковкой загруженных файлов, пока wget работает в фоновом режиме. Для этого я сначала жду появления первого файла .bz2, затем запускаю распараллеленное извлечение до тех пор, пока на сервере не появится последний файл .bz2 (это то, что делают два цикла until и while).

Я очень доволен этим подходом, однако я думаю, что его можно улучшить. Вот мои вопросы:

  • как я могу запустить несколько экземпляров wget для выполнения параллельных загрузок, если мой список файлов указан как соответствующий шаблон? Должен ли я писать несколько шаблонов сопоставления с фрагментами данных внутри или мне обязательно нужно загружать список содержимого с сервера, разделять этот список и затем передавать его в качестве входных данных для wget?
  • parallelized_extraction может дать сбой, если загрузка файлов идет очень медленно, так как он не найдет новый файл bz2 для извлечения и выйдет из цикла на следующей итерации, хотя wget все еще работает в фоновом режиме. Хотя это никогда не случалось со мной, это возможность. Чтобы позаботиться об этом, я попытался добавить условие ко второму, запустив PID из wget в фоновом режиме, чтобы проверить, существует ли он, но почему-то он не работает.
parallelized_extraction(){
    # ...................
    # same as before ....
    # ...................
    while [ `ls -1 ${1}.bz2 2>/dev/null | wc -l ` -gt 0 -a kill -0 ${2} >/dev/null 2>&1 ]; do
        ls ${1}.bz2| parallel -j+0 bzip2 -d '{}' 
        sleep 1
    done
}
download_merge_2d_variable()
{
    filename="ifile_${year}${month}${day}${run}_*_${1}.grib2"
    wget -r -nH -np -nv -nd -A "${filename}.bz2" "url/${run}/${1,,}/" &
    # get ID of process running in background
    PROC_ID=$!
    parallelized_extraction ${filename} ${PROC_ID}
    # do the merging
    rm ${filename}
}

Любая подсказка, почему это не работает? Любые предложения о том, как улучшить мой код? Спасибо

ОБНОВЛЕНИЕ Я публикую здесь свое рабочее решение, основанное на принятом ответе, если кому-то интересно.

# Extract a plain list of URLs by using --spider option and filtering
# only URLs from the output 
listurls() {
    filename="$1"
    url="$2"
    wget --spider -r -nH -np -nv -nd --reject "index.html" --cut-dirs=3 \
        -A $filename.bz2 $url 2>&1\
        | grep -Eo '(http|https)://(.*).bz2'
}
# Extract each file by redirecting the stdout of wget to bzip2
# note that I get the filename from the URL directly with
# basename and by removing the bz2 extension at the end 
get_and_extract_one() {
  url="$1"
  file=`basename $url | sed 's/\.bz2//g'`
  wget -q -O - "$url" | bzip2 -dc > "$file"
}
export -f get_and_extract_one
# Here the main calling function 
download_merge_2d_variable()
{
    filename="filename.grib2"
    url="url/where/the/file/is/"
    listurls $filename $url | parallel get_and_extract_one {}
    # merging and processing
}
export -f download_merge_2d_variable_icon_globe

person Guido Cioni    schedule 02.10.2020    source источник
comment
Вероятно, сначала был испробован rsync? Затем можно попытаться использовать что-то вроде параллельного использования. IMHO rsync имеет некоторые технические особенности, которые вы частично создаете сами. Хотя я не сомневаюсь, что ваш сценарий требует некоторой разумной обработки, как и вы.   -  person Joop Eggen    schedule 02.10.2020


Ответы (1)


Можете ли вы указать URL-адреса для загрузки?

listurls() {
  # do something that lists the urls without downloading them
  # Possibly something like:
  # lynx -listonly -image_links -dump "$starturl"
  # or
  # wget --spider -r -nH -np -nv -nd -A "${filename}.bz2" "url/${run}/${1,,}/"
  # or
  # seq 100 | parallel echo ${url}${year}${month}${day}${run}_{}_${id}.grib2
}

get_and_extract_one() {
  url="$1"
  file="$2"
  wget -O - "$url" | bzip2 -dc > "$file"
}
export -f get_and_extract_one

# {=s:/:_:g; =} will generate a file name from the URL with / replaced by _
# You probably want something nicer.
# Possibly just {/.}
listurls | parallel get_and_extract_one {} '{=s:/:_:g; =}'

Таким образом, вы будете распаковывать во время загрузки и делать все параллельно.

person Ole Tange    schedule 02.10.2020
comment
да, это хорошая идея. Я попытался найти, как можно было получить список файлов непосредственно в wget, но не смог найти хорошего решения. Я собираюсь попробовать этот подход позже и предоставлю MWE, чтобы показать, работает ли он. - person Guido Cioni; 02.10.2020
comment
P.S. Я думаю, что аргумент file для get_and_extract_one не нужен, так как если я его не укажу, wget будет использовать имя файла, а bzip2 по умолчанию будет использовать то же самое без bz2 в конце... если я не ошибаюсь. Прямо сейчас я не указываю имя файла для вывода, и все работает хорошо. - person Guido Cioni; 02.10.2020
comment
@GuidoCioni Требуется аргумент файла: wget -O - отправляет вывод на стандартный вывод, и мы используем его для распаковки во время загрузки. Другими словами: bzip2 начинает распаковку до полного извлечения файла. - person Ole Tange; 02.10.2020
comment
Спасибо @Ole за предложение. Я сделал обновление в основном вопросе, чтобы показать, что я использовал ваше предложение для разработки моего рабочего решения;) Не стесняйтесь комментировать - person Guido Cioni; 03.10.2020
comment
Странно: если я вызываю функцию download_merge_3d_variable только для 1 переменной за раз, она работает... но как только я пытаюсь распараллелить ее, выполняя variables=("V" "FI"); parallel -j ${N_CONCUR_PROCESSES} download_merge_3d_variable ::: "${variables[@]}", что-то идет не так: данные вообще не загружаются для одной переменной и для остальные данные неполные. Это связано с передачей данных на стандартный вывод? Для переменной, которая вместо этого загружает 2D-данные, этого не происходит, поэтому мне интересно, имеет ли это какое-то отношение к чрезмерному количеству файлов, возвращаемых listurls - person Guido Cioni; 03.10.2020
comment
Хорошо, я понял это. Сервер, вероятно, убивал меня из-за слишком большого количества запросов с одного и того же IP-адреса. Я решил это, поставив задержку на создание нескольких заданий, то есть parallel --delay 2. - person Guido Cioni; 03.10.2020