bash — возвращает статус из фоновой функции.

У меня есть скрипт bash примерно со следующей структурой:

function download {
    # download a big file
}

function prepare_stuff {
    # prepare some stuff
}

function process_download {
    # process the downloaded file
}

download & prepare_stuff & wait
process_download

Первое, что он делает, это загружает файл размером в несколько сотен мегабайт. Пока идет загрузка, в фоновом режиме готовятся некоторые другие вещи. Когда оба из них завершены, загрузка обрабатывается.

download может закончиться тремя разными способами:

  1. Загрузка не удалась (например, сервер недоступен)
  2. Файл был успешно загружен
  3. Файл не изменился на сервере с момента последней загрузки

Случай 1 является состоянием ошибки (в этом случае функция должна возвращать что-то отличное от нуля), а 2 и 3 - нет (т.е. возвращаемое значение должно быть равно нулю).

Теперь я хочу, чтобы process_download пропустил фактическую обработку при обнаружении случая 1 или 3, поэтому мне нужно передать какой-то статус обратно из download. Поскольку download работает в подоболочке, переменная не будет работать (назначения выполняются в подоболочке и не передаются обратно в родительскую оболочку).

Как я могу передать какое-то значение из функции в подоболочке обратно в родительскую оболочку?


person user149408    schedule 27.01.2017    source источник
comment
Все сводится к инструментам, используемым в функции, а точнее к коду выхода последней команды, выполненной в функции.   -  person Inian    schedule 27.01.2017
comment
У вас уже есть способ проверить состояние 3 или вы ищете отзывы об этом?   -  person Fred    schedule 27.01.2017
comment
@Fred Функция загрузки может определить все три условия.   -  person user149408    schedule 27.01.2017
comment
Таким образом, вам нужно только проверить ненулевой код возврата из загрузки для обоих условий, которые потребуют обхода окончательной обработки?   -  person Fred    schedule 27.01.2017
comment
Не так, как я изначально планировал (неизмененный файл вернул бы ноль, как и успешная загрузка, а неудачная загрузка вернула бы код ошибки), но в итоге я так и сделал.   -  person user149408    schedule 27.01.2017


Ответы (2)


Ты можешь это сделать :

download &
download_pid=$!
prepare_stuff &
prepare_pid=$!
result=0
wait $download_pid || result=$?
wait $prepare_pid

Затем result будет содержать код возврата вашей предыдущей команды загрузки, и оба фоновых задания будут завершены, и вы можете сделать что-то вроде:

[[ $result = 0 ]] || process_download

С разъяснениями относительно вашего третьего условия я мог бы сделать ответ более полным.

person Fred    schedule 27.01.2017
comment
Я знаю, я сначала неправильно понял вопрос. Я обновил свой ответ. При этом мне неясно, хочет ли ОП предложение проверить условие 3 или нет, поэтому мое обновленное предложение не обрабатывает это на данный момент. - person Fred; 27.01.2017
comment
@codeforester Так и есть. Функция prepare_stuff вызывается во время загрузки в фоновом режиме. Команды wait, вызывающие блокировку скрипта, возникают, когда обе функции выполняются параллельно. - person Fred; 27.01.2017
comment
Может ли быть ситуация, когда процесс загрузки завершается даже раньше, чем мы можем его дождаться? В таком случае wait выйдет из строя из-за неправильного pid. Итак, кажется, что мы должны запускать загрузку в подоболочке. Я удалил свой ответ, потому что $! не был установлен после { }, возможно, из-за &&. - person codeforester; 27.01.2017
comment
@codeforester Это сработает. Статус процессов остается доступным после их завершения. Попробуйте true & { sleep 2 ; wait $! ; } увидеть его в действии в тривиальном случае. - person Fred; 27.01.2017
comment
Только что проверил это: я смоделировал время выполнения с помощью sleep и вставил еще более длинный sleep перед первым wait — работает так же хорошо, даже если функция завершилась к тому времени, когда мы доберемся до wait для нее. - person user149408; 27.01.2017
comment
Если бы это не сработало, честно говоря, wait был бы практически бесполезен из-за отсутствия надежности. - person Fred; 27.01.2017

В итоге я сделал что-то похожее на то, что предложил Фред.

Прежде всего, я отказался от идеи «файл не изменен на сервере = нет ошибки» и теперь использую код возврата просто для того, чтобы указать, есть ли у нас загрузка для обработки — 0, если был загружен новый файл, 1, если загрузка не удалась или изменений не было. Так намного проще обращаться.

Затем:

function download {
    # download a big file
    # return nonzero if download failed
    # else return nonzero if file has not changed
    # else return 0 (download successful, file has changed)
}

function prepare_stuff {
    # prepare some stuff
}

function process_download {
    # process the downloaded file
}

download &
PID_DOWNLOAD=$!
prepare_stuff &
PID_PREPARE_STUFF=$!
wait $PID_PREPARE_STUFF
wait $PID_DOWNLOAD && process_download || echo "Nothing to process."
person user149408    schedule 27.01.2017