Взято из этого ответа :
Терминал 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
обходит сам процесс, но появляется на терминале. Связь между терминалом
, дескриптором файла
, файлом устройства
, консолью
меня сбивает с толку. Кроме того, мне иногда кажется, что в технической документации ими злоупотребляют. Может кто-нибудь просветить меня?
Прежде всего, исходя из наблюдаемого поведения, я предполагаю, что это про линукс. Я ожидаю, что под другой ОС все может обернуться по-другому (к лучшему или к худшему).
Я считаю, что наиболее практичным выводом из этого может быть то, что это неправильный способ взаимодействия с процессом.
Основная причина путаницы здесь в том, что вы ожидаете от файловой системы 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.
В таком случае, когда ваш процесс читает с терминала в качестве своего стандартного ввода, ссылка указывает на узел устройства, который представляет как вход (при чтении с устройства) и выводе (при записи на устройство) аспектов этого терминала, этот узел, следовательно, относится только к стандартному вводу вашего процесса, когда вы читаете из него, запись в него не имеет отношения к стандартному вводу вашего процесса.
Вы неправильно думаете о стандартных файловых дескрипторах.
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
получает данные из файлового дескриптора, указывающего на другой конец. трубы.
В обоих случаях принцип один и тот же. Целевой процесс выполняет чтение с определенного устройства, и нам нужно заставить это устройство генерировать желаемый результат. Как мы можем это сделать, зависит от того, что это за устройство.
(*) Виртуальный терминал мог быть спроектирован так, чтобы обеспечивать различные устройства для стандартного ввода и стандартного вывода оболочки, а также подключенное устройство в стандартный ввод можно было запрограммировать так, чтобы любые записанные в него символы возвращались во входной поток. Но у него нет , чтобы работать таким образом, и, как оказалось, это не так, возможно, потому что основная цель проектирования первых виртуальных терминалов заключалась в том, чтобы вести себя так же, как старомодные физические терминалы , которые они заменяли.