2

Мой скрипт пытается собрать информацию, которая может присутствовать или не присутствовать в STDIN во время выполнения, но cat зависает, если канал пуст. Как я могу гарантировать, что мой скрипт пропустит этот шаг, если в STDIN ничего нет?

stdin=$(cat <&0)

Обратите внимание, что я специально не ищу никаких решений, которые ссылаются на /dev /, так как я намерен использовать его в chroot независимо от того, был ли монтирован /dev /, если это возможно.

1 ответ1

3

Обычно при работе с трубами и стандартным вводом у истощенной трубы нет особого значения. Новые данные могут все еще появляться, пока не будет eof который закроет канал. Ваша cat заканчивается в eof как и ожидалось. Если бы не было никаких данных до eof только тогда вы можете сказать , что STDIN был действительно пуст.

Рассмотреть sender | receiver Нередко sender (намного) медленнее, чем receiver ; в этом случае stdin receiver почти всегда истощается, но вам вряд ли захочется уничтожить всю трубу из-за этого. Поэтому инструменты, которые выходят на "пустом" (истощенном, но еще не завершенном) стандартном вводе, являются скорее исключениями, чем стандартными.

В Bash есть read -t 0 (-t не требуется для POSIX). Из help read:

Если TIMEOUT равен 0 , read возвращается немедленно, без попытки считывания каких-либо данных, возвращая успех, только если входные данные доступны в указанном файловом дескрипторе.

По умолчанию read читает из stdin, поэтому состояние выхода read -t 0 сообщит вам, является ли stdin "пустым". Но будьте осторожны! Команда как

echo 1 | read -t 0

может завершиться успешно или нет, потому что echo и read выполняются одновременно, а не последовательно. Чтобы избежать этого, ваш скрипт должен sleep , прежде чем read -t 0 . В зависимости от того, откуда приходит stdin, "время" может быть относительно долгим. Сделайте что-то вроде этого:

sleep 1
if read -t 0; then …   # process stdin here, you know it's non-empty

Вы заполняете переменную данными, взятыми из стандартного ввода. Поскольку хранить двоичные данные в переменной не очень хорошая идея (прочитайте это), возможно, ваши данные - просто текст. Если это так, используйте read -t следующим образом:

read -r -t 5 -d $'\0' stdin

Нулевой символ (который вы не можете сохранить в переменной Bash в любом случае) в качестве разделителя (-d $'\0') позволит вам прочитать любой текст (например, с помощью новых строк) в переменной stdin . Через максимум 5 секунд (-t 5) команда завершается, что позволяет вашему сценарию продолжаться.

Другой подход с timeout . Базовый пример из моего Debian:

timeout --foreground 5 cat | wc -c

(Замените wc -c вашим кодом, который анализирует стандартный ввод; это всего лишь пример).

Это должно обрабатывать двоичные данные просто отлично. Если cat не получит eof то через 5 секунд она будет убита, поэтому wc все равно получит eof и линия не остановится. Проблема в том, что cat будет убита независимо от того, обрабатывает ли она какие-либо данные в данный момент. Я предполагаю, что вы хотите получить все данные, если они есть, даже если это занимает более 5 секунд. Улучшенная версия:

{ timeout --foreground 5 dd bs=1 count=1 2>/dev/null && cat; } | wc -c

Если первый байт появится в течение 5 секунд, сработает cat . Затем он будет обрабатывать любой дополнительный ввод до eof , независимо от того, сколько времени это займет. Все, включая первый байт (если есть), перейдет в wc . Если в течение 5 секунд не будет ни байта, ни eof , wc получит только eof ; линия не останавливается.

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .