Кошка зависает при попытке прочитать пустой STDIN

329
StarCrashr

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

stdin=$(cat <&0) 

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

2
Я был бы удивлен, если бы chroot без _atimum_ / dev / null был применим в производстве. В любом случае, между "/ dev mount" и "/ dev, созданным вручную с минимальным расстоянием", есть большой путь ... grawity 5 лет назад 0
Никакое решение, кроме `cat -` или даже простого` cat`, не сделало бы то же самое, что ваш `cat <& 0`. xenoid 5 лет назад 0
@xenoid да, я знаю. Я попробовал все три, и все они дают идентичные результаты. <& 0 - это всего лишь остаток от игры вокруг, чтобы посмотреть, смогу ли я сделать ошибку с кошкой, а не ждать. StarCrashr 5 лет назад 0
Кстати, я продолжал играть с ним, ожидая ответа, и обнаружил, что это не просто зависание. Он искал ввод с консоли. Это привело к экспериментам с heredocs, чтобы попытаться завершить работу cat, если у нее нет входных данных, но они получили ответ, прежде чем уйти с этим. StarCrashr 5 лет назад 0

1 ответ на вопрос

3
Kamil Maciorowski

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

Посмотрим sender | receiver. Это не редкость sender(намного) медленнее, чем receiver; в этом случае стандартный 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. Если нет ни байта, ни eofчерез 5 секунд, wcполучит просто eof; линия не останавливается.

Мой вклад действительно текст, на случай, если кому-то интересно. Это массив информации о пользователе и / или хосте, разделенный двоеточиями, который может содержать или не содержать пароль, поэтому дополнительный stdin важен для моего использования (пароли не разрешены в командной строке). Если в stdin нет данных, сценарий пытается использовать методы аутентификации без пароля и ищет остальную информацию в первом аргументе. StarCrashr 5 лет назад 0

Похожие вопросы