Я передал строку в скрипте bash и хочу проверить, есть ли в канале данные, прежде чем передавать их в программу.
Поиск, который я нашел, test -t 0
но он не работает здесь. Всегда возвращает ложь. Так как быть уверенным, что в трубе есть данные?
Пример:
echo "string" | [ -t 0 ] && echo "empty" || echo "fill"
Выход: fill
echo "string" | tail -n+2 | [ -t 0 ] && echo "empty" || echo "fill"
Выход: fill
В отличие от стандартного / канонического способа проверки того, дает ли предыдущий конвейер выходной сигнал? вход должен быть сохранен, чтобы передать его программе. Это обобщает, Как направить вывод от одного процесса к другому, но выполнить, только если первый имеет вывод? который фокусируется на отправке электронной почты.
Ответы:
Нет возможности посмотреть содержимое канала с помощью общедоступных утилит оболочки, а также нет способа прочитать символ в канал и затем вернуть его обратно. Единственный способ узнать, что канал имеет данные, - это прочитать байт, а затем вы должны доставить этот байт к месту назначения.
Так что просто сделайте это: прочитайте один байт; если вы обнаружите конец файла, то делайте то, что хотите, когда ввод пуст; если вы действительно читаете байт, тогда выполните форк то, что вы хотите сделать, когда ввод не пустой, направьте в него этот байт и передайте остальные данные.
ifne
Утилита от moreutils Джои Гесса запускает команду , если ее ввод не пуст. Обычно он не устанавливается по умолчанию, но он должен быть доступен или прост в сборке на большинстве вариантов Unix. Если вход пуст,ifne
ничего не делает и возвращает статус 0, который нельзя отличить от успешно выполненной команды. Если вы хотите что-то сделать, если ввод пуст, вам нужно сделать так, чтобы команда не возвращала 0, что можно сделать, если в успешном случае вернуть различимый статус ошибки:test -t 0
не имеет к этому никакого отношения; он проверяет, является ли стандартный ввод терминалом. Это не говорит ничего так или иначе относительно того, доступен ли какой-либо вход.источник
peek
утилиту, которая могла бы возвращать реальные данные из канала, а не только их количество. (в 4.4 BSD каналы 386BSD и т. д. были реализованы в виде пар сокетов , но это было выпотрошено в более поздних версиях * BSD - хотя они оставляли их двунаправленными).read -t 0
( a в этом случае означает тайм-аут, если вам интересно).Простым решением является использование
ifne
команды (если ввод не пустой). В некоторых дистрибутивах он не установлен по умолчанию. Это часть пакетаmoreutils
в большинстве дистрибутивов.ifne
запускает данную команду тогда и только тогда, когда стандартный ввод не пустОбратите внимание, что если стандартный ввод не пуст, он передается
ifne
данной командеисточник
Старый вопрос, но в случае, если кто-то сталкивается с ним, как я: мое решение - читать с таймаутом.
Если
stdin
пусто, это вернется через 5 секунд. В противном случае он будет читать все входные данные, и вы можете обрабатывать их по мере необходимости.источник
-t
сожалению, она не является частью POSIX: pubs.opengroup.org/onlinepubs/9699919799/utilities/read.htmlпроверить, открыт ли файл-дескриптор stdin (0):
источник
[ -t 0 ]
проверяет, открыт ли fd 0 для tty , а не закрыт или открыт../that_script </dev/null
=> "У stdin есть данные". Или./that_script <&-
чтобы действительно был закрыт stdin .Вы также можете использовать
test -s /dev/stdin
(в явной форме).источник
В Баш:
Обнаруживает, есть ли на входе данные (без чтения чего-либо). Затем вы можете прочитать ввод (если он доступен во время выполнения чтения):
Примечание: поймите, что это зависит от времени. Это обнаруживает, если вход уже имеет данные только во время выполнения
read -t
.Например, с
выход
1
(ошибка чтения, т. е. пустой вход). Эхо записывает некоторые данные, но не очень быстро запускается и записывает свой первый байт, поэтомуread -t 0
сообщит, что его ввод пуст, поскольку программа еще ничего не записала.источник
bash
будет либо a,select()
либо anioctl(FIONREAD)
, либо ни один из них, но не оба, как это должно работать для него.read -t0
сломан. Не используйте егоОдин простой способ проверить, доступны ли данные для чтения в Unix, с помощью
FIONREAD
ioctl.Я не могу представить себе, чтобы какая-либо стандартная утилита делала именно это, поэтому вот тривиальная программа, которая делает это (лучше, чем
ifne
из moreutils IMHO ;-)).Если на stdin нет данных, он выйдет со статусом 1. Если есть данные, он запустится
prog
. Если нетprog
, он выйдет со статусом 0.Вы можете удалить
poll
звонок, если вас интересуют только данные, доступные сразу . Это должно работать с большинством типов fds, а не только с каналами.fionread.c
источник
POLLHUP
события, чтобы обработать пустой случай? Это работает, если есть несколько описаний файлов на другом конце канала?Если вам нравятся короткие и загадочные строки:
Я использовал примеры из оригинального вопроса. Если вы не хотите использовать данные по каналу
-q
параметр с помощью grepисточник
Кажется, это разумная реализация ifne в bash, если вы хорошо читаете всю первую строку
источник
read
также вернет false, если ввод не пустой, но не содержит символа новой строки,read
выполняет некоторую обработку своего ввода и может прочитать более одной строки, если вы не вызовете его какIFS= read -r line
.echo
не может быть использован для произвольных данных.Это работает для меня, используя
read -rt 0
пример из исходного вопроса, без данных:
источник
{ sleep .1; echo yes; } | { read -rt0 || echo NO; cat; }
(ложно отрицательный) иtrue | { sleep .1; read -rt0 && echo YES; }
(ложно положительный). На самом деле, в Bashread
будет обмануть даже FDS , открытый в записи-только режим:{ read -rt0 && echo YES; cat; } 0>/tmp/foo
. Единственное, что он, кажется, делает, этоselect(2)
на этом FD.select
вернет fd как "готовый", если aread(2)
на нем не заблокируется, независимо от того, вернется ли онEOF
или возникнет ошибка. Вывод:read -t0
это сломано вbash
. Не используйте это.{ sleep .1; echo yes; } | { read -rt0 || echo NO; cat; }
не является ложноотрицательным, потому что (во время чтения) нет ввода. Позже (сон 0,1) , что вход в наличии (для кошки).echo "" | { read -t0 && echo YES; }
печатает ДА, ноecho "" | { read -rt0 && echo YES; }
не печатает .