How to process input and output streams in Steel Bank Common Lisp? - lisp

I'm trying to figure out how to use the output stream of one program I start with RUN-PROGRAM so it can be used as the input of another program started with RUN-PROGRAM (i.e., the moral and perhaps literal equivalent of piping). I've tried using a number of combinations of the :INPUT, :OUTPUT and :WAIT keyword arguments, but nothing I've hit
upon has been productive so far. Any tips would be helpful; for example, how would I go about doing something like ls | grep lisp from the shell?
One of my attempts is
(defun piping-test ()
(let ((grep-process (run-program "/usr/bin/grep" '("lisp")
:input :stream
:output :stream)))
(unwind-protect
(with-open-stream (s (process-input grep-process))
(let ((ls-process (run-program "/bin/ls" '()
:output s)))
(when ls-process
(unwind-protect
(with-open-stream (o (process-output grep-process))
(loop
:for line := (read-line o nil nil)
:while line
:collect line))
(process-close ls-process)))))
(when grep-process (process-close grep-process)))))
Running this in a SLIME REPL causes everything to hang until I break
with C-c C-c, so it's pretty obviously not the right thing, but I'm
not sure how to change it so it is the right thing.
EDIT: Adding :WAIT NIL to both RUN-PROGRAM invocations, or to only the invocation for grep, doesn't do the trick. In that case, the function will hang, and breaking with C-c C-c gets a stack trace indicating that there's a local function (defined via FLET) called SB-UNIX:SELECT that has hung.

I got a working answer from Raymond Toy on comp.lang.lisp. His solution was for CMUCL, but it worked with the essentially identical RUN-PROGRAM function on the closely related SBCL, and with minor changes it will work on CCL as well, because CCL's RUN-PROGRAM is basically a clone of the one from CMUCL/SBCL.
The secret, as it were, is to set up the ls process first, and then provide its output stream to the grep process as input, like so:
(defun piping-test2 ()
(let ((ls-process (run-program "/bin/ls" '()
:wait nil
:output :stream)))
(unwind-protect
(with-open-stream (s (process-output ls-process))
(let ((grep-process (run-program "/usr/bin/grep" '("lisp")
:input s
:output :stream)))
(when grep-process
(unwind-protect
(with-open-stream (o (process-output grep-process))
(loop
:for line := (read-line o nil nil)
:while line
:collect line))
(process-close grep-process)))))
(when ls-process (process-close ls-process)))))
I also experimented with omitting the :WAIT NIL argument from the RUN-PROGRAM call for ls, and it worked just as well.

Try adding :wait nil to your arguments to run-program. That should have both your grep and your ls running in the background. As is, you're starting the grep process, waiting for that to finish, then starting the ls you're intending to feed into the grep process. Alas, since you're waiting for the grep to finish, you never get that far.

Relatedly, but perhaps not spot on to your question, you could do:
(with-output-to-string (s)
(ccl:run-program "sh" (list "-c" "ls a/directory/somewhere/*.lisp") :output s)
s)
or
(with-output-to-string (s)
(ccl:run-program "sh" (list "-c" "ls /a/directory/somewhere/*.lisp | wc -l") :output s)
s)
or
(with-output-to-string (s)
(ccl:run-program "ssh" (list "a-user#some-ip" "sh -c ls /a/directory/somewhere/on/remote/server/*.lisp | wc -l") :output s)
s)
And, of course, you can use
(format nil "ls ~a" directory)
To get input, you can do something like:
(with-output-to-string (out)
(format t "~%Enter your sudo password:~%")
(with-input-from-string (s (read))
(ccl:run-program "ssh" (list *remote* "sudo cat /etc/init.d/nginx") :input s :output out))
out)

Related

cmucl: run-program output process exited only

I'm on debian stretch and install CMUCL from the stable repository. When trying the following
(run-program "sh" '("ls") :output T)
I get
#<process 2845 :EXITED>
without the expected output. Any help please?
Read ls(1) and dash(1) & bash(1). Notice that POSIX /bin/sh is often a symlink to one of them (often dash, sometimes bash). Be aware of the role of the PATH variable (e.g. in shells and for execvp(3)). And system(3) is using /bin/sh with -c, and your sh (assuming it is a POSIX shell) needs -c to interpret a string as a command.
You could try
(run-program "/bin/ls" nil :output t)
or
(run-program "/bin/sh" '("-c" "ls") :output t)
You probably don't have ls in your working directory (but somewhere else in your PATH; on POSIX it should be /bin/ls) and /bin/ls is not a shell script, but some ELF executable (on Linux; on most Unixes it is some executable and is not a script). So /bin/sh ls (or sh ls) can't work as you want it to.
To run ls there is no need to start a shell! And you might not need to fork a ls process for that purpose (of listing files), because you could call opendir(3) readdir(3) stat(2) closedir(3) as FFI (or find some package doing that).
PS. I have used SBCL to test these.
In CMUCL
(run-program "ls" '("-l") :input t :output t)
will work. This works fine, too:
(run-program "ls" '("-l" "-h") :input t :output t)
and if you need to produce a simple-string, to add it as an argument:
(run-program "ls"
`("-l" ,(format nil "~A" path-name))
:input t
:output t)
in that case you must use backquote (backtick) and comma.
It is also possible to do:
(defun my-ls (file)
(run-program "ls"
`("-l" "-h" ,file)
:input t
:output t))
now you have:
(my-ls "/home/user/")

Launching shell command in Eshell without prompt interfence

I'm trying to customize my eshell to intercept python to do two things:
Open a new window and run python if no arguments are given (e.g., $ python)
Run the command "per usual" if arguments are given (e.g., $ python foobar.py)
So far I have something like this
(defun eshell/python (&rest cmd-args)
(if (not cmd-args)
(progn (run-python "python")
(select-window (split-window-below))
(switch-to-buffer "*Python*")
(balance-windows)
nil)
(message "Use '*python' to call python directly")))
I've tried replacing (message ...) with a few different things:
Based on the ouput of eshell-parse-command "python test.py" I tried
(progn (eshell-trap-errors
(eshell-named-command "python" cmd-args)))
but it hits a recursion limit.
Since *python test.py does what I want, I then tried
(progn (eshell-trap-errors
(eshell-named-command (concat "*" "python") cmd-args)))
but that puts the python process in the background and interrupts stdout with the output of my eshell-prompt-function.
Finally, I've fiddled with shell-command but I can't get it to write to the eshell buffer. In particular,
(progn (eshell-trap-errors
(shell-command (mapconcat (lambda(x) x) cmd-args " ")
(get-buffer "*eshell*") (get-buffer "*eshell*"))))
gives me a Text is read only message and moves point to start of the eshell buffer.
Is what I'm looking for possible?
Edit 1
Running without eshell/python defined, I've instead tried to avoid alias problems:
(defun eshell/gvr (&rest cmd-args)
(if (not cmd-args)
(progn (run-python "python")
(select-window (split-window-below))
(switch-to-buffer "*Python*")
(balance-windows)
nil)
(progn (eshell-trap-errors
(eshell-named-command "python" cmd-args)))))
If test.py is
print "Hello World"
x = raw_input("What should I repeat? ")
print x
running gvr test.py in eshell fails when I reply to the prompt because eshell tries to execute the input instead of handing it to python, but running python test.py goes off without a hitch.
How can I get run my own subprocesses in eshell the same way that they happen by default?

Run commands in Emacs asynchronously, but display output incrementally

I have a utility function:
(defun execute-in-buffer (command-with-args buffer)
"Execute a string COMMAND-WITH-ARGS representing a shell command with arguments,
inserting the results in BUFFER."
(switch-to-buffer buffer)
(insert (format ">>> %s\n" command-with-args))
(let* ((command-args-list (s-split " " command-with-args))
(command (car command-args-list))
(args (cdr command-args-list)))
(apply 'call-process command nil buffer t args)))
This allows me to do things like (execute-in-buffer "ls /" (get-buffer-create "*my-output*"). However, it's not well suited for slow commands. If I call a series of slow commands, I don't get any output until the very end:
(let ((buf (get-buffer-create "*my-output")))
(execute-in-buffer "sleep 10" buf)
(execute-in-buffer "ls /" buf))
I want to be able to call synchronously, so the next command only runs after the previous one finishes. However, I want to see the output from my commands as they run. How would I do this?
(Example code is just for show, I'm happy to drop it in favour of something else.)
Using synchronous processes
If you want to stick with synchronous processes (e.g. using call-process), you need to call (redisplay) after each call to execute-in-buffer for the display to be updated and the output to be visible (also see this question for more details). However, the output of each command will not be visible until the process terminates, and emacs will hang while the external processes are running.
Using asynchronous processes
Using asynchronous processes is a bit more complicated, but avoids hanging Emacs while the commands are running, which also solves the redisplay issue. The tricky part here is to sequentially chain all commands. Here is a bit of elisp which should do the trick:
(defun execute-commands (buffer &rest commands)
"Execute a list of shell commands sequentially"
(with-current-buffer buffer
(set (make-local-variable 'commands-list) commands)
(start-next-command)))
(defun start-next-command ()
"Run the first command in the list"
(if (null commands-list)
(insert "\nDone.")
(let ((command (car commands-list)))
(setq commands-list (cdr commands-list))
(insert (format ">>> %s\n" command))
(let ((process (start-process-shell-command command (current-buffer) command)))
(set-process-sentinel process 'sentinel)))))
(defun sentinel (p e)
"After a process exited, call `start-next-command' again"
(let ((buffer (process-buffer p)))
(when (not (null buffer))
(with-current-buffer buffer
;(insert (format "Command `%s' %s" p e) )
(start-next-command)))))
;; Example use
(with-current-buffer (get-buffer-create "*output*") (erase-buffer))
(execute-commands "*output*"
"echo 1"
"sleep 1"
"echo 2; sleep 1; echo 3"
"ls /")
This works for me:
(async-shell-command "echo 1; sleep 10; echo 2; sleep 10; ls /" "*abcd*")
This can be adapted to do what you need?

How can I improve this Emacs lisp function?

The intent is to use git grep as the command for M-x grep, and all the buffer benefits that come along with it. Desired functionality:
It reads the word/thing at point as the default value (done, sort of)
It reads the current region as the default argument if a region is set.
The following is the code I have so far:
(defun bw-read-string-at-point ()
(interactive)
(let ((word (word-at-point)))
(set-text-properties 0 (length word) nil word)
word))
(defun bw-git-grep (search-str)
"Uses `git-grep` to find `search-str`"
(interactive
(list
(read-string (format "Search for (%s): " (bw-read-string-at-point)))))
(let ((search-str (if (= (length search-str) 0)
(bw-read-string-at-point) search-str)))
(grep (concat "git --no-pager grep -i -I -nH --no-color --extended-regexp " search-str))))
I feel like the interactive bit there is quite clumsy and could be made much better.
Actually, it looks pretty good. Except you should use the default' argument ofread-string, and the interactive in bw-read-string-at-point should not be there. Or better yet, just use grep-tag-default.
Here's how I'd tweak it:
(defun bw-git-grep (search-str)
"Uses `git-grep` to find `search-str`"
(interactive
(let ((default (grep-tag-default)))
(list
(read-string (format "Search for (default %s): " default)
nil nil default))))
(grep (concat "git --no-pager grep -i -I -nH --no-color --extended-regexp " search-str)))
I would use read-from-minibuffer instead of read-string:
(defun bw-git-grep (pattern)
(interactive
(list
(read-from-minibuffer
"Search for: "
(if (region-active-p)
(buffer-substring-no-properties (region-beginning) (region-end))
(thing-at-point 'word)))))
(let* ((grep-command "git --no-pager grep -i -I -nH --no-color --extended-regexp ")
(command (concat grep-command pattern))
(grep-use-null-device nil))
(grep command)))
Also, you probably need to ensure that grep-use-null-device is nil to avoid grep appending /dev/null to your command (which git doesn't seem to like much)

running shell commands with gnu clisp

I'm trying to create a "system" command for clisp that works like this
(setq result (system "pwd"))
;;now result is equal to /my/path/here
I have something like this:
(defun system (cmd)
(ext:run-program :output :stream))
But, I am not sure how to transform a stream into a string. I've reviewed the hyperspec and google more than a few times.
edit: working with Ranier's command and using with-output-to-stream,
(defun system (cmd)
(with-output-to-string (stream)
(ext:run-program cmd :output stream)))
And then trying to run grep, which is in my path...
[11]> (system "grep")
*** - STRING: argument #<OUTPUT STRING-OUTPUT-STREAM> should be a string, a
symbol or a character
The following restarts are available:
USE-VALUE :R1 Input a value to be used instead.
ABORT :R2 Abort main loop
Break 1 [12]> :r2
Something like this?
Version 2:
(defun copy-stream (in out)
(loop for line = (read-line in nil nil)
while line
do (write-line line out)))
(defun system (cmd)
(with-open-stream (s1 (ext:run-program cmd :output :stream))
(with-output-to-string (out)
(copy-stream s1 out))))
[6]> (system "ls")
"#.emacs#
Applications
..."
Per the CLISP documentation on run-program, the :output argument should be one of
:terminal - writes to the terminal
:stream - creates and returns an input stream from which you can read
a pathname designator - writes to the designated file
nil - ignores the output
If you're looking to collect the output into a string, you'll have to use a read-write copying loop to transfer the data from the returned stream to a string. You already have with-output-to-string in play, per Rainer's suggestion, but instead of providing that output stream to run-program, you'll need to write to it yourself, copying the data from the input stream returned by run-program.
You are asking specifically about clisp. I'll add here that
if you are using Clozure CL then you can also easily run os subprocesses.
Some examples:
;;; Capture the output of the "uname" program in a lisp string-stream
;;; and return the generated string (which will contain a trailing
;;; newline.)
? (with-output-to-string (stream)
(run-program "uname" '("-r") :output stream))
;;; Write a string to *STANDARD-OUTPUT*, the hard way.
? (run-program "cat" () :input (make-string-input-stream "hello") :output t)
;;; Find out that "ls" doesn't expand wildcards.
? (run-program "ls" '("*.lisp") :output t)
;;; Let the shell expand wildcards.
? (run-program "sh" '("-c" "ls *.lisp") :output t)
Do a search for run-program in the CCL docs located here: http://ccl.clozure.com/ccl-documentation.html
There are a couple nice Lisp ways of doing this in this stackoverflow answer: Making a system call that returns the stdout output as a string Once again, Rainer to the rescue. Thanks Ranier.
This is a shorter one
(defun system(cmd)
(ext:shell (string cmd)))
> (system '"cd ..; ls -lrt; pwd")