Перенаправление ввода-вывода с помощью Ruby в GNU/Linux

Итак, я написал небольшое приложение под названием коровий язык.

Он выполняет различные задания в системе 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 действительно работает с каналами?

Проблема в том, что вы не знаете, как надежно определить, является ли стандартный ввод каналом? Проверьте этот ответ. Не используйте тайм-аут, это состояние гонки.

that other guy 28.05.2019 21:59

Почему бы вам просто не прочитать исходный код лолкота и не извлечь из этого урока? Всего около 300 строк кода.

TeWu 28.05.2019 22:00

@thatotherguy, нет, я знаю об isatty? метод. Моя проблема в том, что мое решение (часть кода, которую я написал выше) не работает с такими программами, как cmatrix, и даже с оболочкой irb.

S.Goswami 28.05.2019 22:03

@TeWu, я прочитал исходный код лолкота после того, как мне понадобилась эта функция. lolcat не использует ни одного файла. Все очень грязно. Lolcat использует краску для драгоценных камней, а краска считывает цвета из gz-файла. Я не хочу этого. Приложение, которое я написал, не должно содержать больше файлов, и оно должно иметь зависимость только от Ruby, оно должно загружаться как можно быстрее.

S.Goswami 28.05.2019 22:07

@ С.Госвами Я не понимаю. Вы знаете, что ваша попытка неверна, и вы также говорите, что знаете, как правильно это сделать. Что именно вы тогда спрашиваете? Вы спрашиваете, почему irb надежно проигрывает вашу гонку, а echo уверенно ее выигрывает?

that other guy 28.05.2019 22:15

На самом деле это echo правильно выводит вывод. Но если вы запускаете такие программы, как cmatrix | lolcat, он работает, irb | lolcat работает (и лолкот не жалуется на TTY или подобные вещи). Однако я уже реализовал метод STDOUT.tty? в первом коммите, который завершается, если tty не обнаружен — скажем, если вы запускаете программу в таких редакторах, как atom или vscode. В любом случае, мне интересно узнать, как я могу реализовать свою программу таким образом, чтобы она могла работать с другими программами?

S.Goswami 28.05.2019 22:35

Кроме того, посмотрите на irb | head -n5, cmatrix | head -n5 они все работают. Я думаю, что моя реализация вообще неверна! Независимо от тайм-аута # тайм-аут, есть ли какой-нибудь драгоценный камень из стандартной библиотеки, который я могу использовать? На самом деле мне интересно, что может быть правильной реализацией около года!!

S.Goswami 28.05.2019 22:42
Пошаговое руководство по созданию собственного Slackbot: От установки до развертывания
Пошаговое руководство по созданию собственного Slackbot: От установки до развертывания
Шаг 1: Создание приложения Slack Чтобы создать Slackbot, вам необходимо создать приложение Slack. Войдите в свою учетную запись Slack и перейдите на...
0
7
115
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Чтобы правильно прочитать все данные из канала, читайте блок за блоком без тайм-аутов и убедитесь, что вы можете обрабатывать любые данные по мере их поступления:

#!/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

Это устраняет две проблемы с вашим опубликованным кодом (игнорируя все другие программы и комбинации, о которых вы спрашиваете):

  1. Вы используете тайм-аут, поэтому программа не работает, если ввод идет медленно
  2. Вы читаете строку за строкой, поэтому программа не работает, если ввод не основан на строке

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, ваш ответ правильный и очень полезный. Большое спасибо за ответ!

S.Goswami 29.05.2019 04:51

Извините, это может быть бесполезный комментарий, но я хотел бы сказать, что cowspeak должен проверить, происходит ли перенаправление ввода-вывода, если нет, он покажет случайную цитату. Итак, мне нужно использовать гем Timeout из стандартной библиотеки, чтобы убедиться, что он сначала проверяет строку с STDIN.gets, и если строки нет, он не будет зацикливаться на ... until STDIN.eof?.

S.Goswami 29.05.2019 05:18

Вам не нужен тайм-аут, чтобы проверить, происходит ли перенаправление. Это то, что isatty делает надежно и надежно. Использование тайм-аута действительно плохо.

that other guy 29.05.2019 08:27

Другие вопросы по теме