Вопрос или проблема
Как прочитать ${PIPESTATUS[0]}
команды, когда переменная установлена в текущей оболочке? Есть ли способ передать ее в текущую оболочку каким-то образом?
Я устанавливаю переменную MAIL=$(ldapsearch.... | find_for_mail)
и хочу быть уверенным, что ldapsearch
не завершился с ошибкой, прежде чем я решу, что атрибут mail действительно не существует в LDAP.
$ true | false
$ echo ${PIPESTATUS[0]} ${PIPESTATUS[1]}
0 1
$ Z=$(true | false)
$ echo ${PIPESTATUS[0]}
1 <--- почему 1?
$ echo ${PIPESTATUS[1]}
$ Z=$(true | false;echo ${PIPESTATUS[0]} ${PIPESTATUS[1]})
$ echo $Z
0 1
$ Z=$(true | false;C="${PIPESTATUS[0]} ${PIPESTATUS[1]}")
$ echo $C
$ Z=$(true | false;export C="${PIPESTATUS[0]} ${PIPESTATUS[1]}")
$ echo $C
Проблема в том, что export
экспортирует переменные как переменные окружения от родительского процесса к дочернему, а не наоборот. Если вас интересует только один из кодов завершения, вы можете использовать следующее:
$ Z=$(false | true; exit ${PIPESTATUS[0]})
$ echo $?
1
Один из способов сделать это в Linux – передавать данные с помощью временного файла (<<<
использует временный файл).
{
read -u3
output=$(cmd1 | cmd2 | cmd3; printf '%s\n' "${PIPESTATUS[@]}" > /dev/fd/3)
readarray -t cmdsubst_pipe_status <&3
} 3<<< ''
printf '%s\n' "статус cmd1: ${cmdsubst_pipe_status[0]}"
Редактирование 2024 года. Такой трюк перестал работать в bash 5.0, где bash обновил разрешения временного файла, используемого для heredoc/herestrings, так что он стал недоступен для записи, что можно было исправить, восстановив их с помощью chmod u+w /dev/fd/3
. В bash 5.1 временные файлы были заменены на каналы для коротких heredoc/herestrings. Таким образом, вышеуказанное снова будет работать (хотя мне пришлось добавить read -u3
, чтобы игнорировать начальную пустую строку) на Linux (где /dev/fd/x
в канале ведет себя как именованный канал), пока printf '%s\n' "${PIPESTATUS[@]}"
помещается в буфер канала (по умолчанию 64KiB в Linux, что должно быть вполне достаточно).
Ответ или решение
Для того чтобы правильно обработать результат выполнения команд в конвейере и получить статус их завершения, необходимо учесть, что переменные окружения не передаются из дочерних процессов обратно в родительский. Это ограничение оставляет нас в ситуации, когда мы не можем просто использовать ${PIPESTATUS[0]}
после выполнения команды в конструкции $()
.
Вот несколько способов решения вашей задачи:
- Использование временного файла или канала: Как упоминалось в вашем вопросе, можно использовать временный файл или канал, чтобы передать статус завершения команд из дочернего процесса в родительский.
Пример с использованием канала:
{
read -u3
output=$(ldapsearch ... | find_for_mail; printf '%s\n' "${PIPESTATUS[@]}" >&3)
readarray -t cmdsubst_pipe_status <&3
} 3<<< ''
printf 'Статус ldapsearch: %s\n' "${cmdsubst_pipe_status[0]}"
В этом примере мы используем файловый дескриптор 3
для того, чтобы записать статусы завершения на отдельный канал, а затем читаем их обратно в массив cmdsubst_pipe_status
.
- Прямое чтение статуса после выполнения команд в конвейере: Если нужно просто получить статус первой команды в конвейере, можно обернуть команды в фигурные скобки и использовать
exit ${PIPESTATUS[0]}
:
Z=$(ldapsearch ... | find_for_mail; exit ${PIPESTATUS[0]})
if [ $? -eq 0 ]; then
echo "ldapsearch выполнен успешно."
else
echo "Ошибка в ldapsearch."
fi
Тем не менее, так как в некоторых ситуациях вы можете не захотеть прерывать выполнение скрипта, рассматриваете вариант с временным файлом или аналогом.
- Использование комбинации
trap
и временного файла: Также удобно использовать командуtrap
, чтобы передать ошибки:
temp_file=$(mktemp)
trap 'rm -f "$temp_file"' EXIT
{
ldapsearch ... | find_for_mail; echo ${PIPESTATUS[0]} > "$temp_file"
}
wait $!
status="$(<"$temp_file")"
printf 'Статус ldapsearch: %s\n' "$status"
Эти методы обеспечат передачу статусов выполнения команд из конвейера и нивелируют ограничения, связанные с зависимостями между родительскими и дочерними процессами.
Учитывая возможности вашего окружения, выберите наиболее подходящий метод для сбора статусов выполнения и обработки ошибок в скриптах.