我正在尝试使用 bash 创建持久连接。在终端 1 上,我保持一个 netcat 作为服务器运行:
$ nc -vlkp 3000
Listening on [0.0.0.0] (family 0, port 3000)
在 2 号航站楼,我创建了一个 fifo 并养了一只猫:
$ mkfifo fifo
$ cat > fifo
在终端 3 上,我将 fifo 作为客户端 netcat 的输入:
$ cat fifo | nc -v localhost 3000
Connection to localhost 3000 port [tcp/*] succeeded!
在 4 号航站楼,我发送任何我想要的东西:
$ echo command1 > fifo
$ echo command2 > fifo
$ echo command3 > fifo
回到终端 1,我看到正在接收命令:
$ nc -vlkp 3000
Listening on [0.0.0.0] (family 0, port 3000)
Connection from [127.0.0.1] port 3000 [tcp/*] accepted (family 2, sport 41722)
command1
command2
command3
所以,一切正常。但是当我将其放入脚本中(我称之为 fifo.sh)时,bash 无法写入 fifo:
在终端1上,相同的监听服务器:
$ nc -vlkp 3000
Listening on [0.0.0.0] (family 0, port 3000)
在终端 2 上,我运行脚本:
#!/bin/bash
rm -f fifo
mkfifo fifo
cat > fifo &
pid1=$!
cat fifo | nc -v localhost 3000 &
pid2=$!
echo sending...
echo comando1 > fifo
echo comando2 > fifo
echo comando3 > fifo
kill -9 $pid1 $pid2
终端2的输出为:
$ ./fifo.sh
Connection to localhost 3000 port [tcp/*] succeeded!
sending...
在 1 号终端上,我只看到连接。没有命令:
$ nc -vlkp 3000
Listening on [0.0.0.0] (family 0, port 3000)
Connection from [127.0.0.1] port 3000 [tcp/*] accepted (family 2, sport 42191)
Connection closed, listening again.
知道为什么它只能交互工作吗?或者有没有其他方法可以仅使用 Bash 创建持久连接?我不想选择 Expect,因为我有一个更大的 Bash 脚本,它在发送 command1 后执行一些工作,而 command2 取决于 command1 输出等。
谢谢!
当进程在脚本中的后台启动时,标准输入将从
/dev/null
重定向。这意味着第一个 cat
命令将在执行后立即读取并发出 EOF
,这将导致 netcat 在启动后立即退出,因此脚本中后面的输出将永远不会到达 fifo,因为没有'当时是一个积极的倾听者。
在这种情况下,当计算
cat > fifo
时,shell 会分叉一个子进程,从 /dev/null
重定向标准输入,并尝试打开 fifo
进行写入。此时,孩子仍处于阻塞 open
呼叫中。请注意,直到 cat
调用完成后才会执行 open
。
接下来,生成
cat fifo | nc -v localhost 3000
。 cat
打开 fifo
进行读取,这允许第一个子级的阻塞 open
完成并执行第一个 cat
。
第一个
cat
继承其父级的文件描述符,因此其标准输入附加到 /dev/null
,因此它会立即读取并发出 EOF
。第二个 cat
读取 EOF
并将其传递给 nc
的标准输入,这会导致 netcat 退出。
当评估
echo
语句时,由 $pid1
和 $pid2
标识的流程已完成。由于 fifo
上不再有侦听器,因此第一个 echo
将永远阻塞。
我没有纯 shell 修复,但您可以使用 perl 等外部程序将占位符写入器打开为
fifo
,而不是使用 shell 重定向。另外,请注意,在 nc
语句之后开始存在与 echo
的竞争(其中 kill
发生在 netcat 有机会处理输入/发送输出之前),因此这里我在 cat | nc
之后添加了延迟
表情。几乎肯定有更好的解决方案,但这是我想到的:
#!/bin/bash
rm -f fifo
mkfifo fifo
perl -e 'open(my $fh, ">", "fifo"); sleep 3600 while 1' &
pid1=$!
cat fifo | nc -v localhost 3000 &
pid2=$!
sleep 2
echo sending...
echo comando1 > fifo
echo comando2 > fifo
echo comando3 > fifo
kill -9 $pid1 $pid2
希望这有帮助,很好的问题!
@zackse的解释很到位。
您可以在脚本中打开一个文件描述符来保留
netcat
从看到 EOF:
#!/bin/bash
# clean up on exit: close FD 3 and exit all backgrounded
# processes in this process group (see `kill(2)`)
trap 'rm -f fifo; exec 3>&-; kill 0' EXIT
mkfifo fifo
# netcat reads from `fifo `
busybox nc -v localhost 3000 <fifo &
# create FD 3 for writing to `fifo`; this keeps the reading end
# of the pipe open and prevents netcat from seeing EOF
exec 3>fifo
echo Sending...
echo foo >fifo
echo bar >fifo
echo baz >fifo
# wait for all background processes to exit
# allows you to send more data to netcat
wait