## Quick Guide
Command mode uses simple shell-like syntax
```
git commit -a
```
Forms starting with parenthesis are considered as Clojure code
```
(+ 1 2)
```
Glob patterns and environment variables are expanded
```
ls *.txt
ls $HOME
cd ~/Downloads
```
Use output from a command or function as arguments to other command
```
echo (+ 1 2)
echo (sh-str date)
```
Piping output between commands
```
ls | head
```
Piping with functions works similarly to `->>` threading macro
```
ls | (clojure.string/upper-case)
ls | #(clojure.string/replace % #"\.txt" ".md")
```
Use special `|>` pipe operator to split input into sequence of lines
```
ls |> (reverse) | (take 5)
```
Redirects - note that there must be spaces around redirection operators `>` and `>>`
```
ls > files.txt
echo hi >> file.txt
ls 2 > files.txt
```
Command status
```
echo hi && echo OK
! echo hi || echo FAILED
```
## Examples
Most of helper utilities can be replaced with functions on sequences.
```clojure
bash: ls | head -n 5
closh: ls |> (take 5)
bash: ls | tail -n 5
closh: ls |> (take-last 5)
bash: ls | tail -n +5
closh: ls |> (drop 4)
; Print filenames starting with "."
bash: ls -a | grep "^\\."
closh: ls -a |> (filter #(re-find #"^\." %))
; Print only odd numbered lines counting from 1
bash: ls | sed -n 1~2p
closh: ls |> (keep-indexed #(when (odd? (inc %1)) %2))
; Math
bash: echo '(1 + sqrt(5))/2' | bc -l
closh: (/ (+ 1 (Math.sqrt 5)) 2)
```
### Control flow
For loops:
```bash
for f in /sys/bus/usb/devices/*/power/wakeup; do echo $f; cat $f; done
```
```clojure
; Using doseq
(doseq [f (expand "/sys/bus/usb/devices/*/power/wakeup")] (println f) (sh cat (str f)))
```
```clojure
; Or multi pipes
ls /sys/bus/usb/devices/*/power/wakeup |> (map #(str % "\n" (sh-str cat (str %)))) | cat
```
If conditionals:
```bash
if test -f package.json; then echo file exists; else echo no file; fi
```
```clojure
echo (if (sh-ok test -f package.json) "file exists" "no file")
```
### Sequence of commands
```
bash: ls; echo hi
closh: (sh ls) (sh echo hi)
```
## Reference
### History
History gets saved to the file `~/.closh/closh.sqlite` which is a SQLite database.
Use up and down arrows to cycle through history. First history from a current session is used, then history from all other sessions is used.
If you type some text and press up then the text will be used to match beginning of the command (prefix mode). Pressing ctrl-r will switch to matching anywhere in the command (substring mode). The search is case insensitive.
While in the history search mode you can use following controls:
- enter to accept the command and execute it
- tab to accept the command but have ability to edit it
- esc cancel search keeping the initial text
- ctrl-c cancel search and resetting the initial text
To show history you can run:
```sh
sqlite3 ~/.closh/closh.sqlite "SELECT command FROM history ORDER BY id ASC"
```
For convenience you can add the following to your `~/.closhrc` file:
```clj
(defcmd history []
(sh sqlite3 (str (getenv "HOME") "/.closh/closh.sqlite") "SELECT command FROM history ORDER BY id ASC" | cat))
```
### Environment variables
There are some helper functions for doing common things with environment variables
**setenv**: set environment variable
```
setenv "ONE" "1"
=> ("1")
(setenv "ONE" "1")
=> ("1")
(setenv "ONE" "1" "TWO" "2")
=> ("1" "2")
```
**getenv**: get environment variable
```
getenv "ONE"
=> "1"
(getenv "ONE")
=> "1"
(getenv "ONE" "TWO")
=> {"ONE" "1", "TWO" "2"}
getenv
=> ;; returns a map of all environment variables
```
**source-shell**: run bash scripts and import the resulting environment variables into the closh environment
```
(source-shell "export ONE=42")
=> nil
getenv "ONE"
=> "42"
```
`source-shell` defaults to `bash` but you can use other shells:
```
(source-shell "zsh" "source complicated_script.zsh")
=> nil
getenv "SET_BY_COMPLICATED_SCRIPT"
=> "..."
```
To set a temporary variable while running a command, do this:
```
env VAR=1 command
```
Which is equivalent to bash:
```
VAR=1 command
```
#### Built-in environment variables
- `PWD` - a path to the current working directory
### Custom prompt
The prompt can be customized by defining `closh-prompt` function in `~/.closhrc` file.
For example you can use [powerline](https://github.com/banga/powerline-shell) prompt like this:
```clojure
(require-macros '[closh.zero.core :refer [sh-str]])
(defn closh-prompt []
(sh-str powerline-shell --shell bare))
```
Or you can reuse existing prompt from [fish](http://fishshell.com/) shell:
```clojure
(require-macros '[closh.zero.core :refer [sh-str]])
(defn closh-prompt []
(sh-str fish -c fish_prompt))
```
Bash [prompt format](http://www.tldp.org/HOWTO/Bash-Prompt-HOWTO/bash-prompt-escape-sequences.html) can be used via [decode-prompt](https://github.com/substack/decode-prompt) module. Install it with `npm install -g decode-prompt`. Then use it like:
```clojure
(require '[cljs.nodejs])
(def decode-prompt (js/require "decode-prompt"))
(def PS1 "\\[\\033[01;32m\\]\\u@\\h\\[\\033[00m\\]:\\[\\033[01;34m\\]\\w\\[\\033[00m\\]\\$ ")
(defn closh-prompt []
(decode-prompt PS1 #js{:env js/process.env}))
```
Or you can implement a custom [prompt in clojure](https://gist.github.com/jeroenvandijk/22927bd763ab786ec826a7727b43208c).
### Tab completion
Closh delegates completion to existing shells. When tab completion is triggered it tries to fetch completions first from `fish`, then `zsh` and finally `bash`. One the mentioned shells needs to be installed for completion to work.
If the completion does not work you can find out the reason by cloning the repo and trying out:
```sh
./resources/completion/completion.bash "ls "
./resources/completion/completion.zsh "ls "
./resources/completion/completion.fish "ls "
```
### Custom commands
You can define helper aliases, abbreviations, functions and commands in your `~/.closhrc` file.
#### Aliases
Aliases for defining or overriding functionality. When alias is used it does not get expanded and is saved into history as is.
Example:
```clojure
(defalias ls "ls --color=auto")
```
#### Abbreviations
Abbreviations are similar to aliases but expand to underlying definition on the command line after you type space or press enter. Therefore autocomplete can work seamlessly and also full command is saved to history. Inspired by [abbr](https://fishshell.com/docs/current/commands.html#abbr) in fish (more [details](https://github.com/fish-shell/fish-shell/issues/731)).
```clojure
(defabbr gco "git checkout")
(defabbr gaa "git add --all")
```
#### Functions
Define Clojure functions, run in command line like: `(hello "World")`.
```clojure
(defn hello [name]
(str "Hello " name))
```
#### Commands
Similar to functions, but can be executed like commands without using parens. Run in command line like: `hello World`.
```clojure
; Define commands like with defn
(defcmd hello [name]
(str "Hello " name))
; Promote existing function to a command
(defcmd upper clojure.string/upper-case)
```
### Quoting
Prevent some expansion is same like bash with double-quoted string:
```
echo "*"
```
Disable expansion completely with a single quote:
```clojure
bash: echo '$HOME'
closh: echo '$HOME ; notice there is only one quote
; if the quoted string has spaces wrap in double quotes and then prepend single quote
bash: echo '$HOME $PWD'
closh: echo '"$HOME $PWD"
```
### Signal handling
- SIGINT interrupts currently running command or code
- SIGQUIT is ignored