Executing a shell command from Common Lisp
Asked Answered
V

8

27

How can i execute a shell (bash) command within a Common Lisp program and assign the output to a variable?

Valli answered 19/5, 2011 at 22:16 Comment(0)
S
13

ASDF provides a RUN-SHELL-COMMAND that works with many Common Lisp implementations including ABCL, Allegro CL, CLISP, Clozure CL, ECL, GCL, LispWorks, SBCL, CMU, XCL and SCL.

It takes a control string and a list of arguments like FORMAT, and synchronously executes the result using a Bourne-compatible shell. Capture output by binding an optional stream.

Schliemann answered 20/5, 2011 at 0:9 Comment(6)
Keep in mind, from the ASDF documentation for run-shell-command: "This function is obsolete and present only for the sake of backwards-compatibility: “If it's not backwards, it's not compatible”. We strongly discourage its use. Its current behavior is only well-defined on Unix platforms (which include MacOS X and cygwin). On Windows, anything goes. The following documentation is only for the purpose of your migrating away from it in a way that preserves semantics."Clinometer
if it is deprecated, whats an alternative?Fellows
According to the documentation, run-program is preferred over run-shell-commandMagistrate
You can find the run-program documentation here and simple usage examples here.Stelle
the alternative seems to be uiop run-program (sync) and launch-program (async).Wiredraw
@Fellows as long as you're only going to programming on POSIX systems then there's nothing wrong with run-shell-command. It's only considered deprecated because it doesn't work on Windows.Yacano
P
13

ITA has released inferior-shell under their QITAB umbrella project.

Some links of possible interest :

A git repository is currently hosted at common-lisp.net :

git clone git://common-lisp.net/projects/qitab/inferior-shell.git
Polytrophic answered 30/5, 2013 at 5:21 Comment(3)
In addition to the git repo, :inferior-shell is installable through quicklisp.Clinometer
After reading the posted answers here, I investigated and switched to Inferior-shell because it seems to work on both Linux and Windows, whereas Trivial-shell doesn't seem to work on Windows.Pensionary
Note for better understanding: inferior-shell uses uiop's function run-program (which is synchronous, launch-program is async).Wiredraw
T
8

You can consider using Trivial-shell (url)

(trivial-shell:shell-command "echo foo")

shell-command returns output, so you can assign it to a variable.

In asdf.lisp file you can read:

;;;; We probably should move this functionality to its own system and deprecate

;;;; use of it from the asdf package. However, this would break unspecified

;;;; existing software, so until a clear alternative exists, we can't deprecate

;;;; it, and even after it's been deprecated, we will support it for a few

;;;; years so everyone has time to migrate away from it. -- fare 2009-12-01

Tagalog answered 20/5, 2011 at 6:44 Comment(1)
According to the trivial-shell page on Cliki: "NB: These days you might prefer inferior-shell for its wider support and richer interface."Clinometer
P
7

Nowadays I would use uiop:run-program, where uiop stands for "universal input output" and is a compatibility layer provided by asdf3, formerly known as asdf/driver. As has been said asdf:run-shell-command is obsolete and uiop inherits many features of other libraries such as trivial-shell.

UIOP readme

Platysma answered 13/4, 2016 at 14:59 Comment(2)
Your link is broke.Jeanniejeannine
Fixed it, thank you for the note. Btw, that was my first stackoverflow post, I wanted to do a comment first but could not because I needed something like 50 karma (now it seems to work).Platysma
A
7

In sbcl:

(sb-ext:run-program "/bin/sh" (list "-c" "whoami") :input nil :output *standard-output*)

It works fine for me:)

Ayr answered 1/10, 2016 at 23:59 Comment(0)
B
5

Some CL implementations have built-in functions for this purpose. For example, SBCL has sb-ext:run-program, and CCL has run-program.

Bursar answered 30/5, 2013 at 7:1 Comment(0)
H
3

This (appupdate.cl) program is an example of creating and executing a shell script using the Steel Bank Common Lisp (sbcl) implementation, which assumes you have sbcl installed and its in your path.

I wrote this on Ubuntu 14.04 as a simple way to perform the automation of the updating, upgrading, and kernel upgrading of the app/system software.

#!/usr/local/bin/sbcl --script
(with-open-file (str "/home/geo/update.sh"
                     :direction :output
                     :if-exists :supersede
                     :if-does-not-exist :create)
  (format str "#! /bin/bash~%~%apt-get update~%~%apt-get upgrade -y~%~%apt-get dist-upgrade -y~%~%exit~%))
(sb-ext:run-program "/bin/chmod" '("+x" "/home/geo/update.sh")
    :output *standard-output*)
(sb-ext:run-program "/bin/bash" '("/home/geo/update.sh")
    :output *standard-output*)
(sb-ext:run-program "/bin/rm" '("-rf" "/home/geo/update.sh")
    :output *standard-output*)

So of course it creates a shell script entitled update.sh, which is directed to /bin/bash via shebang (#!). After doing so the sb-ext:run-program built directs a shell to execute /bin/chmod passing the flag "+x" as an argument and the /path/to/the-file. This function changes the mode of access of the file to executable (changes the permissions).

Next, a shell is open and executes /bin/bash and the bash binary is passed the argument of the executable shell scripts file location.

Lastly the file is removed from the working directory (note in this case the appupdate.cl is in my home directory therefore is the working directory).

The appupdate.cl file can be executed from the command line after it is changed to executable and temporary root privileges are gained:

:~$ chmod +x appupdate.cl

:~$ sudo bash

:~# ./appupdate.cl

:~# exit

Easily enough the sudo command could be added to the script (e.g. sudo apt-get update) and using the sudo bash sequence would not be necessary.

NOTE: In the LispWorks ide on 14.04 the (sys:run-shell-command "") is still applicable even though it has sort of become a 'legacy' function.

Hexagram answered 29/5, 2015 at 21:23 Comment(0)
K
2

I tried out some answers but it was not straightforward. This is what worked easily:

(ql:quickload "external-program")
;; run shell command e.g. "ls -l" and capture the output into string *output*
(defparameter *output* 
              (with-output-to-string (out) 
                (external-program:run "ls" '("-l")  ; command with parameters as list of strings
                                      :output out)))
;; and after that, you can write functions to parse the output ...

This is from Edi Weitz's book Common Lisp Recipes which belongs to the shelve of any serious Lisp programmer, in my view...

Kittykitwe answered 13/10, 2018 at 6:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.