cmucl: run-program output process exited only - lisp

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/")

Related

executing N times in bach an emacs macro

I have an Emacs macro(named/saved) in a mymacro.el file
I would like to execute N times the macro in batch mode
like this:
emacs --batch -l ~/mymacro.el test.txt -f MyFoo
Question how to add N times in the lisp mymacro.el code ?
:-)
ErgoEmacs has a good page on this. What you want to do is reference the argv variable after calling emacs with the --script option. (elt argv 0) will give you the value of the first argument you specify. (elt argv 1) will give you the second argument, etc.
For example, let's say you call the following command:
emacs --script ~/mymacro.el ~/test.txt 22
Emacs will call the script ~/mymacro.el.
(elt argv 0) will give you "~/test.txt".
(elt argv 1) will give you "22".
(elt argv 3) will give you nil.
You can also use the variable command-line-args, outlined here.
To specifically batch-execute a macro a number of times, borrowing from this page on the wiki, we can create a script to run mymacro on a file, in the file run-mymacro.el:
;; Insert the macro here with `insert-kbd-macro`
(fset 'mymacro [...])
(defun : ()
(let ((file-name (elt argv 0))
(repeats (string-to-int (elt argv 1))))
(find-file file-name)
(condition-case nil
(execute-kbd-macro (symbol-function 'mymacro) repeats)
(error nil) )
(save-buffer) )
We can then call this script as so:
emacs --script ~/run-mymacro.el ~/test.txt 22
This will open the file ~/text.txt, run your macro 22 times and save the result. The original file should be saved in a backup with ~ at the end of the filename.
This is all untested, so it may need some tweaking before it works 100%.
As far as I understand your question, is it the case that you want to run the command emacs --batch -l ~/mymacro.el test.txt -f MyFoo N times?
If so, and if you are using Linux, it is easier to just use the for-loop of Linux shell.
#!/bin/bash
for i in {1..N} # replace N by your wanted number
do
emacs --batch -l ~/mymacro.el test.txt -f MyFoo
done

How to define an alternate command for emacs eshell

I'm trying to start using eshell in place of bash within emacs, but I rely heavily on bash functions that I have written over the years. I'd like to configure eshell to invoke bash whenever a "command not found" condition occurs, in case the command in question is implemented as a bash function.
There is a variable tantalizingly named eshell-alternate-command-hook that sounds like it is made to order, but my lack of elisp skill is interfering with my success I think.
This is my best effort:
(add-hook 'eshell-alternate-command-hook 'invoke-bash t t)
(defun invoke-bash (command args)
(throw 'eshell-replace-command
(list "bash -c" command args)))
But when I test it, it doesn't work:
c:/temp $ lsd
Wrong number of arguments: (lambda (command args) (throw (quote eshell-replace-command) (list "bash -c" command args))), 1
c:/temp $
This is what I eventually came up with:
(defun invoke-bash (command)
(progn
(setq invoke-bash-cmd (concat "bash -c \"" command " " (mapconcat 'identity eshell-last-arguments " ") "\""))
(message invoke-bash-cmd)
(throw 'eshell-replace-command
(eshell-parse-command invoke-bash-cmd))))
I'm not eshell guru, but in the place where this hook is used, I see that it receives only one argument - command, that you trying to execute, so your code could look like
(add-hook 'eshell-alternate-command-hook 'invoke-bash)
(defun invoke-bash (command)
(throw 'eshell-replace-command
(list "bash -c" command)))
but it doesn't work, because you need to return elisp function, not name of command (according to documentation). If you want to run bash, then you need to return string with full path to it, but I hadn't found how to pass additional arguments to bash. Maybe you can find more in corresponding section on Emacs Wiki?

How to set the default-directory of compilation in Emacs?

I am coding OCaml under Emacs, I have one makefile in the working folder, and several sub-folders containing .ml files. If I launch M-x compile and make works fine on a buffer of makefile, but does not work on a buffer of a .ml file, it gives me an error:
-*- mode: compilation; default-directory: "..." -*-
Compilation started at Fri Jan 27 18:51:35
make -k
make: *** No targets specified and no makefile found. Stop.
Compilation exited abnormally with code 2 at Fri Jan 27 18:51:35
It is understandable because the default-directory is sub-folder which does not contain makefile. Does anyone know how to set the folder of makefile always as the default-directory of compilation?
You can call make with the right arguments:
make -C .. -k
where .. is the path to your Makefile
You can control this from within emacs by writing a function that (temporarily) sets default-directory and calls compile.
(defun compile-in-parent-directory ()
(interactive)
(let ((default-directory
(if (string= (file-name-extension buffer-file-name) "ml")
(concat default-directory "..")
default-directory))))
(call-interactively #'compile))
When using compile-in-parent-directory all ml files will be compiled in the parent directory of where they are. Of course if they are nested deeper you can change the logic to reflect that. In fact there is a version on the EmacsWiki which searches parent directories until it finds a makefile. I found this after I wrote this answer, otherwise I would have just pointed you there. sigh. The good thing about my method is that it's not specific to make so that you can use the same "trick" for other commands.
You can also change the call to compile to be non-interactive if you know exactly what you want the command to be. This would work particularly well if it's bound to a key in the appropriate mode hook.
i use a script like this which allows me to run make from any sub-directory (assuming you are in a posix-like environment). just put this script in your PATH as something like "sub_make.sh" and invoke it the same way you would invoke make:
#!/bin/bash
# search for project base
INIT_DIR=`pwd`
while [ "$PWD" != "/" ] ; do
if [ -e "makefile" ] ; then
break
fi
cd ..
done
if [ ! -e "makefile" ] ; then
echo "Couldn't find 'makefile'!"
exit 1
fi
# indicate where we are now
echo "cd "`pwd`
echo make "$#"
# now run make for real
exec make "$#"
That's what I have in some of my configs :)
(defun* get-closest-pathname (&optional (max-level 3) (file "Makefile"))
(let* ((root (expand-file-name "/"))
(level 0)
(dir (loop
for d = default-directory then (expand-file-name ".." d)
do (setq level (+ level 1))
if (file-exists-p (expand-file-name file d))
return d
if (> level max-level)
return nil
if (equal d root)
return nil)))
(if dir
(expand-file-name file dir)
nil)))
(add-hook 'c-mode-hook
(lambda ()
(unless (file-exists-p "Makefile")
(set (make-local-variable 'compile-command)
(let ((file (file-name-nondirectory buffer-file-name))
(mkfile (get-closest-pathname)))
(if mkfile
(progn (format "cd %s; make -f %s"
(file-name-directory mkfile) mkfile))
(format "%s -c -o %s.o %s %s %s"
(or (getenv "CC") "gcc")
(file-name-sans-extension file)
(or (getenv "CPPFLAGS") "-DDEBUG=9")
(or (getenv "CFLAGS") "-ansi -pedantic -Wall -g")
file)))))))
Matthias Puech has a solution involving a .dir-local file in the project root directory:
((nil . ((eval . (setq default-directory
(locate-dominating-file buffer-file-name
".dir-locals.el")
)))))
It is presumably also possible to use something like: (shell-command-to-string "git rev-parse --show-toplevel") as that innermost bit.
Not a completely general solution w.r.t makefile location, but adding this here for posterity because it solved my particular use-case.
If you use projectile and your makefile is always in the root of your project directory, then you can use projectile-compile-project.
(In my case, I wanted to lint my project, so calling (compile "flake8") would only flake from the current buffer's directory downwards, whereas what I really wanted was linting of the entire project. projectile-compile-project achieves this.)

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")

How to process input and output streams in Steel Bank Common 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)