# How to run local commands Complete [installation](installation.adoc) and initialize the local runtime before running these examples. ## Run a command in a temporary machine Choose an unused machine name. Pass commands as vectors of strings: ```clojure (require '[ol.smolvm :as smol]) (with-open [machine (smol/create {:name "command-example" :cpus 1 :memory-mib 256})] (smol/exec machine ["echo" "hello"])) ``` The result includes `:exit-code`, `:stdout`, `:stderr`, and byte arrays for both output streams. Check `:exit-code` for success. A nonzero exit code does not throw. **⚠️ WARNING**\ Closing a handle from `create` deletes the machine and its storage. The example deletes both when `with-open` ends, including after an exception. Copy any required files out before leaving the block. Commands do not run through a shell. For pipes or redirection, explicitly run a shell available in the guest: ```clojure (with-open [machine (smol/create {:name "shell-example"})] (smol/exec machine ["sh" "-c" "printf hello > /tmp/message; cat /tmp/message"] {:timeout-ms 5000})) ``` `exec` buffers all output in memory. For large output, use [streaming output](#stream-command-output). ## Transfer a file Use absolute guest paths. `file-write` creates parent directories and replaces existing content: ```clojure (with-open [machine (smol/create {:name "file-example"})] (smol/file-write machine "/workspace/message" "hello") (String. ^bytes (smol/file-read machine "/workspace/message") "UTF-8")) ``` The returned string is `"hello"`. For binary data, pass a byte array and use the returned byte array directly. If a consumer expects an input stream, use `file-read-stream`: ```clojure (with-open [machine (smol/create {:name "file-stream-example"})] (smol/file-write machine "/workspace/message" "hello") (with-open [input (smol/file-read-stream machine "/workspace/message")] (slurp input :encoding "UTF-8"))) ``` This also returns `"hello"`. The entire file is buffered before the stream is returned. It is not a bounded-memory transfer. Read errors occur during `file-read-stream`, not during later stream reads. Close the returned stream. ## Use an existing machine List machines, then connect by name: ```clojure (smol/list) (with-open [machine (smol/connect "existing-machine")] {:name (smol/machine-name machine) :result (smol/exec machine ["echo" "hello"])}) ``` Replace `"existing-machine"` with a name from the list. `connect` starts the machine if needed. Closing this handle does not stop or delete the machine. `machine-name` reads the handle without a native call. Local IDs equal machine names. This accessor does not check whether the machine still exists. To stop a machine but keep its disk, call `stop` before closing the handle. To delete the machine and its storage, call `delete`. ## Stream command output Use `process` to consume command output through Java streams. It returns a process record with live output streams and a `CompletableFuture` in `:result`. Waiting on that future needs no extra thread. ### Copy stdout as the command runs Capture stderr separately so it cannot fill an unread Java pipe: ```clojure (require '[clojure.java.io :as io]) (with-open [machine (smol/create {:name "stream-example"})] (let [process (smol/process machine ["echo" "hello"] {:err :string :timeout-ms 5000})] (try (with-open [^java.io.InputStream output (:out process)] (io/copy output System/out)) (deref (:result process) 10000 ::timeout) (finally (smol/destroy process))))) ``` The command prints `hello`. The result future supports timed dereference on both Clojure and Babashka. Failures throw `ExecutionException`. Use `ex-cause` to inspect the original failure. Timed dereference waits up to ten seconds for `{:exit n :out value :err value}`, then returns `::timeout` if no result is available. A wait timeout does not cancel the command. The `finally` block requests cancellation if the process is still running. Use `smol/destroy`, not `future-cancel`, to stop the guest command. This wait limit does not interrupt the preceding `io/copy`. The separate `:timeout-ms` option limits guest execution to five seconds. It does not limit time spent writing to an output stream. Close the returned input streams after use. The process record is not Closeable, so do not put it in `with-open`. If both outputs use the default `:stream` mode, drain them concurrently. Do not wait for the process before draining its live output streams. ### Forward both outputs If you do not need to read the output in Clojure, pass output streams: ```clojure (with-open [machine (smol/create {:name "forward-example"})] (let [process (smol/process machine ["echo" "hello"] {:out System/out :err System/err :timeout-ms 5000})] (try (deref (:result process) 10000 ::timeout) (finally (smol/destroy process) (.flush System/out) (.flush System/err))))) ``` The library does not close or flush supplied output streams. For short output, choose `:string` or `:bytes` instead. ### Consume native events Use `stream-open` when you need separate stdout, stderr and exit events. Use one reader and close the handle even after an exit event: ```clojure (with-open [machine (smol/create {:name "event-example"}) stream (smol/stream-open machine ["echo" "hello"] {:timeout-ms 5000})] (loop [] (when-let [event (smol/stream-next stream)] (case (:kind event) :stdout (.write System/out ^bytes (:data event)) :stderr (.write System/err ^bytes (:data event)) :exit (println "Exit:" (:exit-code event)) :error (throw (ex-info (:message event) event))) (recur))) (.flush System/out) (.flush System/err)) ``` Output events carry bytes. A UTF-8 character can cross event chunks, so use a streaming decoder if you need text. Use `stream-kill` to cancel a native stream command. It does not close the handle. Do not pass process records to native stream functions. Native output queues are unbounded. Consume output promptly. Streaming stdin is not supported. For staged mounts, networking and images, see [How to configure local workloads](local-workloads.adoc). For branches, checkpoints and packing, see [How to reuse and save machine state](saved-machines.adoc). See [Local API reference](api/ol-smolvm.adoc) for image configuration, mounts, ports, stream and process options, and cancellation. For smol cloud file streams, see [How to run smol cloud commands](cloud-commands.adoc).