Общие комментарии о том, когда использовать errno
Нет стандартной C или библиотечная функция POSIX никогда не устанавливает errno
в ноль. Печать сообщения об ошибке на основе errno
, когда fd
не равен NULL, не подходит; номер ошибки не из popen()
(или не установлен, потому что popen()
не удалось). Печать res
после pclose()
в порядке; добавление strerror(errno)
приводит к той же проблеме (информация в errno
может быть совершенно неактуальной). Вы можете установить errno
на ноль перед вызовом функции. Если функция возвращает индикацию ошибки, может быть уместно взглянуть на errno
(посмотрите спецификацию функции - определено ли для установки errno
при ошибке?). Тем не менее, errno
может быть установлен ненулевым с помощью функции, даже если это удастся. Стандартные операции ввода-вывода Solaris используются для установки errno = ENOTTY
, если выходной поток не был подключен к терминалу, даже если операция прошла успешно; это вероятно все еще делает. И установка Solaris errno
даже на успех вполне законна; Правильно смотреть на errno
, только если (1) функция сообщает о сбое и (2) задокументирована функция установки errno
(POSIX или руководством системы).
См. C11 §7.5 Ошибки <errno.h>
¶3 :
Значение errno в начальном потоке равно нулю при запуске программы (начальное значение errno в других потоках является неопределенным значением) , но никогда не устанавливается в ноль какой-либо библиотечной функцией. 202) Значение errno может быть установлено в ненулевое значение при вызове библиотечной функции независимо от того, есть ли ошибка, если использование errno не задокументировано в описании функции в этом международном стандарте.
202) Таким образом, программа, использующая errno
для проверки ошибок, должна установить ее на ноль перед вызовом библиотечной функции, а затем проверить это перед последующим вызовом библиотечной функции. Конечно, библиотечная функция может сохранить значение errno
при вводе, а затем установить его на ноль, если исходное значение восстанавливается, если значение errno
все еще равно нулю непосредственно перед возвратом.
POSIX аналогично (errno
):
Многие функции предоставляют номер ошибки в errno
, который имеет тип int
и определяется в <errno.h>
. Значение errno
должно быть определено только после вызова функции, для которой оно явно указано для установки, и до тех пор, пока оно не будет изменено при следующем вызове функции или если приложение присвоит ему значение. Значение errno
следует проверять только в том случае, если оно указано допустимым возвращаемым значением функции. Заявки должны получить определение errno
путем включения <errno.h>
. Ни одна функция в этом томе POSIX.1-2017 не должна устанавливать значение errno равное 0. Установка errno
после успешного вызова функции не определена, если в описании этой функции не указано, что errno
не должно изменяться.
popen()
и pclose()
Спецификация POSIX для popen()
не очень полезна. Есть только одно обстоятельство, при котором popen()
должен потерпеть неудачу; все остальное «может потерпеть неудачу».
Однако подробности для pclose()
гораздо более полезны, включая:
Если интерпретатор языка команд не может Для выполнения статуса завершения дочернего процесса, возвращаемого pclose()
, должно быть, как если бы интерпретатор командного языка завершал работу, используя exit(127)
или _exit(127)
.
и
По при успешном возврате pclose()
возвращает статус завершения интерпретатора командного языка. В противном случае pclose()
должен вернуть -1 и установить errno для указания ошибки.
Это означает, что pclose()
возвращает значение, полученное от waitpid()
- состояние выхода из команды, которая была вызывается. Обратите внимание, что он должен использовать waitpid()
(или эквивалентно избирательную функцию - поиск для wait3()
и wait4()
в системах BSD); он не авторизован для ожидания других дочерних процессов, кроме того, который создан popen()
для этого файлового потока. Существуют предписания о том, что pclose()
должен быть уверен, что ребенок вышел, даже если какая-то другая функция ожидала мертвого ребенка в промежуточный период и тем самым вызвала потерю системой статуса для ребенка, созданного popen()
.
Если вы интерпретируете десятичное число 32512 как шестнадцатеричное, вы получите 0x7F00. И если вы использовали макросы WIFEXITED
и WEXITSTATUS
из <sys/wait.h>
для этого, вы обнаружите, что статус выхода равен 127
(потому что 0x7F
является десятичным числом 127
, а статус выхода закодирован в старшие биты состояния, возвращаемые waitpid()
.
int res = pclose(fd);
if (WIFEXITED(res))
printf("Command exited with status %d (0x%.4X)\n", WEXITSTATUS(res), res);
else if (WIFSIGNALED(res))
printf("Command exited from signal %d (0x%.4X)\n", WTERMSIG(res), res);
else
printf("Command exited with unrecognized status 0x%.4X\n", res);
И помните, что 0
- это состояние выхода, указывающее успех; все остальное обычно указывает на ошибку какого-либо рода. Вы можете дополнительно проанализировать статус выхода для поиска 127
или ретранслированных сигналов и т. д. c. Маловероятно, что вы получите статус «сигнализированный» или нераспознанный статус.
popen()
сказал вам, что ребенок потерпел неудачу .
Конечно, возможно, что выполненная команда фактически вышла из себя со статусом 127, это неизбежно сбивает с толку, и единственный способ обойти это - избежать состояний выхода в диапазоне от 126 до 128 + 'максимальное число сигналов. '(что может означать 126 .. 191 при наличии 63 распознанных сигналов.) Значение 126
также используется POSIX для сообщения, когда отсутствует интерпретатор, указанный в шебанге (#!/usr/bin/interpreter
) ( в отличие от выполняемой программы, недоступной). Будет ли это возвращено pclose()
- это отдельное обсуждение. И передача сигналов осуществляется оболочкой, потому что нет другого (простого) способа сообщить, что ребенок умер от сигнала в противном случае.