2011-12-07 119 views
40

我想輸出一些數據到一個管道,讓另一個進程對數據一行一行地做些什麼。這裏是一個玩具例子:如何避免回聲關閉FIFO命名管道? - Unix FIFO的有趣行爲

mkfifo pipe 
cat pipe& 
cat >pipe 

現在我可以進入任何我想要的,然後按Enter鍵後,我立刻看到同一行。但是,如果替代第二管道與echo

mkfifo pipe 
cat pipe& 
echo "some data" >pipe 

管道後echocat pipe&完成關閉,這樣我可以不通過管道傳遞任何更多的數據。有沒有辦法避免關閉管道和接收數據的進程,以便我可以通過管道從bash腳本傳遞多行數據,並在它們到達時對它們進行處理?

回答

38

當FIFO打開讀取時,它會阻塞調用進程(通常)。當進程打開FIFO進行寫入時,讀取器將被解除阻塞。當寫入程序關閉FIFO時,讀取過程獲得EOF(0字節讀取),除了關閉FIFO並重新打開外,沒有什麼可以做的了。因此,您需要使用一個循環:

mkfifo pipe 
(while cat pipe; do : Nothing; done &) 
echo "some data" > pipe 
echo "more data" > pipe 

另一種方法是保持某個進程打開FIFO。

mkfifo pipe 
sleep 10000 > pipe & 
cat pipe & 
echo "some data" > pipe 
echo "more data" > pipe 
+0

第二個版本則方位的工作!第一個不適合我,因爲我不想重新啓動接收數據的進程。 – user1084871

+9

您可能可以作弊,並讓'cat'通過使用:'cat pipe 3> pipe'將管道打開以便寫入。 'cat'命令不會使用文件描述符3,但是將會有一個叫做管道的FIFO,用於寫入(儘管它將在另一個文件描述符 - 可能是4上讀取它)。 –

+1

'exec> 6 pipe'沒有達到同樣的效果嗎?基本上將'pipe'分配給文件描述符6並保持寫入狀態。而不是直接寫入'pipe',你可能希望使用'>&6'寫入描述符,否則它應該保持它打開iirc – Haravikk

46

把所有你想要的報表輸出到FIFO在同一子shell:

# Create pipe and start reader. 
mkfifo pipe 
cat pipe & 
# Write to pipe. 
(
    echo one 
    echo two 
) >pipe 

如果你有一些更復雜,你可以打開管道寫入:

# Create pipe and start reader. 
mkfifo pipe 
cat pipe & 
# Open pipe for writing. 
exec 3>pipe 
echo one >&3 
echo two >&3 
# Close pipe. 
exec 3>&- 
+1

優秀的答案,並且實際上成功地保持管道在任意複雜的命令序列中打開。 – voetsjoeba

+5

你能簡單介紹一下它是如何工作的嗎?特別是最後一行:'exec 3>& - ' –

+2

@NickChammas:在第二個例子中,exec3管道打開文件描述符3寫入'pipe';兩個'echo'命令通過輸出重定向寫入管道;最後一個'exec 3>& - '是在這種情況下如何關閉一個打開的文件描述符 - 描述符3。那時候,在後臺運行的'cat'會得到一個EOF並終止。 –

1

作爲此處其他解決方案的替代方案,您可以在循環中將cat作爲您的命令的輸入:

mkfifo pipe 
(while true ; do cat pipe ; done) | bash 

現在你可以給它一個指令的時間和它不會關閉:

echo "'echo hi'" > pipe 
echo "'echo bye'" > pipe 

你必須殺死,當你想它了,當然,過程。我認爲這是最方便的解決方案,因爲它可以讓您在創建過程時指定非退出行爲。

1

我增強從喬納森萊弗勒的回答第二個版本,支持關閉管道:

dir=`mktemp -d /tmp/temp.XXX` 
keep_pipe_open=$dir/keep_pipe_open 
pipe=$dir/pipe 

mkfifo $pipe 
touch $keep_pipe_open 

# Read from pipe: 
cat < $pipe & 

# Keep the pipe open: 
while [ -f $keep_pipe_open ]; do sleep 1; done > $pipe & 

# Write to pipe: 
for i in {1..10}; do 
    echo $i > $pipe 
done 

# close the pipe: 
rm $keep_pipe_open 
wait 

rm -rf $dir 
14

您可以通過在讀寫模式下打開管道的讀取端很容易解決這個問題。讀者只有在最後一位作家關閉後才能獲得EOF。因此以讀寫方式打開它可以確保至少有一個作者。

所以,你的第二個例子更改爲:

mkfifo pipe 
cat <>pipe & 
echo "some data" >pipe 
+1

這顯然是唯一正確的答案。謝謝。 –

+1

用這種方法我不能解決如何關閉管道的問題,我只能殺死那些可能表現不一樣的貓進程。例如,如果cat實際上是一個帶有END塊的awk程序,那麼END塊在接收到SIGTERM時不會執行。 – pix