Итак, я написал небольшое приложение под названием коровий язык.
Он выполняет различные задания в системе GNU/Linux. Но проблема заключается в приеме входных данных с использованием каналов.
Я уже реализовал прием перенаправления ввода-вывода с помощью этого кода:
#!/usr/bin/ruby -w
require 'timeout'
STDIN.sync = STDOUT.sync = true
pipe = false
begin
Timeout.timeout(0.000_000_000_001) do pipe = STDIN.gets end
rescue Timeout::Error
end
if pipe
print pipe
print pipe while pipe = STDIN.gets
end
Все в порядке.
Итак, для echo -e "hello\nworld" | ruby cowspeak
я получаю
hello
world
Но проблема в том, что он не работает с такими программами, как irb (да, мне не нужно использовать IRB с этой программой, но я хочу узнать, как это должно работать). Рабочий пример — драгоценный камень lolcat
.
Итак, для irb | ruby cowspeak
я заставляю его работать вечно в цикле while
(реализовано в цикле loop
на коровьем языке).
Lolcat отлично работает и в этом случае!
Кроме того, такие приложения, как смматрица, не работают с моей программой, но они работают с lolcat.
Unix-программы, такие как ковсей (написанная на Perl), также не очень хорошо работают с каналами - такое же поведение, как и у коровьего языка...
В любом случае, я вижу, что вопрос задан здесь раньше: ruby pipe, перенаправление ввода-вывода и stderr
Но это не отвечает на мой вопрос должным образом.
Как я могу реализовать перенаправление ввода-вывода с помощью Ruby и вообще, как гем lolcat действительно работает с каналами?
Почему бы вам просто не прочитать исходный код лолкота и не извлечь из этого урока? Всего около 300 строк кода.
@thatotherguy, нет, я знаю об isatty? метод. Моя проблема в том, что мое решение (часть кода, которую я написал выше) не работает с такими программами, как cmatrix, и даже с оболочкой irb.
@TeWu, я прочитал исходный код лолкота после того, как мне понадобилась эта функция. lolcat не использует ни одного файла. Все очень грязно. Lolcat использует краску для драгоценных камней, а краска считывает цвета из gz-файла. Я не хочу этого. Приложение, которое я написал, не должно содержать больше файлов, и оно должно иметь зависимость только от Ruby, оно должно загружаться как можно быстрее.
@ С.Госвами Я не понимаю. Вы знаете, что ваша попытка неверна, и вы также говорите, что знаете, как правильно это сделать. Что именно вы тогда спрашиваете? Вы спрашиваете, почему irb
надежно проигрывает вашу гонку, а echo
уверенно ее выигрывает?
На самом деле это echo
правильно выводит вывод. Но если вы запускаете такие программы, как cmatrix | lolcat
, он работает, irb | lolcat
работает (и лолкот не жалуется на TTY или подобные вещи). Однако я уже реализовал метод STDOUT.tty?
в первом коммите, который завершается, если tty не обнаружен — скажем, если вы запускаете программу в таких редакторах, как atom или vscode. В любом случае, мне интересно узнать, как я могу реализовать свою программу таким образом, чтобы она могла работать с другими программами?
Кроме того, посмотрите на irb | head -n5
, cmatrix | head -n5
они все работают. Я думаю, что моя реализация вообще неверна! Независимо от тайм-аута # тайм-аут, есть ли какой-нибудь драгоценный камень из стандартной библиотеки, который я могу использовать? На самом деле мне интересно, что может быть правильной реализацией около года!!
Чтобы правильно прочитать все данные из канала, читайте блок за блоком без тайм-аутов и убедитесь, что вы можете обрабатывать любые данные по мере их поступления:
#!/usr/bin/ruby -w
if STDIN.isatty
STDERR.puts "You are not piping, but I will read from stdin anyways"
STDERR.puts "because that is the canonical Unix behavior."
end
until STDIN.eof?
STDOUT.write(STDIN.readpartial(4096))
end
Это устраняет две проблемы с вашим опубликованным кодом (игнорируя все другие программы и комбинации, о которых вы спрашиваете):
echo
похоже работает, потому что это встроенная оболочка и поэтому, скорее всего, запустится до того, как ruby
закончит загрузку, но это все еще зависит от случая и расписания. Вот гонка, сфальсифицированная так, что echo
проигрывает:
$ ( sleep 1; echo Hello ) | ./yourprogram
./yourprogram:9: warning: constant ::TimeoutError is deprecated
Вы также ждете полные строки, но irb
выводит только подсказку без перевода строки, а cmatrix
вообще не выводит переводы строки. Это вызывает тайм-аут из-за вышеуказанной ошибки, и в противном случае просто считывает память, пока она не умрет:
$ while echo -n foo; do true; done | ./yourprogram
./yourprogram:9: warning: constant ::TimeoutError is deprecated
Предложение выше не имеет ни одной из этих проблем.
По какой-то странной причине я написал TimeoutError, а не Timeout::Error!! Кроме того, мы не можем использовать STDIN.getch, потому что это вызывает ошибку Inappropriate ioctl for device (Errno::ENOTTY)
. Помимо использования Timeout, ваш ответ правильный и очень полезный. Большое спасибо за ответ!
Извините, это может быть бесполезный комментарий, но я хотел бы сказать, что cowspeak должен проверить, происходит ли перенаправление ввода-вывода, если нет, он покажет случайную цитату. Итак, мне нужно использовать гем Timeout из стандартной библиотеки, чтобы убедиться, что он сначала проверяет строку с STDIN.gets
, и если строки нет, он не будет зацикливаться на ... until STDIN.eof?
.
Вам не нужен тайм-аут, чтобы проверить, происходит ли перенаправление. Это то, что isatty
делает надежно и надежно. Использование тайм-аута действительно плохо.
Проблема в том, что вы не знаете, как надежно определить, является ли стандартный ввод каналом? Проверьте этот ответ. Не используйте тайм-аут, это состояние гонки.