Толик Панков
hex_laden
............ .................. ................
October 2025
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31

Толик Панков [userpic]
Linux shell, pipes (конвейеры) и код завершения.

Преамбула


При работе с конвейерами (пайпами, "трубами", pipe), часто возникает вопрос, как отловить ошибку, произошедшую внутри конвейера.

Суть проблемы


Для примера возьмем код, где ход загрузки файла отображается dialog'овым прогрессбаром (копия):

#!/bin/bash

FADDR="http://tolik-punkoff.com/static/test.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/linux-shell-pipes-konvejery-i-kod-zaversheniya/

Tags: , ,