sh

added
1.2

ns
clojure.java.shell

type
function

(sh & args)

Passes the given strings to Runtime.exec() to launch a sub-process.

Options are

:in      may be given followed by any legal input source for
clojure.java.io/copy, e.g. InputStream, Reader, File, byte[],
or String, to be fed to the sub-process's stdin.
:in-enc  option may be given followed by a String, used as a character
encoding name (for example "UTF-8" or "ISO-8859-1") to
convert the input string specified by the :in option to the
sub-process's stdin.  Defaults to UTF-8.
If the :in option provides a byte array, then the bytes are passed
unencoded, and this option is ignored.
:out-enc option may be given followed by :bytes or a String. If a
String is given, it will be used as a character encoding
name (for example "UTF-8" or "ISO-8859-1") to convert
the sub-process's stdout to a String which is returned.
If :bytes is given, the sub-process's stdout will be stored
in a byte array and returned.  Defaults to UTF-8.
:env     override the process env with a map (or the underlying Java
String[] if you are a masochist).
:dir     override the process dir with a String or java.io.File.

You can bind :env or :dir for multiple operations using with-sh-env
and with-sh-dir.

sh returns a map of
:exit => sub-process's exit code
:out  => sub-process's stdout (as byte[] or String)
:err  => sub-process's stderr (String via platform default encoding)

                user=> (use '[clojure.java.shell :only [sh]])

;; Note: The actual output you see from a command like this will look messier.
;; The output below has had all newline characters replaced with line
;; breaks.  You would see a big long string with \
 characters in the middle.
user=> (sh "ls" "-aul")

{:exit 0, 
 :out "total 64
drwxr-xr-x  11 zkim  staff    374 Jul  5 13:21 .
drwxr-xr-x  25 zkim  staff    850 Jul  5 13:02 ..
drwxr-xr-x  12 zkim  staff    408 Jul  5 13:02 .git
-rw-r--r--   1 zkim  staff     13 Jul  5 13:02 .gitignore
-rw-r--r--   1 zkim  staff  12638 Jul  5 13:02 LICENSE.html
-rw-r--r--   1 zkim  staff   4092 Jul  5 13:02 README.md
drwxr-xr-x   2 zkim  staff     68 Jul  5 13:15 classes
drwxr-xr-x   5 zkim  staff    170 Jul  5 13:15 lib
-rw-r--r--@  1 zkim  staff   3396 Jul  5 13:03 pom.xml
-rw-r--r--@  1 zkim  staff    367 Jul  5 13:15 project.clj
drwxr-xr-x   4 zkim  staff    136 Jul  5 13:15 src
", :err ""}
            
                user=> (use '[clojure.java.shell :only [sh]])

user=> (println (:out (sh "cowsay" "Printing a command-line output")))

 _________________________________ 
< Printing a command-line output. >
 --------------------------------- 
        \\   ^__^
         \\  (oo)\\_______
            (__)\\       )\\/\\
                ||----w |
                ||     ||

nil
            
                user=> (use '[clojure.java.shell :only [sh]])
nil

;; note that the options, like :in, have to go at the end of arglist
;; advantage of piping-in thru stdin is less need for quoting/escaping
user=> (println (:out (sh "cat" "-" :in "Printing input from stdin with funny chars like ' \\" $@ & ")))
Printing input from stdin with funny chars like ' " $@ & 
nil
            
                ;; sh is implemented using Clojure futures.  See examples for 'future'
;; for discussion of an undesirable 1-minute wait that can occur before
;; your standalone Clojure program exits if you do not use shutdown-agents.
            
                (sh "pwd" :dir "/home/ics/icsdev")
{:exit 0, :out "/home/ics/icsdev\
", :err ""}
            
                (require '[clojure.java.shell :as shell])
(shell/sh "sh" "-c" "cd /etc; pwd")
{:exit 0, :out "/etc\
", :err ""}
            
                ;; note that you have to split you script by whitespace 
;; that was confusing for me 
;; for example script: 
;; "terraform plan -var param1=value1 -var param2=value2 -var-file=/etc/var.tfvars"

(shell/sh "terraform" "plan" "-var" "param=value" "-var" "param2=value2" "-var-file=/etc/var.tfvars")