Вопрос или проблема
Допустим, у меня есть процесс, который выводит пользовательский ввод, работающий в оболочке. Как можно передавать данные между этой оболочкой и другой терминальной сессией? Возможно ли это без использования промежуточного файла?
Например, если у меня есть следующий код
fgets(string, LEN, stdin);
printf("%s", string);
То возможно ли передать данные в stdin
и получить данные из stdout
через другую оболочку?
Я не совсем уверен, что понял, что вы имеете в виду, но.
В рамках одной сессии оболочки (терминала) вы можете использовать конвейер для передачи данных от одной команды к другой, например:
$ ls -l | grep что-то
Если вам нужно сделать это между двумя разными оболочками, вы можете использовать именованный канал:
tty1$ mkfifo /tmp/mypipe
tty1$ ls -l > /tmp/mypipe
tty2$ grep что-то < /tmp/mypipe
Безопаснее будет использовать mktemp
для создания директории, в которую поместить именованный канал:
tty1$ dir=$(mktemp -d)
tty1$ mkfifo "$dir/mypipe"
tty1$ ls -l > "$dir/mypipe"
tty1$ rm -r "$dir"
хотя это требует вручную скопировать путь в другое окно.
Конечно, именованный канал немного напоминает промежуточный файл, поскольку ему нужно имя пути. Но он больше похож на канал тем, что данные не записываются в постоянное хранилище, и читатель ждет писателя, если писатель медленный, вместо того чтобы, возможно, столкнуться с преждевременным концом файла.
(Обычно вы бы использовали ls -l *что-то*
вместо ls | grep
, но это работает как пример.)
Я думаю, что ответ @ilkkachu полезен и предоставляет то, что вам нужно. Я постараюсь объяснить более подробно и одновременно научиться использовать fifo.
-
Подготовьте два окна командной строки, w1 и w2 на одном компьютере.
-
Создайте слушающий
program
, я сделал оболочечный скрипт в w1:#!/bin/bash while true do read string if [ "${string:0:4}" == "Stop" ] then printf "Уловил\n" break elif [ "$string" != "" ] then printf "$string " else sleep 3 fi done
-
Подготовьте fifo в w1:
dir=$(mktemp -d) mkfifo "$dir/mypipe"
-
Запустите программу и дайте ей ожидать ввода через fifo в w1:
< "$dir/mypipe" ./program
-
Ищите fifo и выводите некоторые строки в него в w2:
$ find /tmp -name mypipe 2>/dev/null /tmp/tmp.dRhpqajJqz/mypipe $ > '/tmp/tmp.dRhpqajJqz/mypipe' echo qwerty $ > '/tmp/tmp.dRhpqajJqz/mypipe' echo asdf $ > '/tmp/tmp.dRhpqajJqz/mypipe' echo Stopp
-
Посмотрите на вывод в w1:
qwerty asdf Уловил $
Вы также можете сделать это более автоматизированным, например, как в следующем примере, который предполагает, что существует только один временный файл с именем mypipe
:
-
Запустите программу снова в w1:
< "$dir/mypipe" ./program
-
В w2:
> $(find /tmp -name mypipe 2>/dev/null) echo 'Привет, мир' >> $(find /tmp -name mypipe 2>/dev/null) echo 'Остановите мир'
-
Посмотрите на вывод в w1:
Привет, мир Уловил $
Демо C программы:
#include
#include
int main () {
char string[21];
while(1){
fgets(string, 20, stdin);
string[strlen(string)-1] = 0;
if(strcmp("Stop", string) == 0){
printf("Уловил");
return 1;
}
}
}
Эта C программа выводит только после того, как она прочитала 'Stop'.
Это зависит от того, насколько сложное решение вы хотите.
Как вы, безусловно, уже знаете, вы можете просто писать из одного терминального устройства в другое, но проблема с этим заключается в том, что различия в настройках терминала (stty -a
) могут вызвать странные вещи.
# Проверьте используемые терминальные устройства:
# На терминале-1 (приемник):
tty
# /dev/pty0
# На терминале-2 (передатчик):
tty
# /dev/pty1
# На терминале-1:
cat /dev/pty1 > out.txt
# На терминале-2
cat in.txt > /dev/pty0
Вы обнаружите, что это не сработало, как предполагалось! Текст из T2
действительно появляется на T1
, но текст уходит прямо в буфер экрана, а не в файл/канал. Единственное, что вы можете сделать, это скопировать/вставить текст с экрана (буфера).
Причина этого заключается в проектировании с учетом безопасности, которое восходит к эпохе Unix PDP-7
примерно в 1970-х. (Да, у них тогда тоже были настоящие h4x0r5.)
По-прежнему возможно, что существуют параметры stty
и инструменты, которые могут изменить это поведение, но я еще ни одного не нашел. (Общее устройство памяти или fifo также многообещающе, как и было предложено в предыдущих ответах.)
Самый простой способ сделать это, который также доступен из терминала на удаленном компьютере в одной подсети, — использовать netcat (nc
), например так:
nc -l 31337 > out.txt # На стороне приема
nc -N 31337 < in.txt # На стороне передачи
# [CTRL+D], если не выходит автоматически
Это невероятно полезно, когда не нужно использовать ssh
или scp
с паролями и навигацией по длинным путям и т. д.
С устройством общей памяти (smd
) вы также можете иметь интерактивную сессию. Это обычно для связи с модемами.
while read a;do echo -e "${a}\r" >>/dev/smd0 ; timeout 1s cat /dev/smd0;done
# или лучше:
# Если у вас есть strace и microcom (busybox).
strace 2>/dev/null -e inject=ioctl:retval=0 microcom /dev/smd0
Чтобы проверить, какое у вас устройство, вы можете сделать это:
# ll /dev/pts/0
crw------- 1 root tty 136, 0 Dec 23 07:42 /dev/pts/0
# Ищите "136":
# cat /proc/tty/drivers
/dev/tty /dev/tty 5 0 system:/dev/tty
/dev/console /dev/console 5 1 system:console
/dev/ptmx /dev/ptmx 5 2 system
/dev/vc/0 /dev/vc/0 4 0 system:vtmaster
rfcomm /dev/rfcomm 216 0-255 serial
g_serial /dev/ttyGS 237 0-7 serial
usbserial /dev/ttyUSB 188 0-511 serial
acm /dev/ttyACM 166 0-255 serial
ttyprintk /dev/ttyprintk 5 3 console
serial /dev/ttyS 4 64-73 serial
pty_slave /dev/pts 136 0-1048575 pty:slave
pty_master /dev/ptm 128 0-1048575 pty:master
unknown /dev/tty 4 1-63 console
fiq-debugger /dev/ttyFIQ 253 0 serial
# Драйвер устройства "136" является "Псевдотерминальным слейвом" (pty:slave)
Ответ или решение
Для передачи данных между оболочками без использования промежуточных файлов можно воспользоваться несколькими методами. В этой статье подробно рассмотрим основные подходы, включая использование именованных каналов (FIFO) и сетевых инструментов, таких как netcat
.
Использование именованных каналов (FIFO)
Именованные каналы позволяют обмениваться данными между разными процессами, работающими в разных терминалах. Они хоть и требуют создания специального файла, не сохраняют данные как обычный файл и обеспечивают взаимосвязь в реальном времени.
Шаги по созданию и использованию именованного канала:
-
Создание именованного канала:
В первом терминале выполните команду:mkfifo /tmp/mypipe
-
Запуск программы для чтения данных:
Затем выполните в этом же терминале вашу программу. Предположим, у вас есть простая программа на C, которая читает данные изstdin
:./your_program < /tmp/mypipe
-
Отправка данных из другого терминала:
В другом терминале вы можете отправлять данные в именованный канал:echo "Ваши данные" > /tmp/mypipe
-
Завершение работы:
Если вы хотите завершить программу, отправьте специальную команду:echo "Stop" > /tmp/mypipe
Использование netcat
Если оба терминала находятся на одной машине или в одной сети, можно воспользоваться утилитой netcat
(nc
), которая идеально подходит для сетевой передачи данных.
Шаги по использованию netcat
:
-
Запуск
netcat
для прослушивания:
В одном терминале выполните:nc -l -p 31337
-
Отправка данных из другого терминала:
В другом терминале выполните:echo "Привет, мир!" | nc localhost 31337
-
Завершение работы:
Чтобы завершить сессиюnetcat
, просто используйтеCtrl+C
в терминале, где работает сервер.
Выводы
Как вы видите, передача данных между оболочками без записи в промежуточные файлы - это вполне осуществимая задача. Выбор метода зависит от ваших конкретных требований и условий. Именованные каналы рекомендуется использовать для локальных обменов данными, а netcat
является отличным решением для сценариев с сетью.
Эти подходы позволяют эффективно обмениваться данными в реальном времени, что особенно полезно в сценариях автоматизации и взаимодействия между различными компонентами системы.