CLISP ext:run-program

647 views Asked by At

I am having trouble using run-program with arguments. The documentation at http://www.clisp.org/impnotes/shell.html is very incomplete for a newbie like me, and I would need to see some examples.

In particular, how can I get the directory list of files with "txt" extension?

This works:

(ext:run-program "ls" ) ; I am running on Mac OS X

but if I add arguments, it doesn't work. I have tried:

(ext:run-program "ls" :arguments "*.txt")
(ext:run-program "ls" :arguments '(*.txt))
(ext:run-program "ls *.txt)

Can anyone tell me the right syntax and, hopefully, provide some more examples of run-program?

Note that the above is just an example. What I want is to be able to use run-command, not to list the directory, which I can do with list-directory.

Thanks for any help.

2

There are 2 answers

3
sds On

List Files

First of all, if all you want is to list files, you can use the standard directory or CLISP-specific ext:dir.

Globbing

Second, the "*" globbing is expanded by shell, not individual commands, so you need to use ext:run-shell-command instead of ext:run-program:

> (ext:run-shell-command "ls *.txt")
foo.txt

> (ext:run-program "ls" :arguments '("foo.txt"))
foo.txt

> (ext:run-program "ls")
foo.txt
bar.c

Summary

The manual assumes a certain level of familiarity with the way modern OSes work in this respect. The other excellent answer addresses those issues in detail; briefly:

  • run-program executes another program using the standard fork/exec paradigm
  • run-shell-command goes through shell, so you can use globbing and things like loops
  • shell is mostly for interactive use

More examples

You can find more example of run-program in the CLISP test suite.

If you want to read from a compressed file, you can do this:

;; return lines as a list
(with-input-from-file (in (ext:make-pipe-input-stream "gunzip -c myfile.gz"))
  (loop for l = (read-line in nil nil)
    while l
    collect l))

Actually, you can use CLISP as your shell!

0
Basile Starynkevitch On

Try something like

(ext:run-program "stat" :arguments '("/tmp" "/sys"))

I am using stat, not ls in this example.

What you might want is globbing (read glob(7)). You should do it yourself in Lisp, or else run /bin/sh -c; in other words it is up to you to build the expanded list passed after :arguments. (You can't pass *.txt as a single argument, it would have the same effect as a quoted shell argument like in
ls '*.txt' shell command)

This is not CLISP specific. ext:run-program is probably forking then calling (in the child) execve(2) so you have to do the globbing yourself.

Maybe you want the CLISP equivalent of glob(3) or fnmatch(3). I don't know enough CLISP if it has them or not.

Read Advanced Linux Programming to get a more clear picture of this. On POSIX systems the invoking process (often your shell when typing a command, or your Common Lisp program if using ext:run-program ....) has to expand and build the argument list. The point is that when you run ls *.txt it is your shell (not the ls process) which is expanding *.txt, so ls is getting an expanded argument list like e.g. a.txt foo.txt bar.txt; so you need to do the same in your Lisp code.