Bash 脚本中的持久连接

问题描述 投票:0回答:2

我正在尝试使用 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 输出等。

谢谢!

bash fifo netcat
2个回答
4
投票

当进程在脚本中的后台启动时,标准输入将从

/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

希望这有帮助,很好的问题!


0
投票

@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
© www.soinside.com 2019 - 2024. All rights reserved.