Соединение ввода _and_output между двумя командами в оболочке/bash

У меня есть две (UNIX) программы A и B, которые читают и пишут из stdin/stdout.

Моя первая проблема заключается в том, как подключить стандартный вывод A к стандартному вводу B и стандартный вывод B к стандартному вводу A. То есть что-то вроде A | B, но двунаправленная труба. Я подозреваю, что мог бы решить эту проблему, используя exec для перенаправления, но я не мог этого добиться работать. Программы интерактивны, поэтому временный файл не подойдет.

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

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

Я бы предпочел решение оболочки POSIX или, по крайней мере, что-то, что работает в bash на cygwin.

Благодаря вашим ответам я пришел к следующему решению. Команды A/B используют nc для прослушивания двух портов. Программа регистрации использует sed (с -u для небуферизованной обработки).

bash-3.2$ fifodir=$(mktemp -d)
bash-3.2$ mkfifo "$fifodir/echoAtoB"
bash-3.2$ mkfifo "$fifodir/echoBtoA"
bash-3.2$ sed -u 's/^/A->B: /' "$fifodir/echoAtoB" &
bash-3.2$ sed -u 's/^/B->A: /' "$fifodir/echoBtoA" &
bash-3.2$ mkfifo "$fifodir/loopback"
bash-3.2$ nc -l -p 47002 < "$fifodir/loopback" \
          | tee "$fifodir/echoAtoB" \
          | nc -l -p 47001 \
          | tee "$fifodir/echoBtoA" > "$fifodir/loopback"

Это прослушивает подключение к портам 47001 и 47002 и выводит весь трафик на стандартный вывод.

В оболочке 2 выполните:

bash-3.2$ nc localhost 47001

В оболочке 3 выполните:

bash-3.2$ nc localhost 47002

Теперь строки, введенные в оболочке 2, будут записаны в оболочку 3 и наоборот, а трафик будет зарегистрирован в оболочке 1, что-то вроде:

B->A: input to port 47001
A->B: input to port 47002

Вышеупомянутое было протестировано на Cygwin

Обновление: приведенный выше скрипт перестал работать через несколько дней (!). Судя по всему, он может зайти в тупик. Некоторые предложения в ответах могут быть более надежными.


person Per Mildner    schedule 26.09.2008    source источник


Ответы (8)


http://bisqwit.iki.fi/source/twinpipe.html

person JeeBee    schedule 26.09.2008
comment
twinpipe также поставляется со сценарием оболочки, который использует подход именованных каналов, предложенный другими. Спасибо - person Per Mildner; 26.09.2008

Как насчет именованного канала?

# mkfifo foo
# A < foo | B > foo
# rm foo

Для вашей второй части я считаю, что тройник - правильный ответ. Так становится:

# A < foo | tee logfile | B > foo
person sherbang    schedule 26.09.2008

Вероятно, вы могли бы уйти с именованными каналами:

mkfifo pipe
gawk '$1' < pipe | gawk '$1' > pipe
person Aaron Maenpaa    schedule 26.09.2008

Вы можете использовать Ожидание.

Expect — это инструмент для автоматизации интерактивных приложений, таких как telnet, ftp, passwd, fsck, rlogin, tip и т. д.

Вы можете использовать следующий код (взятый из книги Exploring Expect) в качестве отправной точки — он соединяет выход proc1 с входом proc2 и наоборот, как вы просили:

#!/usr/bin/expect -f
spawn proc1
set proc1 $spawn_id
spawn proc2
interact -u $proc1
person Bruno Gomes    schedule 26.09.2008

Я потратил на это уйму времени, бросил и в последний раз решил использовать ksh (оболочку Korn), которая это позволяет.

cmd1 |& cmd2 >&p <&p

где |& — оператор (конвейера) для запуска совместного процесса, а &p — файловый дескриптор этого совместного процесса.

person Community    schedule 09.02.2009

В какой-то момент у меня была эта проблема, и я собрал эту простую программу на C.

#include <stdio.h>
#include <unistd.h>

#define PERROR_AND_DIE(_x_) {perror(_x_); _exit(1);}

int main(int argc, char **argv) {
    int fd0[2];
    int fd1[2];


    if ( argc != 3 ) {
        fprintf(stdout, "Usage %s: \"[command 1]\" \"[command 2]\"\n", argv[0]);
        _exit(1);
    }

    if ( pipe(fd0) || pipe(fd1) ) PERROR_AND_DIE("pipe")

    pid_t id = fork();
    if ( id == -1 ) PERROR_AND_DIE("fork");

    if ( id ) {
        if ( -1 == close(0) )  PERROR_AND_DIE("P1: close 0");
        if ( -1 == dup2(fd0[0], 0) ) PERROR_AND_DIE("P1: dup 0"); //Read my STDIN from this pipe

        if ( -1 == close(1) )  PERROR_AND_DIE("P1: close 1");
        if ( -1 == dup2(fd1[1], 1) ) PERROR_AND_DIE("P1: dup 1"); //Write my STDOUT here
        execl("/bin/sh", "/bin/sh", "-c", argv[1], NULL);
        PERROR_AND_DIE("P1: exec")
    }

    if ( -1 == close(0) )  PERROR_AND_DIE("P2: close 0");
    if ( -1 == dup2(fd1[0], 0) ) PERROR_AND_DIE("P2: dup 0");

    if ( -1 == close(1) )  PERROR_AND_DIE("P2: close 1");
    if ( -1 == dup2(fd0[1], 1) ) PERROR_AND_DIE("P2: dup 1");


    execl("/bin/sh", "/bin/sh", "-c", argv[2], NULL);
    PERROR_AND_DIE("P2: exec")
}
person basicer    schedule 27.10.2011

Этот вопрос похож на один, который я задавал ранее. Решения, предложенные другими, заключались в использовании именованных каналов, но я подозреваю, что у вас их нет в cygwin. В настоящее время я придерживаюсь своего (попытки) решения, но для этого требуется /dev/fd/0, которого у вас, вероятно, тоже нет.

Хотя мне не очень нравится аспект передачи командных строк в виде строк twinpipe (упомянутый JeeBee (139495)), это может быть ваш единственный вариант в cygwin.

person mweerden    schedule 26.09.2008
comment
ИМХО, это лучшее / единственное разумное решение ... хотя, похоже, требуется bash (вместо тире / sh) - person Dreamcat4; 09.06.2019

Я бы предложил "coproc":

#! /bin/bash
# initiator needs argument

if [ $# -gt 0 ]; then
  a=$1
  echo "Question $a"
else
  read a
fi

if [ $# -gt 0 ]; then
  read a
  echo "$a" >&2
else
  echo "Answer to $a is ..."
fi

exit 0

Тогда посмотрите этот сеанс:

$ coproc ./dialog
$ ./dialog  test  < /dev/fd/${COPROC[0]}  > /dev/fd/${COPROC[1]}
Answer to Question test is ...
person mmaruska    schedule 29.11.2012