Почему запись в консоль, к которой подключен STDIN процесса, не отправляет ввод в само приложение?

Взято из этого ответа :

Терминал 1:

[ciupicri@hermes ~]$ cat
shows on the tty but bypasses cat

Терминал 2:

[ciupicri@hermes ~]$ pidof cat
7417
[ciupicri@hermes ~]$ echo "shows on the tty but bypasses cat" > /proc/7417/fd/0

Я не совсем понимаю, зачем писать в файловый дескриптор, соответствующий stdin процесса cat обходит сам процесс, но появляется на терминале. Связь между терминалом , дескриптором файла , файлом устройства , консолью меня сбивает с толку. Кроме того, мне иногда кажется, что в технической документации ими злоупотребляют. Может кто-нибудь просветить меня?

2
задан 1 January 2019 в 20:31
2 ответа

Прежде всего, исходя из наблюдаемого поведения, я предполагаю, что это про линукс. Я ожидаю, что под другой ОС все может обернуться по-другому (к лучшему или к худшему).

Я считаю, что наиболее практичным выводом из этого может быть то, что это неправильный способ взаимодействия с процессом.

Основная причина путаницы здесь в том, что вы ожидаете от файловой системы Linux / proc более высоких, чем от того, что она на самом деле предоставляет.

Если вы внимательно посмотрите на то, что на самом деле находится в файловой системе / proc, вы увидите что-то вроде этого:

$ ls -l /proc/29058/fd/
total 0
lrwx------ 1 user1 user1 64 Jan  1 20:39 0 -> /dev/pts/2
lrwx------ 1 user1 user1 64 Jan  1 20:39 1 -> /dev/pts/2
lrwx------ 1 user1 user1 64 Jan  1 20:39 2 -> /dev/pts/2
$

Как видите, представление в / proc для всех "стандартных" fds (0 / stdin, 1 / stdout, 2 / stderr) этого процесса является символическими ссылками, все они связаны с тем же самым, а именно с терминалом (псевдотерминальным ведомым устройством).

То есть, вы никогда не писали на stdin вашего процесса cat в первую очередь, вы писали на терминал, где он находится .
И это произошло не потому, что концепция stdin работает каким-то странным образом, а потому, что файловая система Linux / proc просто ссылается на то, что процесс считывает как свой stdin.
В таком случае, когда ваш процесс читает с терминала в качестве своего стандартного ввода, ссылка указывает на узел устройства, который представляет как вход (при чтении с устройства) и выводе (при записи на устройство) аспектов этого терминала, этот узел, следовательно, относится только к стандартному вводу вашего процесса, когда вы читаете из него, запись в него не имеет отношения к стандартному вводу вашего процесса.

2
ответ дан 3 December 2019 в 09:31

Вы неправильно думаете о стандартных файловых дескрипторах.

STDIN указывает на устройство ввода. Если вы получите STDIN, принадлежащий другому процессу, вы можете использовать его для кражи входных данных этого процесса. Вы не должны писать в него, и если вы это сделаете, нет особых причин ожидать, что этот вывод будет прочитан целевым процессом; это произойдет только в том случае, если устройство вернет свой выход обратно к входу.

Аналогично, STDOUT указывает на устройство вывода. Если вы получаете STDOUT, принадлежащий другому процессу, вы можете использовать его для генерации вывода, который будет идти в тот же файл или терминал, что и другой процесс. Вам не следует читать из него, и если вы это сделаете, вы вряд ли увидите результат процесса.

В этом конкретном сценарии , как уже указывал Хокан, как STDIN, так и STDOUT указывают на то же устройство, поэтому, хотя вы не должны писать в STDIN, если вы это сделаете, это будет иметь тот же эффект, что и запись в STDOUT, то есть он отправит ваш вывод прямо на терминал. Если бы стандартные файловые дескрипторы были чем-то другим, например, если бы вы использовали перенаправление файлов или конвейерную переадресацию, тогда запись в STDIN не была бы такой же, как запись в STDOUT.

Подумайте об этом так: если бы ваш процесс писал в свой собственный стандартный ввод, вы, как правило, не ожидаете увидеть этот вывод как новый ввод. Тот факт, что это другой процесс, выполняющий запись, этого не меняет.

В частности, , если ваш процесс выполняется на виртуальном терминале (без каких-либо перенаправлений), то при записи в стандартный вывод вы ожидаете, что вывод должен отображаться на терминале, а не возвращаться на ваш ввод. Тот факт, что вы неправильно используете дескриптор стандартного входного файла для терминала, а не стандартный дескриптор выходного файла, этого не изменит, и не имеет значения, какой процесс выполняет запись. (*)


Может быть поучительно рассмотреть некоторые конкретные примеры, когда процесс выполняет запись на свой собственный стандартный ввод.

Что, если стандартный ввод был перенаправлен из файла?

foo@bar:~/test$ cat hello
hi
foo@bar:~/test$ (cat; echo hello there > /dev/stdin; cat) < hello
hi
lo there

Содержимое файла перезаписывается; если процесс не завершил чтение файла первым или если файл стал длиннее, чем был раньше, он прочитает часть или все содержимое, которое только что записал. Обратите внимание: поскольку мы уже прочитали «привет» и новую строку, при продолжении чтения мы пропускаем первые три символа «привет, там».

Что, если стандартный ввод - это конвейер?

foo@bar:~/test$ echo weird | (echo hello > /dev/stdin; cat)
weird
hello

Очевидно, конвейерное устройство Linux фактически возвращает свой вывод обратно на свой ввод, но я не уверен, гарантируется ли такое поведение POSIX или зависит от конкретной реализации. На вашем месте я бы не стал использовать этот трюк!


Так как же лучше всего послать процессу ввод?

Что ж, один вариант описан в этом ответе .

Другой - правильно использовать канал, например,

echo I'm sending some input | cat > myinput

Здесь процесс echo отправляет входные данные в cat процесс.Это гарантированно работает правильно, потому что echo отправляет данные в файловый дескриптор, указывающий на один конец канала, а cat получает данные из файлового дескриптора, указывающего на другой конец. трубы.

В обоих случаях принцип один и тот же. Целевой процесс выполняет чтение с определенного устройства, и нам нужно заставить это устройство генерировать желаемый результат. Как мы можем это сделать, зависит от того, что это за устройство.


(*) Виртуальный терминал мог быть спроектирован так, чтобы обеспечивать различные устройства для стандартного ввода и стандартного вывода оболочки, а также подключенное устройство в стандартный ввод можно было запрограммировать так, чтобы любые записанные в него символы возвращались во входной поток. Но у него нет , чтобы работать таким образом, и, как оказалось, это не так, возможно, потому что основная цель проектирования первых виртуальных терминалов заключалась в том, чтобы вести себя так же, как старомодные физические терминалы , которые они заменяли.

3
ответ дан 3 December 2019 в 09:31

Теги

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