Как передавать данные между оболочками без чтения/записи во временные файлы

Вопрос или проблема

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

Например, если у меня есть следующий код

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)

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

Шаги по созданию и использованию именованного канала:

  1. Создание именованного канала:
    В первом терминале выполните команду:

    mkfifo /tmp/mypipe
  2. Запуск программы для чтения данных:
    Затем выполните в этом же терминале вашу программу. Предположим, у вас есть простая программа на C, которая читает данные из stdin:

    ./your_program < /tmp/mypipe
  3. Отправка данных из другого терминала:
    В другом терминале вы можете отправлять данные в именованный канал:

    echo "Ваши данные" > /tmp/mypipe
  4. Завершение работы:
    Если вы хотите завершить программу, отправьте специальную команду:

    echo "Stop" > /tmp/mypipe

Использование netcat

Если оба терминала находятся на одной машине или в одной сети, можно воспользоваться утилитой netcat (nc), которая идеально подходит для сетевой передачи данных.

Шаги по использованию netcat:

  1. Запуск netcat для прослушивания:
    В одном терминале выполните:

    nc -l -p 31337
  2. Отправка данных из другого терминала:
    В другом терминале выполните:

    echo "Привет, мир!" | nc localhost 31337
  3. Завершение работы:
    Чтобы завершить сессию netcat, просто используйте Ctrl+C в терминале, где работает сервер.

Выводы

Как вы видите, передача данных между оболочками без записи в промежуточные файлы - это вполне осуществимая задача. Выбор метода зависит от ваших конкретных требований и условий. Именованные каналы рекомендуется использовать для локальных обменов данными, а netcat является отличным решением для сценариев с сетью.

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

Оцените материал
Добавить комментарий

Капча загружается...