cmd
cmd copied to clipboard
error-output cannot be retrieved from uiop's condition object
I'm not sure if this is something that can be worked around in cmd
, but it is not possible to get at the process-info of a condition being signalled with the synchronous uiop:run-program. My idea was that I could pass an :error-output
string/stream, and get at it when handling a condition, but by that point the process has already exited, and uiop has cleaned up its state.
I guess uiop does something similar to (run-program) (when error (cleanup) (signal)))
, so when the subprocess-error condition is signalled, its process
slot is already nil.
Is there any way to work around this? I really would like to get the stderr output in a condition handler.
Simple example:
(let ((s (make-string-output-stream)))
(handler-case (uiop:run-program "exit 1" :error-output s)
(uiop/run-program:subprocess-error (c) c)))
(inspect *)
This works for me:
(let ((s (make-string-output-stream)))
(handler-case (uiop:run-program "echo something >&2; exit 1" :error-output s)
(uiop/run-program:subprocess-error (c)
(print (get-output-stream-string s)))))
Of course. s
is in the lexical environment of that condition handler, but in the case that it's not, when the condition is being handled around a caller of the function calling uiop:run-program
, this is where the issue is most problematic.
(in-package :supplier-package)
(defun foo (string)
(let ((s (make-string-output-stream)))
(cmd:$cmd "echo" string :error-output s)))
(in-package :client-package)
(defun some-user-function ()
(handler-case (supplier-package:foo "hello")
(uiop/run-program:subprocess-error (c)
;; print or resignal an error using stderr output here
)))
I've pushed up a feature to allow overriding the null device for output and error output. E.g.:
(defun foo (string)
(cmd:$cmd "bash -c 'echo $0; echo busted >&2; exit 1'" string))
(defun some-user-function ()
(let ((cmd:*null-error-output* (make-string-output-stream)))
(handler-case (foo "hello")
(uiop/run-program:subprocess-error ()
(princ (get-output-stream-string cmd::*null-error-output*))
;; print or resignal an error using stderr output here
))))
I agree it would be better if when there was an error we got back an error object containing the stderr. I have to think more about how to do this in a way that (1) doesn't store unbounded amounts of error output, (2) works with launch-program's handling of error output (e.g. :if-error-output-does-not exist
) and (3) works even if the user has explicitly redirected stderr.
I have a branch up (https://github.com/ruricolist/cmd/tree/stderr-temporary) that partially handles this by storing error output in a temporary file if no stderr is specified. It doesn't "tee" the output yet though.