Bash

後台程序的管道部分輸出並將其儲存在變數中

  • August 23, 2020

背景:

我正在開發一個伺服器在隨機可用埠啟動的項目。伺服器由以下命令啟動:

node server.js

一旦伺服器準備好並開始在可用埠上提供服務,它就會以以下形式在標準輸出上輸出(以及一堆其他日誌)完整的伺服器 URL(包括埠號):

Server started! Listening on URL: http://localhost:12927

此伺服器程序一直執行,直到您手動終止它(並繼續列印一些其他日誌)。

實際問題:

我正在編寫一個腳本來啟動伺服器,然後收聽它的輸出。一旦我收到類似的消息"Server started! Listening on URL: [SERVER_URL]",我想將其儲存SERVER_URL在一個變數中並稍後在腳本中處理(例如,打開瀏覽器進行自動化測試)。

我試過的:

對 Bash 只有非常基本的了解,我嘗試了以下選項(當然,在研究 SE 之後):

  1. 我研究了一些關於使用grep正則表達式的資訊。如果我手動執行此操作(即不將其儲存到變數而只是從echo命令中進行管道傳輸),這將非常有效。
SERVER_URL=$(node server.js | grep -m 1 -ohP 'Server started! Listening on URL: \K([^\s$]*)')

但是,它在這條線上有點阻塞,可能它正在等待節點伺服器被殺死。我的要求很明確:我想從伺服器的輸出中填充 SERVER_URL,而不是等待它超過它在標準輸出上吐出該 URL 的點。

  1. 我還擺弄了一些重定向程序替換。我了解到管道實際上是在等待源程序完成。我嘗試通過重定向執行以下操作,但它不起作用:
SERVER_URL=$(node server.js >&3 | grep -m 1 -ohP 'Server started! Listening on URL: \K([^\s$]*)')

它拋出了一些奇怪的“未處理的’錯誤’事件”異常(錯誤:寫 EPIPE)。

任何幫助表示讚賞。謝謝!

是的,在:

var=$(cmd | grep -m1 pattern)

cmd的標準輸出是到 的管道grep,而grep的標準輸出是外殼的管道。

grep找到第一個匹配項後,它將其輸出到其標準輸出並退出。

shell 會讀取grep’ 的輸出,grep退出時查看 end-of-file,但在 的情況下bash,shell 也會等待cmd退出。

現在,因為grep已經退出,cmd’s stdout 現在是一個損壞的管道。所以下次cmd寫入標準輸出時,它會收到一個 SIGPIPE 信號並死掉。

cmd在這裡,問題是,一旦你讀到與模式匹配的第一行,你想對 ’s 的其餘輸出做什麼?

如果你對它不感興趣,你可以執行一個後台cat命令來讀取它並丟棄它:

{
  url=$(grep -m1 -oP 'Server started! Listening on URL: \K\S+')
  cat <&0 > /dev/null & # <&0 to work around the fact that bash redirects
                        # background commands stdin from /dev/null
} < <(node server.js)

如果你想保留它,你可以將它重定向到某個文件,並tail -f在 shell 中使案例如等待 URL 消息:

node server.js > server.log &
{
 IFS= read -r pid
 url=$(grep -m1 -oP 'Server started! Listening on URL: \K\S+')
 kill -- "$pid"
} < <(echo "$BASHPID"; exec tail -n +1 -f server.log)

在這里傳遞 tail 的 pid 以便在找到消息後將其殺死,而不是等待tail被 SIGPIPE 殺死,如果tail從不輸出任何其他內容,這可能永遠不會發生。

引用自:https://unix.stackexchange.com/questions/605889