後台程序的管道部分輸出並將其儲存在變數中
背景:
我正在開發一個伺服器在隨機可用埠啟動的項目。伺服器由以下命令啟動:
node server.js
一旦伺服器準備好並開始在可用埠上提供服務,它就會以以下形式在標準輸出上輸出(以及一堆其他日誌)完整的伺服器 URL(包括埠號):
Server started! Listening on URL: http://localhost:12927
此伺服器程序一直執行,直到您手動終止它(並繼續列印一些其他日誌)。
實際問題:
我正在編寫一個腳本來啟動伺服器,然後收聽它的輸出。一旦我收到類似的消息
"Server started! Listening on URL: [SERVER_URL]"
,我想將其儲存SERVER_URL
在一個變數中並稍後在腳本中處理(例如,打開瀏覽器進行自動化測試)。我試過的:
對 Bash 只有非常基本的了解,我嘗試了以下選項(當然,在研究 SE 之後):
- 我研究了一些關於使用
grep
正則表達式的資訊。如果我手動執行此操作(即不將其儲存到變數而只是從echo
命令中進行管道傳輸),這將非常有效。SERVER_URL=$(node server.js | grep -m 1 -ohP 'Server started! Listening on URL: \K([^\s$]*)')
但是,它在這條線上有點阻塞,可能它正在等待節點伺服器被殺死。我的要求很明確:我想從伺服器的輸出中填充 SERVER_URL,而不是等待它超過它在標準輸出上吐出該 URL 的點。
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
從不輸出任何其他內容,這可能永遠不會發生。