I am spawning a process from Common Lisp program (gnuplot). I am able to establish input and output streams for the process. However, I have a problem reading from the output. The problem is that I want to try to read from the output, and if there is nothing there, well... do nothing for example.
(Basic problem: I want to read the result of the command show term but I want to skip any other output that gnuplot might have produced before sending this command)
If I just use (read-line gnuplot-output nil :eof) and there is nothing in output stream, it will not indicate :eof (since the stream is still alive and something might appear there) and will just block until it has something to read (i.e. forever).
Is there are way to detect that there is nothing to read? At least, somehow safely time-out the attempt to read (i.e. it shouldn't pop a new line out of the stream once the time-out is reached)?
PS. I am using SBCL
Listen should tell you if there is a character available. I think you'll have to read the stream character by character rather than whole lines at a time, unless you're sure that the program never outputs an incomplete line.
Edit: A quick test (using sb-ext for running the program):
(defun test ()
(do* ((program (run-program "output-test.sh" nil
:search t
:output :stream
:wait nil))
(output-stream (process-output program)))
((not (process-alive-p program)))
(if (listen output-stream)
(loop
for char = (read-char-no-hang output-stream nil nil)
while char
do (write-char char))
(format t "No output available.~%"))
(sleep 1)))
Where output-test.sh is:
#!/bin/sh
for item in *
do
echo $item
sleep 3
done
Related
I want to read the output of a buffer after process started by comint finishes.
(comint-redirect-send-command-to-process
command-string ;; tested to work in the commint buffer
output-buffer-name ;; random text
buffer-process ;; (get-buffer-process (current-buffer))
nil ;; don't echo input back
t) ;; don't show output buffer immediatly
This sexp is evaluated in a buffer running a comint process. I want to read all the text of output-buffer-name once the process has finished.
I have tried applying the solution posted to this question: sleep in emacs lisp by adding this below the process start command:
(defun on-status-change (process status)
(message "status done"))
(set-process-sentinel buffer-process 'on-status-change)
This message does not appear in *Messages*.
There is no prompt in the output-buffer text, but I can write a function that returns t when the output is finished based on the full output text.
How can I react to the buffer finishing/changing, or how can I force comint to run this function synchronously.
The source for comint-redirect-send-command-to-process is here on line 3717
If anyone else has a similar issue, I ended up "solving" this.
(while (not comint-redirect-completed)
(sleep-for 0.01))
comint-redirect-completed’s value is nil
Documentation:
Non-nil if redirection has completed in the current buffer.
Obviously not a great solution, but I couldn't get anything else working.
I would like emacs to process some time-consuming tasks, without blocking input. For this purpose, I tried (where the insert is meant to be replaced by the time-consuming task)
(call-process "emacs" nil 0 nil "--eval=(insert \"a\")")
This works. However, when I want to pass a frame parameter, it doesn't work:
(call-process "emacs" nil 0 nil "--geometry 30x5")
Emacs says "Unknown option `--geometry 30x5".
Any ideas to use call-process to start another emacs session with refined frame size? Thanks!
It sounds like you might want async.el, which does indeed run additional instances of Emacs to carry out the specified processing.
(although I'm unsure of your requirement for a visible frame.)
Follow the link for details of all the other functionality on offer, but the simplest usage example given (which seemed like it might be applicable) is:
(async-start
;; What to do in the child process
(lambda ()
(message "This is a test")
(sleep-for 3)
222)
;; What to do when it finishes
(lambda (result)
(message "Async process done, result should be 222: %s" result)))
When using call-process, each argument to the program being started must be in a separate string - spaces don't count as separators. Your first example works, because it's a single argument, but the second example requires two parameters and should be written like this:
(call-process "emacs" nil 0 nil "--geometry" "30x5")
I have emacs + sbcl + slime installed. I have this function defined
(defun jugar ()
(let* ((nodoActual *nodo-inicial*)
(estadoActual (nodo-estado nodoActual))
(timeStart nil)
(timeEnd nil)
)
(loop while (not (es-estado-final estadoActual)) do
(setf *hojas* 0)
(setf timeStart (get-universal-time))
(setf nodoActual (decision-minimax nodoActual *profundidad* timeStart))
(setf timeEnd (get-universal-time))
(setf estadoActual (nodo-estado nodoActual))
(imprime-en-fichero estadoActual)
(format t "Hojas analizadas: ~a ~%" *hojas*)
(format t "Tiempo empleado: ~a ~%~%" time))
))
that makes a series of calls and print some variables in a loop.
The problem is when I call (jugar) from the *slime-repl sbcl* buffer, the prompt waits until (jugar) execution ends for showing all the (format …) together. I tried the same from a terminal (running sbcl) and it works well, so I guess it is something related to emacs or slime. How can I fix it?
proksid's answer mentions force-output, which is on the right track, but there are actually a few related possibilities. The documentation for force-output also describes finish-output (and clear-output, which isn't relevant for us):
finish-output, force-output, and clear-output exercise control over
the internal handling of buffered stream output.
finish-output attempts to ensure that any buffered output sent to
output-stream has reached its destination, and then returns.
force-output initiates the emptying of any internal buffers but does
not wait for completion or acknowledgment to return.
clear-output attempts to abort any outstanding output operation in
progress in order to allow as little output as possible to continue to
the destination.
If you want to guarantee that the output from the different iterations doesn't get interleaved (I don't know whether this is likely or not), you might want to consider finish-output instead of force-output. In either case, at least be aware of both options.
Add (force-output) after last (format ) call.
The specific problem I'm trying to solve is
send a command to a running telnet session
echo the result of the command with message
But the general problem is sending a command to an inferior (comint) process
and waiting for output to come back and a new prompt to appear, and return the output.
I have:
(defun dired-vlc-test ()
(interactive)
(let* ((buf (process-buffer dired-vlc-telnet-proc))
(old-max (with-current-buffer buf
(point-max))))
(telnet-simple-send dired-vlc-telnet-proc "get_time")
(accept-process-output dired-vlc-telnet-proc 5)
(message (buffer-substring-no-properties old-max (with-current-buffer buf
(point-max))))))
However the output I always get is "get_time", i.e. Emacs is not waiting for new output.
I got the accept-process-output idea from this question
accept-process-output returns too early in your case because it returns as soon as it has accepted some output, but in your case you want to keep accepting output until you get a new prompt. Notice that the remote process does not tell Emacs "here's a prompt", so you will have to tweak your process filter to recognize "Oh, we received something that looks like a prompt" and you will have to call accept-process-output in a loop until the process filter tells it (via some global variable, probably) that it has seen a prompt.
I am starting with emacs, and don't know much elisp. Nearly nothing, really.
I want to use ack as a replacement of grep.
These are the instructions I followed to use ack from within emacs:
http://www.rooijan.za.net/?q=ack_el
Now I don't like the output format that is used in this el file, I would like the output to be that of ack --group.
So I changed:
(read-string "Ack arguments: " "-i" nil "-i" nil)
to:
(read-string "Ack arguments: " "-i --group" nil "-i --group" nil)
So far so good.
But this made me lose the ability to click-press_enter on the rows of the output buffer. In the original behaviour, compile-mode was used to be able to jump to the selected line.
I figured I should add a regexp to the ack-mode. The ack-mode is defined like this:
(define-compilation-mode ack-mode "Ack"
"Specialization of compilation-mode for use with ack."
nil)
and I want to add the regexp [0-9]+: to be detected as an error too, since it is what every row of the output bugger includes (line number).
I've tried to modify the define-compilation-modeabove to add the regexp, but I failed miserably.
How can I make the output buffer of ack let me click on its rows?
--- EDIT, I tried also: ---
(defvar ack-regexp-alist
'(("[0-9]+:"
2 3))
"Alist that specifies how to match rows in ack output.")
(setq compilation-error-regexp-alist
(append compilation-error-regexp-alist
ack-regexp-alist))
I stole that somewhere and tried to adapt to my needs. No luck.
--- EDIT, result after Ivan's proposal ---
With ack.el updated to include:
(defvar ack-regexp-alist
'(("^[0-9]+:" ;; match the line number
nil ;; the file is not found on this line, so assume that it's the same as before
0 ;; The line is the 0'th subexpression (the whole thing)
)
("^[^: ]+$" ;; match a file -- this could be better
0 ;; The file is the 0'th subexpression
))
"Alist that specifies how to match rows in ack output.")
(setq compilation-error-regexp-alist
(append compilation-error-regexp-alist
ack-regexp-alist))
(define-compilation-mode ack-mode "Ack"
"Specialization of compilation-mode for use with ack."
nil)
Then checking the compilation-error-regext-alist variable, I get the value:
(absoft ada aix ant bash borland caml comma edg-1 edg-2 epc ftnchek iar ibm irix java jikes-file jikes-line gnu gcc-include lcc makepp mips-1 mips-2 msft oracle perl rxp sparc-pascal-file sparc-pascal-line sparc-pascal-example sun sun-ada 4bsd gcov-file gcov-header gcov-nomark gcov-called-line gcov-never-called
("^[0-9]+:" nil 0)
("^[^: ]+$" 0))
I find the format of the variable very strange, isn't it? I don't know elisp (yet), so maybe it's correct that way.
Still no links or color in the *ack* buffer.
There is another full-ack package up on ELPA which I have used before and handles --group output.
That said, reading the documentation for compilation-error-regexp-alist you see that it has the form:
(REGEXP FILE [LINE COLUMN TYPE HYPERLINK HIGHLIGHT...])
In the case of --group output, you have to match file and line separately, so I think you want something like (untested)
(defvar ack-regexp-alist
'(("^\\S +$" ;; match a file -- this could be better
0 ;; The file is the 1st subexpression
)
("^[0-9]+:" ;; match the line number
nil ;; the file is not found on this line, so assume that it's the same as before
0 ;; The line is the 0'th subexpression (the whole thing)
))
"Alist that specifies how to match rows in ack output.")
-- Updated --
The variable compilation-error-regext-alist is a list of symbols or elements like (REGEXP ...). Symbols are looked up in compilation-error-regexp-alist-alist to find the corresponding elements. So yes, it is a little weird, but it's easier to see what's turned on and off without having to look at ugly regexes and guess what they do. If you were going to distribute this I would suggest adding the regex to compilation-error-regexp-alist-alist and then turning it on in compilation-error-regext-alist, but that is somewhat moot until you get it to work correctly.
Looking more closely at ack.el, I notice that it uses
(let (compile-command
(compilation-error-regexp-alist grep-regexp-alist)
...)
...
)
In other words it locally overwrites compilation-error-regexp-alist with grep-regexp-alist, so you need to add the regexes there instead. Or even better might be to replace it with
(let (compile-command
(compilation-error-regexp-alist ack-regexp-alist)
...)
...
)
In the end I still recommend full-ack since the filename regex does not seem to be working correctly. It seems more complete (though more complicated), and I have been happy with it.