Skip to content

Latest commit

 

History

History
323 lines (229 loc) · 7.45 KB

guide.md

File metadata and controls

323 lines (229 loc) · 7.45 KB

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.

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:

for f in /sys/bus/usb/devices/*/power/wakeup; do echo $f; cat $f; done
; Using doseq
(doseq [f (expand "/sys/bus/usb/devices/*/power/wakeup")] (println f) (sh cat (str f)))
; Or multi pipes
ls /sys/bus/usb/devices/*/power/wakeup |> (map #(str % "\n" (sh-str cat (str %)))) | cat

If conditionals:

if test -f package.json; then echo file exists; else echo no file; fi
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:

sqlite3 ~/.closh/closh.sqlite "SELECT command FROM history ORDER BY id ASC"

For convenience you can add the following to your ~/.closhrc file:

(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 prompt like this:

(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 shell:

(require-macros '[closh.zero.core :refer [sh-str]])

(defn closh-prompt []
  (sh-str fish -c fish_prompt))

Bash prompt format can be used via decode-prompt module. Install it with npm install -g decode-prompt. Then use it like:

(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.

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:

./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:

(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 in fish (more details).

(defabbr gco "git checkout")
(defabbr gaa "git add --all")

Functions

Define Clojure functions, run in command line like: (hello "World").

(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.

; 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:

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