Вопрос или проблема
Один вопрос, связанный с pty (псевдотерминалы) и с тем, как (cat) golang читает из них.
Сначала я сделал следующее.
- открыл два терминала (
dev/pts/0
– 0 иdev/pts/1
– 1) - затем я ввёл во втором терминале “cat dev/pts/0” (так что все, что написано в первом терминале, будет отображаться во втором)
- начал писать в первом терминале (TUI)
В большинстве случаев это работает, но время от времени некоторые символы пропускаются.
Точнее, для тех символов, которые не отображаются во втором терминале, у меня есть эхо, а для тех, которые отображаются, эхо нет. Это как будто эхо и cat конкурируют за символы.
Однако, когда я работаю с golang и функциями форматирования ввода, каждый раз, когда я что-то пишу в терминал (и таким образом в /dev/pts/0
), функция считывания читает всё из него (нет пропущенных байт) + введённое содержимое отображается в TUI (в этом случае “эхо” работает).
package main
import "fmt"
func main() {
var input string
fmt.Println("Enter text:")
for {
fmt.Scanln(&input)
fmt.Println("You wrote:", input)
}
}
Таким образом, в этом случае, кажется, что “конкуренции” между эхом и golang не существует…
Я ожидаю одинакового поведения в обоих случаях. В частности, прочитать все байты в обоих случаях и иметь (или не иметь) эхо в обоих случаях.
В чем разница? Почему “cat” иногда пропускает байты при чтении из другого терминала, а функция считывания golang этого не делает? Я думал, что golang просто читает из “dev/pts/0” так же, как и “cat”, и просто выдаёт результат.
Ответ или решение
Во-первых, чтобы понять, почему "cat" иногда пропускает байты при чтении с другого терминала, а функция сканирования на Golang – нет, необходимо разобраться в базовых принципах работы с псевдотерминалами (pty).
1. Природа псевдотерминалов
Псевдотерминалы, такие как /dev/pts/0
и /dev/pts/1
, создаются для симуляции работы физических терминалов. Они состоят из пары устройств: master и slave. Ваша команда cat /dev/pts/0
на втором терминале прослушивает ввод с первого терминала, создавая своеобразный канал передачи данных между ними.
2. Конкуренция между Echo и Cat
Когда вы вводите текст в первый терминал, данные поступают через tty драйвер. В зависимости от настроек терминала, tty может обрабатывать ввод различными способами, включая эхо-ввод. В случае с cat
, он не контролирует эхо-ввод, поскольку эхо управляется tty драйвером. Это означает, что и cat
, и эхо-ввод обращаются к одним и тем же символам, потенциально вызывая конфликты. Таким образом, некоторые символы могут быть отображены только в виде эхо-ввода или только через cat
, но не через оба.
3. Обработка ввода в Golang
Воспользовавшись функцией Scanln
из пакета fmt
в Golang, вы получаете преимущества буферизации. Scanln
ждет завершения ввода (обычно завися от нажатия Enter), прежде чем начать обработку. Таким образом, он исключает возможность конфликта с эхо-вводом, так как все данные уже находятся в формате, готовом для чтения, прежде чем Scanln
их обрабатывает.
4. Различия в реализации
cat
просто считывает поток данных побайтово, что делает его уязвимым к коллизиям с tty эхо-вводом. В отличие от cat
, Golang с fmt.Scanln
буферизует и анализирует строку целиком, после чего выводит обработанный текст, минимизируя возможность конфликтов с tty.
Заключение
Таким образом, различие в поведении cat
и функции Scanln
на Golang связано с тем, как они обрабатывают данные ввода. cat
работает в режиме реального времени без буферизации, подвергаясь конкуренции с эхо-вводом, тогда как Golang использует буферизацию и управление вводом, чтобы избежать потери данных. Выполняя разработку приложений, взаимодействующих с терминалами, учитывайте эти аспекты для достижения требуемого поведения.
На всякий случай, при работе с cat
можно рассмотреть применение переменной среды stty
для настройки эхо-ввода, что может помочь снизить количество конфликтов.
Таким образом, понимание особенностей работы каждого из этих инструментов позволит вам более эффективно управлять потоками данных в сценариях с использованием псевдотерминалов.