report exits 0 when stdout is closed, instead of failing the write #30
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
sfdupes report >&-— stdout closed outright rather than redirected — exits 0. README §Error handling lists "stdout write failure" as an exit-1 fatal condition, so this is a straight spec violation.The mechanism makes it worse than a wrong exit code. With fd 1 closed at exec, the next file the process opens is handed fd 1. The database is opened early, so report output can be written into the database file descriptor rather than to a pipe. The command then reports success.
The general case matters more than the closed-fd edge case:
sfdupes report | head -5producesEPIPEon the writes that followheadexiting, and a full disk producesENOSPC. Neither is currently detected, so a truncated report is indistinguishable from a complete one — for a tool whose entire product is a machine-readable list an operator acts on, silently losing rows is the worst failure mode available.Found during the implementation of #4 and deliberately left out of that PR: it predates the change, is unrelated to database lifetime, and belongs on its own.
Definition of done
reportandtrees— including the header line, every row, and the final flush if buffering is introduced.EPIPEis handled deliberately and documented. Decide whethersfdupes report | headis an error or an expected early exit, then make the code and README agree; the common Unix convention is to die quietly onSIGPIPE/EPIPErather than treat it as an error, but README currently says otherwise and one of the two must move.Flushreintroduces the same bug one layer up.reportandtreeswith stdout closed assert exit 1 and a stderr diagnostic; a test writing to a deliberately failing writer asserts the error propagates.EPIPEdecision changes the documented contract.make checkandmake dockergreen.