Linux shell, pipes (конвейеры) и код завершения.
При работе с конвейерами (пайпами, "трубами", pipe), часто возникает вопрос, как отловить ошибку, произошедшую внутри конвейера.
Для примера возьмем код, где ход загрузки файла отображается dialog'овым прогрессбаром (копия):
#!/bin/bash
FADDR="http://tolik-punkoff.com/static/t est.mp3"
wget --progress=dot -O "./test.mp3" "$FADDR" 2>&1 |\
stdbuf -o0 awk '/[.] +[0-9][0-9]?[0-9]?%/ { print substr($0,63,3) }' | \
dialog --gauge "Download file from $FADDR" 10 100
Как видите, конвейер (т.е. передача вывода от одной команды другой через
|
), тут налицо.Если после данной команды просто дописать
echo $?
, то будет видно, что результат будет 0
.И когда все в порядке:
И когда все не в порядке (в данном случае, сделан обрыв связи):
На самом деле, это происходит потому, что в переменной
$?
оказывается код завершения последней команды в pipe.В современных версиях bash перед использованием контейнера, необходимо добавить команду:
set -o pipefail
В старых bash и некоторых других оболочках придется извращаться, может как-нибудь вернусь к вопросу (если для какого утюга что писать буду).
Примечание: Следует иметь в виду “безобидные” команды, которые могут вернуть не ноль.
В больших скриптах со сложными конструкциями и длинными конвеерами можно упустить этот момент из виду, что может привести к некорректным результатам.
После добавления вышеуказанной команды в скрипт делаем тест с обрывом связи:
Или указываем в переменной
$FADDR
некорректный адрес:Теперь
echo $?
отображает корректный код завершения.С учетом вышеизложенного, а так же с учетом знания о кодах завершения
wget
(копия) и о том, что wget
портит файлы в случае ошибки загрузки (копия) можно красиво модифицировать скрипт, чтоб он сообщал пользователю о результате после загрузки.#!/bin/bash
FADDR="http://tolik-punkoff.com/static/test.mp3"
FPATH="./test.mp3"
messagebox() #1 - title $2 - message
{
dialog --title "$1" --msgbox "$2" 10 41
}
set -o pipefail
wget --progress=dot --tries=10 --timeout=30 -O "$FPATH" "$FADDR" 2>&1 |\
stdbuf -o0 awk '/[.] +[0-9][0-9]?[0-9]?%/ { print substr($0,63,3) }' | \
dialog --gauge "Download file from $FADDR" 10 100
RETVAL=$?
case "$RETVAL" in
0)
messagebox "Complete!" "File downloaded!"
;;
1)
messagebox "Error" "Generic error ($RETVAL)"
;;
2)
messagebox "Error" "Command line or config error ($RETVAL)"
;;
3)
messagebox "Error" "I/O error ($RETVAL)"
;;
4)
messagebox "Error" "Network failure ($RETVAL)"
;;
5)
messagebox "Error" "SSL verification failure ($RETVAL)"
;;
6)
messagebox "Error" "Username/password authentication failure ($RETVAL)"
;;
7)
messagebox "Error" "Protocol error ($RETVAL)"
;;
8)
messagebox "Error" "Server error ($RETVAL)"
;;
*)
messagebox "Error" "Unknow error ($RETVAL)"
;;
esac
#remove wrong file if exist
if [ "$RETVAL" -ne 0 ];then
if [ -e "$FPATH" ];then
rm "$FPATH"
fi
fi
Все ОК:
Обрыв связи:
Неверный адрес:
Скрипт на GitHub
Linux pipes tips & tricks Копия в PDF
Это репост с сайта http://tolik-punkoff.com
Оригинал: http://tolik-punkoff.com/2020/02/20/linu