Соединение ввода _and_output между двумя командами в оболочке/bash
Вопрос
У меня есть две (UNIX) программы A и B, которые читают и записывают данные со стандартного ввода/вывода.
Моя первая проблема заключается в том, как подключить стандартный вывод A к стандартному вводу B. и стандартный вывод B на стандартный ввод A.Т.е. что -то вроде | B Но двунаправленная труба.Я подозреваю, что мог бы решить эту проблему, использование exec для перенаправления но я не смог заставить его работать.Программы интерактивны, поэтому временный файл не подойдет.
Вторая проблема заключается в том, что я хотел бы продублировать каждое направление и передать дубликат через программу регистрации на стандартный вывод, чтобы я мог видеть трафик (на основе текстовых строк), который проходит между программами.Здесь мне может сойти с рук tee >(...), если я смогу решить первую задачу.
Кажется, что обе эти проблемы должны иметь хорошо известные решения, но я ничего не смог найти.
Я бы предпочел решение оболочки 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.
Обновлять:Скрипт выше перестал работать через несколько дней(!).Видимо, это может зайти в тупик.Некоторые предложения в ответах могут быть более надежными.
Другие советы
Как насчет именованной трубы?
# mkfifo foo
# A < foo | B > foo
# rm foo
Что касается второй части, я считаю, что правильный ответ — это «ти».Итак, получается:
# A < foo | tee logfile | B > foo
Вероятно, вам сойдет с рук использование именованных каналов:
mkfifo pipe
gawk '$1' < pipe | gawk '$1' > pipe
Вы можете использовать Ожидать.
Expect — это инструмент для автоматизации интерактивных приложений, таких как telnet, ftp, passwd, fsck, rlogin, Tip и т. д.
Вы можете использовать следующий код (взятый из Исследование Ожидайте book) в качестве отправной точки — он соединяет выход proc1 со входом proc2 и наоборот, как вы и просили:
#!/usr/bin/expect -f
spawn proc1
set proc1 $spawn_id
spawn proc2
interact -u $proc1
Я потратил на это много времени, забросил и в последний раз решил использовать ksh (оболочку Korn), которая это позволяет.
cmd1 |& cmd2 >&p <&p
где |&
является оператором (конвейера) для запуска совместного процесса и &p
является файловым дескриптором этого совместного процесса.
В какой-то момент у меня возникла эта проблема, и я собрал эту простую программу на 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")
}
Этот вопрос похож на один Я спрашивал раньше.Решения, предложенные другими, заключались в использовании именованных каналов, но я подозреваю, что в Cygwin их нет.В настоящее время я придерживаюсь мое собственное (попытка) решения, но это требует /dev/fd/0
которого у вас, вероятно, тоже нет.
Хотя мне не очень нравится аспект передачи командных строк как строк. twinpipe
(упоминается JeeBee (139495)) возможно, это ваш единственный вариант в Cygwin.
Я бы предложил «копрок»:
#! /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 ...