Cookbook

If you come from other shells, hopefully the following recipes will get you started quickly:

UI Recipes

  • Put your startup script in ~/.elvish/rc.elv. There is no alias yet, but you can achieve the goal by defining a function:

    fn ls [@a]{ e:ls --color [email protected] }

    The e: prefix (for “external”) ensures that the external command named ls will be called. Otherwise this definition will result in infinite recursion.

  • The left and right prompts can be customized by assigning functions to edit:prompt and edit:rprompt. Their outputs are concatenated (with no spaces in between) before being used as the respective prompts. The following simulates the default prompts but uses fancy Unicode:

    # "tilde-abbr" abbreviates home directory to a tilde.
    edit:prompt = { tilde-abbr $pwd; put '❱ ' }
    # "constantly" returns a function that always writes the same value(s) to
    # output; "edit:styled" writes styled output.
    edit:rprompt = (constantly (edit:styled `whoami`✸`hostname` inverse))

    Here is a terminalshot of the alternative prompts:

    ~❱ # Fancy unicode prompts!                  xiaq✸xiaqsmbp
  • Press ▲︎ to search through history. It uses what you have typed to do prefix match. To cancel, press Escape.

    ~> echo $daemon:pid                          [email protected]
    HISTORY #11610
  • Press Tab to start completion. Use arrow keys ▲︎ ▼︎ ◀︎ ▶︎ or Tab and Shift-Tab to select the candidate. Press Enter, or just continue typing to accept. To cancel, press Escape. It even comes with a scrollbar! :) In fact, all interactive modes show a scrollbar when there is more output to see.

    ~/go/src/github.com/elves/elvish> vim CONTRIBUTING.md 
    COMPLETING argument ━━━━━━━━━━━━
    CONTRIBUTING.md Gopkg.toml README.md edit getopt
    Dockerfile LICENSE cover errors glob
    Gopkg.lock Makefile daemon eval main.go
  • You can make completion case-insensitive with the following code:

    edit:-matcher[''] = [p]{ edit:match-prefix &ignore-case $p }

    You can also make the completion use “smart case” by changing &ignore-case to &smart-case. This means that if your pattern is entirely lower case it ignores case, otherwise it’s case sensitive.

  • Press Ctrl-N to start the builtin filesystem navigator, aptly named “navigation mode.” Use arrow keys to navigate. Enter inserts the selected filename to your command line. If you want to insert the filename and stay in the mode (e.g. when you want to insert several filenames), use Alt-Enter.

    You can continue typing your command when you are in navigation mode. Press Ctrl-H to toggle hidden files; and like in other modes, Escape gets you back to the default (insert) mode.

    ~/go/src/github.com/elves/elvish>            [email protected]
    NAVIGATING
    elvish CONTRIBUTING.md FROM golang:onbuild
    fix-for-0.7 Dockerfile
    images Gopkg.lock
    md-highlighter Gopkg.toml
    LICENSE
    Makefile
    README.md
    cover
    daemon
    edit
    errors
    eval
    getopt
  • Try typing echo [ and press Enter. Elvish knows that the command is unfinished due to the unclosed [ and inserts a newline instead of accepting the command. Moreover, common errors like syntax errors and missing variables are highlighted in real time.

  • Elvish remembers which directories you have visited. Press Ctrl-L to list visited directories. Like in completion, use arrow keys ▲︎ ▼︎ or Tab and Shift-Tab to select a directory and use Enter to cd into it. Press Escape to cancel.

    ~>                                           [email protected]
    LOCATION
    * ~
    * ~/go/src/github.com/elves/elvish
    110 ~/on/elvish-site/code
    62 ~/on/elvish-site/code/src
    52 ~/go/src/github.com/elves/elvish/edit
    34 ~/on/elvish-site/code/tty
    33 ~/on/elvish-site/code/assets
    32 ~/go/src/github.com/elves/elvish/eval
    26 ~/on/chat-app/code
    24 ~/on/elvish-site/code/dst
    20 ~/go/src/github.com/elves/md-highlighter
    14 ~/on/chat-app/code/public
    13 ~/.elvish

    Type to filter:

    ~>                                           [email protected]
    LOCATION x/p/v
    1 ~/go/src/github.com/xiaq/persistent/vector

    The filtering algorithm is tailored for matching paths; you need only type a prefix of each component. In the screenshot, x/p/v matches xiaq/persistent/vector.

  • Elvish doesn’t support history expansion like !!. Instead, it has a “last command mode” offering the same functionality, triggered by Alt-1 by default (resembling how you type ! using Shift-1). In this mode, you can pick individual arguments from the last command using numbers, or the entire command by typing Alt-1 again.

    This is showing me trying to fix a forgotten sudo:

    ~> rm -rf /var
    rm: /var: Operation not permitted
    Exception: rm exited with 1
    Traceback:
    [interactive], line 1:
    rm -rf /var
    ~> [email protected]
    LASTCMD
    M-1 rm -rf /var
    0 rm
    1 -rf
    2 /var

Language Recipes

  • Lists look like [a b c], and maps look like [&key1=value1 &key2=value2]. Unlike other shells, a list never expands to multiple words, unless you explicitly explode it by prefixing the variable name with @:

    ~> li = [1 2 3]
    ~> put $li
    ▶ [1 2 3]
    ~> put [email protected]
    ▶ 1
    ▶ 2
    ▶ 3
    ~> map = [&k1=v1 &k2=v2]
    ~> echo $map[k1]
    v1
  • Environment variables live in a separate E: (for “environment”) namespace and must be explicitly qualified:

    ~> put $E:HOME
    ▶ /home/xiaq
    ~> E:PATH = $E:PATH":/bin"
  • You can manipulate search paths through the special list $paths, which is synced with $E:PATH:

    ~> echo $paths
    [/bin /sbin]
    ~> paths = [/opt/bin [email protected] /usr/bin]
    ~> echo $paths
    [/opt/bin /bin /sbin /usr/bin]
    ~> echo $E:PATH
    /opt/bin:/bin:/sbin:/usr/bin
  • You can manipulate the keybinding through the map $edit:binding. For example, this binds Ctrl-L to clearing the terminal: edit:binding[insert][Ctrl-L] = { clear > /dev/tty }. The first index into the map is the mode and the second is the key. (Yes, the braces enclose a lambda.)

    Use pprint $edit:binding to get a nice (albeit long) view of the current keybinding.

    NOTE: Bindings for letters modified by Alt are case-sensitive. For instance, Alt-a means pressing Alt and A, while Alt-A means pressing Alt, Shift and A. This will probably change in the future.

  • There is no interpolation inside double quotes (yet). For example, the value of "$user" is simply the string $user. Use implicit string concatenation to build strings:

    ~> name = xiaq
    ~> echo "My name is "$name"."
    My name is xiaq.

    Sometimes string concatenation will force you to string literals instead of barewords:

    ~> noun = language
    ~> echo $noun's'
    languages

    You cannot write s as a bareword because Elvish would think you are trying to write the variable $nouns.

  • Double quotes do support C-like escape sequences (\n for newline, etc.):

    ~> echo "a\nb"
    a
    b

    Note: If you run echo "a\nb" in bash or zsh, you might get the same result (depending on the value of some options), and this might lead you to believe that they support C-like escape sequences in double quotes as well. This is not the case; bash and zsh preserve the backslash in double quotes, and it is the echo builtin command that interpret the escape sequences.

  • Elementary floating-point arithmetics as well as comparisons are builtin, with a prefix syntax:

    ~> + 1 2
    ▶ 3
    ~> / (* 2 3) 4
    ▶ 1.5
    ~> > 1 2
    ▶ $false
    ~> < 1 2
    ▶ $true
  • Functions are defined with fn. You can name arguments:

    ~> fn square [x]{
    * $x $x
    }
    ~> square 4
    ▶ 16
  • Output of some builtin commands start with a funny . It is not part of the output itself, but shows that such commands output a stream of values instead of bytes. As such, their internal structures as well as boundaries between values are preserved. This allows us to manipulate structured data in the shell. Read semantics highlights for details.

  • When calling Elvish commands, options use the special &option=value syntax. For instance, the echo builtin command supports a sep option for specifying an alternative separator:

    ~> echo &sep="," a b c
    a,b,c

    The mixture of options and arguments is a classical problem in traditional shell programming. For instance, if you want to print a file whose name is in $x, cat $x is the obvious thing to do, but it does not do this reliably – if $x starts with - (e.g. -v), cat thinks that it is an option. The correct way is cat -- $x.

    Elvish commands are free from this problem. However, the option facility is only available to builtin commands and user-defined functions, not external commands, meaning that you still need to do cat -- $x.

    In principle, it is possible to write safe wrapper for external commands and there is a plan for this.