很多時候,在使用命令列時,我發現自己在某些輸入流指定的一堆不同實例上指定相同的操作,然後想要以某種特定的方式重新組合它們的輸出。
昨天的兩個用例:
我想查看捆綁在 PEM 檔案中的一堆 SSL 憑證的主題。
cat mypemfile.crt | (openssl x509 -noout -text | fgrep Subject:)
只顯示第一個。我需要拆分證書並對每個證書運行相同的命令,然後連接結果。
csplit
可以拆分它們,但只能拆分為檔案。這就麻煩了。為什麼我不能直接說
cat mypemfile.crt | csplit-tee '/BEGIN/ .. /END/' | on-each ( openssl x509 -noout -text | fgrep Subject: ) | merge --cat
?
我們運行一個 JupyterHub 實例,將筆記型電腦伺服器拆分為 Docker 容器。我想查看他們帶有時間戳記的日誌。對於一個容器來說這很容易:
sudo docker logs -t -f $container_id
(
-t
添加時間戳,並保持-f
管道打開,就像tail -f
。)列出所有容器的日誌很容易,並且按時間戳排序:
sudo docker ps | awk '{print $1}' | while read container_id do sudo docker logs -t $container_id done | sort
或者
sudo docker ps | awk '{print $1}' | xargs -n1 sudo docker logs -t | sort
或者
sudo docker ps | awk '{print $1}' | parallel sudo docker logs -t {} | sort
但這些都不會讓我使用該
-f
選項來觀看日誌。為什麼我不能直接使用
sudo docker ps | awk '{print $1}' | csplit-tee /./ | on-each (xargs echo | sudo docker logs -t -f) | merge --line-by-line --lexicographically
或者
sudo docker ps | awk '{print $1}' | parallel --multipipe sudo docker logs -t -f {} | merge --line-by-line --lexicographically
?
顯然,這需要
- 特定的外殼支援。也許我們需要一個獨特的「多管道」符號。
- 用於分割和合併管道的新工具(
csplit-tee
、on-each
和)。merge
- 關於如何在工具內指定任意多個輸入和輸出檔案描述符的固定約定,以便該 shell 將它們視為平行管道。
這已經做到了嗎?或者我可以應用於我的用例的等效內容?
沒有特定內核支援是否可行?我知道內核通常有一個固定的最大數量的打開文件描述符,但是一個實現可以通過不盲目地嘗試一次打開它們來解決這個問題。
知道的話可行嗎GNU並行可行嗎?
答案1
使用 GNU 並行:
cat mypemfile.crt |
parallel --pipe -N1 --recstart '-----BEGIN' 'openssl x509 -noout -text | fgrep Subject:'
未經測試:
sudo docker ps | awk '{print $1}' |
sudo parallel -j0 --lb docker logs -t -f {}
sudo docker ps | awk '{print $1}' |
sudo parallel -j0 --tag --lb docker logs -t -f {}
答案2
不,外殼不能做到這一點。管道只是從來源到目的地的流,通常在不同的進程中。
讓我們看看你的第一個例子:
cat mypemfile.crt |
(openssl x509 -noout -text | fgrep Subject:)
您希望輸入檔案沿著包含 BEGIN 和 END 的行進行分割,但cat
不關心內容,而且管道無論如何都沒有記錄分隔符號的越界指示。
您可以透過管道的來源和目標中的特定約定來實現此目的,但這會消除管道的主要優點,即能夠將程式組合為構建塊,以完成其作者未預期的任務。
openssl
在此特定範例中,修改以處理一個流中的多個憑證比修改openssl
以處理多個流並修改cat
以提供這些多個流要容易得多。