将非终止进程的输出捕获到变量中

将非终止进程的输出捕获到变量中

我怀疑这个问题之前已经被问过并回答过,但我不确定我的搜索是否正确。

我有一个非终止进程(服务器),它在启动时输出一行及其 url 和端口。我想将该行捕获到一个变量中,以便我可以将其传递给其他程序。

所以我正在尝试做这样的事情:

% export URL=`server start` &

然而它没有设置,URL因为(我认为:-))server是非终止的并且仍在运行。

如何将打印的 URL 捕获server到变量中以便我可以在其他地方使用它?

答案1

您可以尝试这样做:

server start &
cat /proc/${!}/fd/1

这将启动服务器和 $!返回它的 id。 fd/1 是通过 /proc 可用的进程的标准输出

注意:start 和 $! 之间不应该有其他行代码因为它可能会返回另一个进程 ID

答案2

解决方案

  1. 创建 FIFO(命名管道)。如果您需要对其他用户隐藏它,请mktemp -d事先创建一个临时目录并在其中创建 FIFO(为简洁起见,此答案不执行此操作)。

    mkfifo myfifo
    
  2. 我们将让服务器进程写入 FIFO;我们将让脚本从 FIFO 中读取数据。启动服务器后,打开 FIFO 进行读取然后关闭(这样就没有读取器了)会导致向服务器进程发送 SIGPIPE;因此最好预先打开 FIFO 进行读取并保持打开状态。另一方面,提前打开FIFO独自读取会阻塞,等待某个进程打开 FIFO 进行写入。要解决这个问题,请打开 FIFO 进行读写,并且稍后再关闭描述符:

    exec 3<>myfifo
    
  3. 在后台启动服务器并让它写入 FIFO:

    server start >&3 &
    

    >&3>myfifo在技术上是不同的;对我们来说,任何一种形式都可以在这里工作)。

  4. 从 FIFO 中精确读取一行:

    <&3 IFS= read -r URL
    

    <&3<myfifo在技术上是不同的;对我们来说,任何一种形式都可以在这里工作)。

  5. cat在后台启动,让进程打印到脚本的标准输出(或将其输出重定向cat/dev/null或其他;根据您的需要进行调整)。server如果向 FIFO 打印太多内容,省略此步骤将导致阻塞。cat仅当您完全确定该过程在您执行的操作之后不再打印任何内容时,您才需要此操作read;即便如此,cat也不会造成任何伤害。

    一般来说,还有另一个问题:server在我们关闭所有打开供读取的描述符后,可能会因为 SIGPIPE 而退出。我们server自己持有用于写入的描述符读取,因此 SIGPIPE 不会发生。如果发生这种情况,那么开始cat从 FIFO 读取即可解决问题。

    然后开始cat

    <myfifo cat 3>&- &
    

    注意这里我们单独打开myfifo读,并且不允许cat继承允许写的描述符。要点是cat不得保持 FIFO 打开以进行写入。稍后这将很重要。

  6. 关闭描述符,因此仅cat保持serverFIFO 打开。您还可以取消链接 FIFO(这不会中断使用它的进程):

    exec 3>&-
    rm myfifo
    

    这样,您的脚本的未来后代就不会继承该描述符。

  7. 与 一起工作$URL

  8. 退出时server,不会有其他进程持有打开的 FIFO 进行写入,cat将遇到 EOF 条件并退出。这就是我们开始的方式cat很重要的时候。如果cat持有继承的文件描述符,形式上它将是 FIFO 的写入者,因此它无法自动退出。我们确保退出cat后会自动退出server

    在脚本的最后,您可能希望wait发生这种情况:

    wait
    

    (这假设没有其他后台作业)。如果没有这个,wait脚本可能会在 之前退出serverservercat留在后台。决定是否需要该脚本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

相关内容