توصيل الإدخال _and_output بين أمرين في Shell/bash
سؤال
لدي برنامجان (UNIX) A وB يقرأان ويكتبان من stdin/stdout.
مشكلتي الأولى هي كيفية توصيل stdout لـ A بـ stdin لـ B و stdout من B إلى stdin من A.أي شيء مثل | ب ولكن أنبوب ثنائي الاتجاه.أظن أنني أستطيع حل هذا عن طريق باستخدام exec لإعادة التوجيه ولكن لم أستطع الحصول عليه للعمل.البرامج تفاعلية لذا لن يعمل الملف المؤقت.
المشكلة الثانية هي أنني أرغب في تكرار كل اتجاه وتوجيه نسخة مكررة عبر برنامج تسجيل إلى stdout حتى أتمكن من رؤية حركة المرور (المعتمدة على سطر النص) التي تمر بين البرامج.وهنا قد أفلت من العقاب >(...) إذا تمكنت من حل المشكلة الأولى.
يبدو أن هاتين المشكلتين يجب أن يكون لهما حلول معروفة ولكنني لم أتمكن من العثور على أي شيء.
أفضّل حل POSIX Shell، أو على الأقل شيئًا يعمل في 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
في Shell 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
يمكنك استخدام يتوقع.
المتوقع عبارة عن أداة لأتمتة التطبيقات التفاعلية مثل 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
(ذكر بواسطة جيبي (139495)))، قد يكون هذا هو خيارك الوحيد في cygwin.
أقترح "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 ...