4

Suppose you have this script, on an Ubuntu machine:

#!/bin/env python3 import sys import time try: i = 1 while True: print(i) i += 1 except Exception as e: sys.stderr.write(f"We caught an exception {e!r}\n") sys.stderr.flush() while True: sys.stderr.write("Sleeping for a minute\n") sys.stderr.flush() time.sleep(60) 

And you run it like this:

# ./some_test.py | head -n 10 1 2 3 4 5 6 7 8 9 10 We caught an exception BrokenPipeError(32, 'Broken pipe') Sleeping for a minute Sleeping for a minute . . . 

And it will not stop, of course. Is there a way in which, from the outside, we can see that the output stream is closed? I checked with lsof and and also fds in /proc and I do not see anything that makes me think that the stream is closed even though I know that the pipe is broken and the exception when it tried to write also says so:

# lsof -p 28466 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME python3 28466 root cwd DIR 252,3 4096 132 /root python3 28466 root rtd DIR 252,3 4096 128 / python3 28466 root txt REG 252,3 5904904 9540 /usr/bin/python3.10 python3 28466 root mem REG 252,3 5712144 33793209 /usr/lib/locale/locale-archive python3 28466 root mem REG 252,3 2220400 50352186 /usr/lib/x86_64-linux-gnu/libc.so.6 python3 28466 root mem REG 252,3 108936 50422136 /usr/lib/x86_64-linux-gnu/libz.so.1.2.11 python3 28466 root mem REG 252,3 194872 50417841 /usr/lib/x86_64-linux-gnu/libexpat.so.1.8.7 python3 28466 root mem REG 252,3 940560 50413373 /usr/lib/x86_64-linux-gnu/libm.so.6 python3 28466 root mem REG 252,3 27002 50386759 /usr/lib/x86_64-linux-gnu/gconv/gconv-modules.cache python3 28466 root mem REG 252,3 240936 50359529 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 python3 28466 root 0u CHR 136,3 0t0 6 /dev/pts/3 python3 28466 root 1w FIFO 0,13 0t0 110475 pipe python3 28466 root 2u CHR 136,3 0t0 6 /dev/pts/3 
# ls -l /proc/28466/fd total 0 lrwx------ 1 root root 64 Oct 20 13:44 0 -> /dev/pts/3 l-wx------ 1 root root 64 Oct 20 13:44 1 -> 'pipe:[110475]' lrwx------ 1 root root 64 Oct 20 13:44 2 -> /dev/pts/3 

2 Answers 2

10

The stream isn’t closed; the fact that you are able to try writing to it without receiving an EBADF error is proof of that. (See man 2 write for details.)

On Linux, given that you’re looking at a pipe, what you could do is check all running processes to see if any other has an open file descriptor to the same pipe. If you find one, then the pipe is still usable for writing without error; if you don’t then either you don’t have appropriate privileges to see the reading process, or the pipe is no longer usable for writing.

lsof can be used for that. Example:

$ sleep 3m | sleep 10 & [1] 62807 62808 $ lsof -p 62807 -ad 1 +E COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME sleep 62807 stephane 1w FIFO 0,14 0t0 806533 pipe 62808,sleep,0r sleep 62808 stephane 0r FIFO 0,14 0t0 806533 pipe 62807,sleep,1w 

Stdout of 62807 (running sleep 3m) is open on the writing end of a pipe, and there's still a process (running sleep 10) with a fd (here 0) open on the reading end.

Same command run after 10 seconds:

$ lsof -p 62807 -ad 1 +E COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME sleep 62807 stephane 1w FIFO 0,14 0t0 806533 pipe 

This time, lsof can't find any other fd open for reading on the pipe upon on fd 1 of 62807.

Without lsof, you can do the equivalent of this zsh code:

$ sleep 3m | sleep 4m & [1] 63098 63100 $ ls -ogd /proc/*/fd/*(e['[[ $REPLY -ef /proc/63098/fd/1 ]]']) l-wx------ 1 64 Oct 20 17:12 /proc/63098/fd/1 -> 'pipe:[810032]' lr-x------ 1 64 Oct 20 17:12 /proc/63100/fd/0 -> 'pipe:[810032]' 

Permissions of those symlinks indicate how the pipe is open for each process.

See also How to find out if pipe is broken?

0
3

Update: After re-reading your question, I see that you were asking about monitoring it externally. Using lsof or looking in /proc/$PID/fd/$FD for the pipe info and trying to find a matching pair elsewhere under /proc is the only way to really do that. If you don't find a reader and writer matching pair, then either one-half of the pipe has been closed or, in the special case of a named pipe, it could be that its peer has not yet opened the file. In general though, you don't normally run into this hanging a process unless the EPIPE error is being ignored for some reason such as in the original example code.

You've already received your answer. The Python exception BrokenPipeError tells you that the reader on the other end of the pipe has closed their end of the pipe. This corresponds to your Python code ultimately calling the write() system call and receiving an error with the value EPIPE. Once you receive this exception, you should assume that no more output will be accepted and finish the job. Your file descriptor will stay open until you decide to close it, but the other end has already closed their file descriptor.

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.