1

Currently I'm using make-process and it's stderr argument which is a buffer.

After the process has exited I'd like to check if the buffer-length is zero (no error output).

This doesn't work as I get an unhelpful Process *my-process* stderr finished.

Is there a way to get the stderr output of a process without additional text?

(besides searching for the string literal and stripping it out which seems over-complicated).


To give some context, I want to treat any error output as an error for the command I'm calling, as the exit-code isn't always a reliable way of checking if an error occurred.

1
  • Note that using cl-letf to override internal-default-process-sentinel can be used to remove the extra text (./test/lisp/net/tramp-tests.el in emacs repo does this), although I'd rather not have to override internal functions if there is a cleaner way to resolve.
    – ideasman42
    Apr 24, 2022 at 4:18

2 Answers 2

2

When you pass :stderr to make-process, it creates a pipe process to filter standard error. That pipe process has its own sentinel, which isn't set explicitly so defaults to internal-default-process-sentinel. That function inserts the text Process *my-process* stderr finished. As of Emacs 26.3, I can't find a way to override this: the stderr process doesn't seem to be available from Lisp.

It does seem however that the original process sentinel runs before the pipe process sentinel. (That's from an experiment on Emacs 26.3, it may well turn out to depend on the Emacs version or on other circumstances, I haven't dug into the details.) Here's a proof-of-concept that seems to work.

(make-variable-buffer-local 'process-associated-stderr-buffer)
(make-variable-buffer-local 'process-associated-stderr-buffer-size)
(defun has-empty-stderr-sentinel (process event)
  (setq process-associated-stderr-buffer-size (buffer-size process-associated-stderr-buffer)))
(defun has-empty-stderr (&rest command)
  (with-temp-buffer
    (let ((stderr-buffer (current-buffer)))
      (with-temp-buffer
    (setq process-associated-stderr-buffer stderr-buffer)
    (let ((process (make-process :name "myprocess"
                     :command command
                     :stderr stderr-buffer
                     :sentinel 'has-empty-stderr-sentinel)))
      (while (process-live-p process)
        (sit-for 0.1)))
    (= 0 process-associated-stderr-buffer-size)))))
(has-empty-stderr "ls" "/") ; → t
(has-empty-stderr "ls" "/does not exist") ; → nil

This is cumbersome enough that you might as well pass your own pipe process to make-process. Call make-pipe-process with a :sentinel argument that checks the process buffer size.

(defun sentinel-is-empty (process event)
  (when (equal event "finished\n")
    (with-current-buffer (process-buffer process)
      (when (= 0 (buffer-size))
        …))))
(with-temp-buffer
  (let ((stderr-buffer (current-buffer)))
    (let* ((stderr-process (make-pipe-process :buffer stderr-buffer
                                              :noquery t
                                              :sentinel sentinel-is-empty)))
           (process (make-process … :stderr stderr-process)))
  …)))
0

As far as I know there isn't a good way to prevent the Process *my-process* stderr finished text from being written into the the output (unless you count overriding internal functions *)

The only practical way I found to do this was to handle the stderr output in the processes sentinel function.

The length of the stderr buffer can be checked for zero, optionally its contents read into a string.

While not ideal (as it would be nice to have a way not to append the text in the first place), all things considered it seems the most practical option.


* internal-default-process-sentinel can be overridden using cl-letf or advice, however it might be best to avoid this since it may be used for necessary internal operations.

NOTE: you might think that having a sentinel would override internal-default-process-sentinel but it doesn't, the function always runs no matter what make-process takes for a sentinel argument.

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.