如何通過管道將命令發送到 grep 並獲取退出狀態,同時仍將命令輸出發送到 stdout 並將錯誤發送到 stderr?
具體案例:
我正在嘗試使用
curl
的 URL-v
,將詳細的調試資訊正常輸出到 stderr,將響應正文正常輸出到 stdout,但還要檢查響應中是否存在正則表達式,grep
如果未找到,則退出非零。該命令的最簡單形式是curl -v "${url}" | grep -q "${pattern}"
但
grep
消耗響應體。我試過的:
- 我看過“ Convince grep to output all lines, not just those with matches ”,它會建議如下內容:
curl -v "${url}" | grep --color -E "${pattern}|$"
但這只會突出顯示匹配的顏色,如果不存在,它不會為您提供非零退出程式碼
pattern
。 2. 我還查看了“如何獲取 grep 退出程式碼但列印所有行?”,這表明:curl -v "${url}" | tee /dev/stderr | grep -q "${pattern}"
它以退出程式碼退出
grep
並輸出響應正文;但它將響應主體輸出到標準錯誤,我想保持curl
標準錯誤和標準輸出流分開且完整。 3. 在“直接輸出到管道和標準輸出”之後,我嘗試了curl -v "${url}" | tee >(grep -q "${pattern}")
但是雖然這會輸出所有內容,並正確分離 stdout 和 stderr 流,但它會丟棄
grep
退出程式碼。目前解決方法:
到目前為止,我唯一能想到的就是將響應正文粘貼在一個臨時文件中:
curl -v "${url}" | tee /tmp/response grep -q "${pattern}" /tmp/response
這讓我得到了正確的輸出和正確的退出程式碼。
但是肯定有一種方法可以作為單個管道執行此操作,而無需臨時文件?
你的第二個答案是在正確的軌道上。嘗試
{ curl -v "${url}" | tee /dev/fd/3 | grep -q "${pattern}";} 3>&1
簡要說明:
- 將
3>&1
文件描述符 1(標準輸出)複製到文件描述符 3(沒有標準用法的最低值)。這是指整個管道(即整個命令行)的標準輸出。新文件描述符 (3) 對整個管道有效。你可以使用任何數字——至少是 9 以內的任何數字 。POSIX Shell 命令語言規範, 第 2.7 節重定向,說
…所有實現應至少支持 0 到 9 …
暗示符合 POSIX 的 shell 可能無法辨識大於 9 的數字。並且
bash(1)
說使用大於 9 的文件描述符的重定向應謹慎使用……
tee /dev/fd/3
告訴tee
寫入文件描述符 3——因此它tee
連接到管道的標準輸出。
- 您必須在此處使用與
***n***>&1
指令中使用的相同的數字。- 類似的文件名 可能不適用於非 Linux 作業系統。
/dev/fd/*n*
請注意, 這 與 的標準輸出無關,標準輸出是tee
grep
.Stéphane Chazelas指出:
grep -q
找到模式時退出,如果在此之後寫入輸出將導致tee
死亡。tee
至少 GNU 實現-p
可以忽略 SIGPIPE 並繼續寫入仍然可以獲得輸出的目標。還要注意,一些 shell 只等待管道的最後一個組件,所以在這裡,一旦grep -q
找到模式就會攜帶腳本的其餘部分……作為形式,我驗證了第一點(如果模式匹配,管道可以提前終止)1。
我提供了這個增強的解決方案來解決這兩個問題:
{ curl -v "${url}" | tee /dev/fd/3 | { grep -q "${pattern}" && cat > /dev/null;} } 3>&1
筆記:
- 如果數據流與模式不匹配,
grep
則將讀取整個流(即其輸入),因此不會出現問題。並且,在這種情況下,grep
將“失敗”,因此,由於&&
,cat
將不會執行,複合命令將從grep
;返回退出程式碼。即失敗(即不匹配)。- 如果數據流確實與模式匹配,那麼(正如 Stéphane 指出的那樣)
grep
將在匹配模式時退出,而不是讀取整個輸入(即來自 的輸出curl | tee
)。但在這種情況下,grep
將“成功”,因此,由於&&
,cat
它將執行,並且它將吸收其餘數據(直到 EOF)。這將確保能夠將整個數據流寫入管道,並且管道(即整個命令行)在處理完所有輸出tee
之前不會終止 。curl
這在技術上仍然存在問題:如果數據流與模式匹配並且
grep
“成功”,那麼管道的退出狀態將是cat
. 我不知道為什麼*(pipe)* | cat > /dev/null
會失敗,但理論上是可能的。為了防止這種情況,我提供:{ curl -v "${url}" | tee /dev/fd/3 | if grep -q "${pattern}"; then cat > /dev/null; true; else false; fi; } 3>&1
如果成功則顯式返回 true
grep
,如果失敗則顯式返回 false。1
-p
選項tee
是在版本 8.24 (2015-07-03)中添加 的。對於上述任何變體,如果您想 從其他地方管道輸出,只需在命令行末尾添加管道,就像您通常那樣:
curl
{ curl …; fi; } 3>&1 | lpr
但是,如果要將其重定向到文件,則必須在以下內容之前插入 輸出重定向
3>&1
:{捲曲...; 菲; } **>*輸出文件***3>&1
回想一下,“管道到文件”是不正確的術語。