Вопрос или проблема
Я бы хотел понять, выполнилась ли сторонняя программа успешно и записала ли что-то в stdout, или ничего не записала, что происходит в случае возникновения ошибки. К сожалению, программа всегда возвращает статус выхода 0 и не выводит сообщения об ошибках. Проверка также не должна изменять или скрывать то, что записывается в stdout программой.
Если вывод программы короткий, возможно использовать подстановку команд, чтобы захватить его, сохранить в переменной окружения и снова вывести:
output="$(external_program)"
printf %s "$output"
if [ -z "$output" ]; then
# ошибка: сторонняя программа ничего не записала
fi
Возможные препятствия:
- Зависит от размера вывода, см. Установка длинной переменной окружения ломает много команд
- Возможно, изменяет вывод, особенно удаляет новые строки, см. Почему символы новой строки теряются при использовании подстановки команд?
- Если вывод программы – это двоичные данные, не стоит сохранять их в переменной окружения (?)
Еще один вариант – записать вывод во временный файл или канал. Это можно обернуть в функцию, которая сообщает результат через статус выхода:
output_exist() (
temporary_file="$(mktemp)" || exit
trap 'rm "$temporary_file"' EXIT
tee "$temporary_file"
test -s "$temporary_file"
)
if ! external_program | output_exist; then
# ошибка: сторонняя программа ничего не записала
fi
Возможные препятствия:
- Временный файл или канал, за которыми нужно следить, обеспечивать очистку и т.д., удобство против портативности, например, POSIX имеет только
mktemp(3)
, но неmktemp(1)
- Очень большой вывод может привести к проблемам с ресурсами/производительностью
Какие есть альтернативы? Есть ли более простое решение?
Вы можете сделать что-то вроде:
if
cmd | {
first_byte=$(dd bs=1 count=1 2> /dev/null | od -An -vto1)
[ -n "$first_byte" ] && printf "\\$(( $first_byte ))" && cat
}
then
echo non-empty
fi
Если вы можете установить дополнительное программное обеспечение, то можно использовать ifne
из moreutils
:
if cmd | ifne -n false; then
echo non-empty
fi
По поводу ваших ограничений:
output=$(cmd)
устанавливает переменную оболочки и я не знаю ни одной реализации sh
, в которой есть ограничение на длину ее переменных. Это становится переменной окружения только если вы экспортируете ее и выполняете команду, и тогда вы можете столкнуться с лимитом системы на размер argv[]
+ envp[]
, передаваемых в execve()
Этот же лимит будет применяться для argv[]
в printf %s "$output"
в тех реализациях sh
, где printf
не встроен (например, ksh88 или некоторые оболочки, производные от pdksh).
$(...)
удаляет заключительные новые строки и в большинстве реализаций либо задыхает на нулях, либо удаляет их. yash
задыхает на выводах, которые не могут быть декодированы как текст в локали.
Как видно из Как создать временный файл в shell скрипте?, в POSIX есть CLI API для mkstemp()
в m4
:
tmpfile=$(
echo 'mkstemp(template)' |
m4 -D template="${TMPDIR:-/tmp}/baseXXXXXX"
) || exit
Хотя системы без m4
не редкость (хотя это неотъемлемая утилита POSIX).
Это может быть не лучший вариант для жестких требований POSIX, скорее идея, как справиться с проблемой размера вывода / временного файла:
$ exec 3>&1
$ output="$( echo foo | tee /dev/fd/3 | wc -c )"
foo
$ echo "$output"
4
$ test "$output" -eq 0 && ...
Ответ или решение
Чтобы решить задачу определения, записал ли внешний программный модуль что-либо в стандартный поток вывода (stdout), важно учесть несколько аспектов, таких как ограничения на размер вывода и потенциал изменения данных при передаче через промежуточные механизмы. Обсудим несколько возможных подходов:
Теория
Запись программы в stdout — это стандартный метод вывода данных в UNIX-подобных системах, который не всегда сопровождается осмысленным кодом завершения или выводом в стандартный поток ошибок (stderr). Коды завершения, как правило, сигнализируют об успешности выполнения программы, но не обеспечивают полной информации о количестве или наличии данных в stdout. Проблема усложняется ограничениями, связанными с переменными окружения и обработкой текстовых данных системными утилитами.
Примеры
-
Командное подстановление (
command substitution
):output="$(external_program)" printf %s "$output" if [ -z "$output" ]; then echo "Ошибка: внешняя программа не произвела вывод" fi
Проблемы: Ограничения на размер переменной окружения и потенциальная потеря новых строк.
-
Использование временного файла:
output_exists() ( temporary_file="$(mktemp)" || exit trap 'rm -f "$temporary_file"' EXIT tee "$temporary_file" | cat test -s "$temporary_file" ) if ! external_program | output_exists; then echo "Ошибка: внешняя программа не произвела вывод" fi
Проблемы: Создание временных файлов требует надлежащего управления их удалением, особенно в условиях ограниченной доступности утилит, таких как
mktemp
. -
Использование
dd
иod
для чтения данных по байтам:if cmd | { first_byte=$(dd bs=1 count=1 2> /dev/null | od -An -vto1) [ -n "$first_byte" ] && printf "\\%o" "$first_byte" && cat }; then echo "Внешняя программа произвела вывод" fi
Проблемы: Более сложный синтаксис и потенциальные проблемы с производительностью.
-
Использование инструмента
ifne
из пакетаmoreutils
:if cmd | ifne -n false; then echo "Внешняя программа произвела вывод" fi
Проблемы: Зависимость от наличия дополнительного ПО.
Применение
При выборе подхода важно учитывать требования к платформенной независимости, специфике данных (например, наличие бинарных данных), а также ограничения на размер вывода. Например, для больших объёмов вывода или если вывод включает бинарные данные, использование временных файлов может быть предпочтительным, несмотря на необходимость их управления. Однако, если платформенная независимость или минимальное использование сторонних утилит являются приоритетом, стоит рассмотреть подходы с командной подстановкой или манипуляциями с dd
и od
.
Также важно учитывать, что использование временных файлов или средств вроде ifne
может потребовать дополнительной работы для обеспечения, что временные файлы правильно удаляются в случае ошибок или прерываний, и что в системе доступны необходимые инструменты.
Таким образом, лучший выбор подхода будет зависеть от конкретных условий использования, технических ограничений и требований к кросс-платформенности. В практике, при планировании и реализации таких решений, стоит также рассмотреть возможное логирование или оповещение о состоянии внешнего программного обеспечения для облегчения отладки и мониторинга.