我怀疑这个问题之前已经被问过并回答过,但我不确定我的搜索是否正确。
我有一个非终止进程(服务器),它在启动时输出一行及其 url 和端口。我想将该行捕获到一个变量中,以便我可以将其传递给其他程序。
所以我正在尝试做这样的事情:
% export URL=`server start` &
然而它没有设置,URL
因为(我认为:-))server
是非终止的并且仍在运行。
如何将打印的 URL 捕获server
到变量中以便我可以在其他地方使用它?
答案1
您可以尝试这样做:
server start &
cat /proc/${!}/fd/1
这将启动服务器和 $!返回它的 id。 fd/1 是通过 /proc 可用的进程的标准输出
注意:start 和 $! 之间不应该有其他行代码因为它可能会返回另一个进程 ID
答案2
解决方案
创建 FIFO(命名管道)。如果您需要对其他用户隐藏它,请
mktemp -d
事先创建一个临时目录并在其中创建 FIFO(为简洁起见,此答案不执行此操作)。mkfifo myfifo
我们将让服务器进程写入 FIFO;我们将让脚本从 FIFO 中读取数据。启动服务器后,打开 FIFO 进行读取然后关闭(这样就没有读取器了)会导致向服务器进程发送 SIGPIPE;因此最好预先打开 FIFO 进行读取并保持打开状态。另一方面,提前打开FIFO独自读取会阻塞,等待某个进程打开 FIFO 进行写入。要解决这个问题,请打开 FIFO 进行读写,并且稍后再关闭描述符:
exec 3<>myfifo
在后台启动服务器并让它写入 FIFO:
server start >&3 &
(
>&3
和>myfifo
在技术上是不同的;对我们来说,任何一种形式都可以在这里工作)。从 FIFO 中精确读取一行:
<&3 IFS= read -r URL
(
<&3
和<myfifo
在技术上是不同的;对我们来说,任何一种形式都可以在这里工作)。cat
在后台启动,让进程打印到脚本的标准输出(或将其输出重定向cat
到/dev/null
或其他;根据您的需要进行调整)。server
如果向 FIFO 打印太多内容,省略此步骤将导致阻塞。cat
仅当您完全确定该过程在您执行的操作之后不再打印任何内容时,您才需要此操作read
;即便如此,cat
也不会造成任何伤害。一般来说,还有另一个问题:
server
在我们关闭所有打开供读取的描述符后,可能会因为 SIGPIPE 而退出。我们server
自己持有用于写入的描述符和读取,因此 SIGPIPE 不会发生。如果发生这种情况,那么开始cat
从 FIFO 读取即可解决问题。然后开始
cat
:<myfifo cat 3>&- &
注意这里我们单独打开
myfifo
读,并且不允许cat
继承允许写的描述符。要点是cat
不得保持 FIFO 打开以进行写入。稍后这将很重要。关闭描述符,因此仅
cat
保持server
FIFO 打开。您还可以取消链接 FIFO(这不会中断使用它的进程):exec 3>&- rm myfifo
这样,您的脚本的未来后代就不会继承该描述符。
与 一起工作
$URL
。退出时
server
,不会有其他进程持有打开的 FIFO 进行写入,cat
将遇到 EOF 条件并退出。这就是我们开始的方式cat
很重要的时候。如果cat
持有继承的文件描述符,形式上它将是 FIFO 的写入者,因此它无法自动退出。我们确保退出cat
后会自动退出server
。在脚本的最后,您可能希望
wait
发生这种情况:wait
(这假设没有其他后台作业)。如果没有这个,
wait
脚本可能会在 之前退出server
,server
并cat
留在后台。决定是否需要该脚本wait
。
概念证明
#!/bin/sh
# fake server
server() {
echo 'example.com'
for i in 1 2 3 4 5 6 7; do echo 'server running'; sleep 1; done
}
mkfifo myfifo
exec 3<>myfifo
server start >&3 &
<&3 IFS= read -r URL
<myfifo cat 3>&- &
exec 3>&-
rm myfifo
echo "The URL is $URL"
sleep 2
echo "Still working with $URL"
sleep 2
echo "Done. Waiting for the server to close."
wait