簡體   English   中英

從管道的后期終止管道中的特定進程

[英]Killing a specific process in a pipeline, from later in the pipeline

我正在編寫一個 shell 腳本,該腳本使用telnet連接到服務器,發送查詢,然后獲取單行響應以進行進一步處理。 經典的解決方案是設置一個子shell,它echo查詢,然后運行sleep以保持連接打開足夠長的時間以返回響應:

#!/bin/sh

SERVER_ADD=localhost
SERVER_PORT=9995

result=$( ( echo '{"op":"get","path":"access"}'; sleep 30 ) | telnet "$SERVER_ADD" "$SERVER_PORT" )
    
echo "$result"

這是可行的,只是sleep時間必須長於服務器響應所需的最長時間。 這意味着每次調用都需要很長時間,這將最小時間從幾十毫秒推到了幾十秒。 我需要我的腳本等待第一行響應,然后終止進程,以便它可以 go 繼續執行其他操作。

(是的:我知道顯而易見的答案是“使用expect 。”不幸的是,我的目標是嵌入式系統,添加expect和編寫它的TcL腳本引擎會使我的圖像大小增加大約一兆字節。不能.)

經過多次試驗和錯誤,我想出了以下內容:

#!/bin/sh

SERVER_ADD=localhost
SERVER_PORT=9995

result=$( ( echo '{"op":"get","path":"access"}'; sleep 30 ) |
    telnet "$SERVER_ADD" "$SERVER_PORT" |
    while read -r line; do
        echo "$line"
        killall sleep
    done ) 2>/dev/null
echo "$result"

解釋

  • 設置一個響應查詢的子shell,然后休眠足夠長的時間以獲得最長的響應時間
  • Pipe 到telnet建立連接
  • Pipe 服務器響應一個循環,一次一行
  • 捕獲並回顯第一行后,按名稱終止sleep命令,這將結束子shell,從而結束 telnet 連接
  • 2>/dev/null使用sleep命令在終止時打印的“終止”消息

這很好用,除了那個killall sleep 這將殺死該用戶控制下的每個sleep實例。 大多數時候這不會是一個問題,但其他時候這將是一個嚴重混亂的錯誤來源。

我怎樣才能在需要時殺死sleep (或整個子外殼),而不會造成附帶損害?

如果您有bash您可以嘗試使用協同處理:

coproc telnet "$SERVER_ADD" "$SERVER_PORT" 
echo '{"op":"get","path":"access"}' >&${COPROC[1]}
result=
while IFS= read -r line
do    result+="$line
"
done <&${COPROC[0]}

coproc bash 命令啟動一個進程,並創建一個名為COPROC的數組,其中包含兩個文件描述符,即進程的標准輸入和標准輸出。 然后,您可以隨意書寫和閱讀它們。 如果您的 telnet 在一個命令后沒有結束,您可能需要發送第二個命令(例如echo 'exit' >&${COPROC[1]} )來斷開連接。

您可以嘗試在運行 telnet 時用任意文件上的flock命令替換睡眠。 然后第二個flock代替睡眠將掛起。 第三個flock -u可以隨時釋放鎖。

雖然典型的用法是flock lockfile command ,但這個腳本利用了flock filedescriptor的用法。 這是因為flock -u只能用於文件描述符,並且沒有命令(添加命令會強制將文件描述符arg 用作文件名)。

通過只打開一次鎖定文件,並為其分配文件描述符 5(任意), flock 5將鎖定它, flock -u 5將解鎖它。

result=$(
    exec 5>mylock
    flock 5
    ( echo '{"op":"get","path":"access"}'; flock mylock true ) |
    telnet "$SERVER_ADD" "$SERVER_PORT" |
    while read line; do
        echo "$line"
        flock -u 5
    done 
) 2>/dev/null

請參閱此其他問題以獲取對flock -u的簡單測試。

您可以嘗試使用 fifo 將數據發送到 telnet,這樣您就可以在需要的時間關閉它。

rm -f myfifo
mkfifo myfifo
result=$(
    telnet "$SERVER_ADD" "$SERVER_PORT"  <myfifo  |
    (  echo '{"op":"get","path":"access"}'  >&5
       while read -r line; do
          echo "$line"
          exec 5>&-
       done
    ) 5>myfifo
)

語法5>myfifo (無空格)打開一個新的 output 文件描述號 5 寫入 fifo。 echo >&5寫入這個 fd。 exec 5>&-關閉這個 fd。 這種語法應該在ash中工作。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM