**Work in progress.**
This tutorial introduces the fundamentals of shell programming with Elvish.
Familiarity with other shells or programming languages is useful but not
required.
# Commands and strings
Let's begin with the traditional "hello world" program:
```elvish-transcript
~> echo Hello, world!
Hello, world!
```
Here, we call the `echo` **command** with two **arguments**: `Hello,` and
`world!`. The `echo` command prints them both, inserting a space in between and
adding a newline at the end, and voilà, we get back the message `Hello, world!`.
## Quoting the argument
We used a single space between the two arguments. Using more also works:
```elvish-transcript
~> echo Hello, world!
Hello, world!
```
The output still only has one space, because `echo` didn't know how many spaces
were used to separate the two arguments; all it sees is the two arguments,
`Hello,` and `world!`. However, you can preserve the two spaces by **quoting**
the entire text:
```elvish-transcript
~> echo "Hello, world!"
Hello, world!
```
In this version, `echo` only sees one argument containing
Hello, world
(with two spaces). A pair of **double
quotes** tells Elvish that the text inside it is a single argument; the quotes
themselves are not part of the argument.
On contrary, the `Hello,` and `world!` arguments are implicitly delimited by
spaces (instead of explicitly by quotes); as such, they are known as
**barewords**. Some special characters also delimit barewords, which we will see
later. Barewords are useful to write command names, filenames and command-line
switches, which usually do not contain spaces or special characters.
## Editing the command line
TODO
## Builtin and external commands
We demonstrated the basic command structure using `echo`, a very simple command.
The same structure applies to all the commands. For instance, Elvish comes with
a `randint` command that takes two arguments `a` and `b` and generates a random
integer in the range a...b-1. You can use the command as a digital dice:
```elvish-transcript
~> randint 1 7
▶ 3
```
Arithmetic operations are also commands. Like other commands, the command names
comes first, making the syntax a bit different from common mathematical
notations:
```elvish-transcript
~> + 17 28 # addition
▶ (num 45)
~> * 17 28 # multiplication
▶ (num 476)
```
The commands above all come with Elvish; they are **builtin commands**. There
are [many more](../ref/builtin.html) of them.
Some builtin commands are in importabled modules. For example, the command to
compute exponention lives in the `math:` module:
```elvish-transcript
~> use math
~> math:pow 2 10
▶ (num 1024)
```
Another kind of commands is **external commands**. They are separate programs
from Elvish, and either come with the operating system or are installed by you
manually. Chances are you have already used some of them, like `ls` for listing
files, or `cat` for showing files.
There are really a myriad of external commands; to start with, you can manage
code repositories with [git](https://git-scm.com), convert documents with
[pandoc](http://pandoc.org), process images with
[ImageImagick](https://www.imagemagick.org/script/index.php), transcode videos
with [ffmpeg](http://ffmpeg.org), test the security of websites with
[nmap](https://nmap.org) and analyze network traffic with
[tcpdump](http://www.tcpdump.org). Many free and open-source software come with
a command-line interface.
Here we show you how to obtain the latest version of Elvish entirely from the
command line: we use `curl` to download the binary and its checksum, `shasum` to
check the checksum, and `chmod` to make it executable (assuming that you are
running macOS on x86-64):
```elvish-transcript
~> curl -s -o elvish https://dl.elv.sh/darwin-amd64/elvish-HEAD
~> curl -s https://dl.elv.sh/darwin-amd64/elvish-HEAD.sha256sum
8b3db8cf5a614d24bf3f2ecf907af6618c6f4e57b1752e5f0e2cf4ec02bface0 elvish-HEAD
~> shasum -a 256 elvish
8b3db8cf5a614d24bf3f2ecf907af6618c6f4e57b1752e5f0e2cf4ec02bface0 elvish
~> chmod +x elvish
~> ./elvish
```
## History and scripting
Some commands are useful to rerun; for instance, you may want to roll the
digital dice several times in a roll, or for another occasion. Of course, you
can just retype the command:
```elvish-transcript
~> randint 1 7
▶ 1
```
The command is short, but still, it can become a chore if you want to run it
repeatedly. Fortunately, Elvish remembers all the commands you have typed; you
can just ask Elvish to recall it by pressing Up:
@ttyshot learn/fundamentals/history-1
This will give you the last command you have run. However, it may have been a
while when you have last run the `randint` command, and this will not give you
what you need. You can either continue pressing Up until you find the
command, or you can give Elvish a hint by typing some characters from the
command line you want, e.g. `ra`, before pressing Up:
@ttyshot learn/fundamentals/history-2
Another way to rerun commands is saving them in a **script**, which is simply a
text file containing the commands you want to run. Using your favorite text
editor, save the command to `dice.elv` under your home directory:
```elvish
# dice.elv
randint 1 7
```
After saving the script, you can run it with:
```elvish-transcript
~> elvish dice.elv
▶ 4
```
Since the above command runs `elvish` explicitly, it works in other shells as
well, not just from Elvish itself.
# Variables and lists
To change what a command does, we now need to change the commands themselves.
For instance, instead of saying "Hello, world!", we might want our command to
say "Hello, John!":
```elvish-transcript
~> echo Hello, John!
Hello, John!
```
Which works until you want a different message. One way to solve this is using
**variables**:
```elvish-transcript
~> var name = John
~> echo Hello, $name!
Hello, John!
```
The command `echo Hello, $name!` uses the `$name` variable you just assigned in
the previous command. To greet a different person, you can just change the value
of the variable, and the command doesn't need to change:
```elvish-transcript
~> var name = Jane
~> echo Hello, $name!
Hello, Jane!
```
Using variables has another advantage: after defining a variable, you can use it
as many times as you want:
```elvish-transcript
~> var name = Jane
~> echo Hello, $name!
Hello, Jane!
~> echo Bye, $name!
Bye, Jane!
```
Now, if you change the value of `$name`, the output of both commands will
change.
## Environment variables
In the examples above, we have assigned value of `$name` ourselves. We can also
make the `$name` variable automatically take the name of the current user, which
is usually kept in an **environment variable** called `USER`. In Elvish,
environment variables are used like other variables, except that they have an
`E:` at the front of the name:
```elvish-transcript
~> echo Hello, $E:USER!
Hello, elf!
~> echo Bye, $E:USER!
Bye, elf!
```
The outputs will likely differ on your machine.
## Lists and indexing
The values we have stored in variables so far are all strings. It is possible to
store a **list** of values in one variable; a list can be written by surrounding
some values with `[` and `]`. For example:
```elvish-transcript
~> var list = [linux bsd macos windows]
~> echo $list
[linux bsd macos windows]
```
Each element of this list has an **index**, starting from 0. In the list above,
the index of `linux` is 0, that of `bsd` is 1, and so on. We can retrieve an
element by writing its index after the list, also surrounded by `[` and `]`:
```elvish-transcript
~> echo $list[0] is at index 0
linux is at index 0
```
We can even do:
```elvish-transcript
~> echo [linux bsd macos windows][0] is at index 0
linux is at index 0
```
Note that in this example, the two pairs of `[]` have different meanings: the
first pair denotes lists, while the second pair denotes an indexing operation.
## Script arguments
Recall the `dice.elv` script above:
```elvish
# dice.elv
randint 1 7
```
And how we ran it:
```elvish-transcript
~> elvish dice.elv
▶ 4
```
We were using `elvish` itself as a command, with the sole argument `dice.elv`.
We can also supply additional arguments:
```elvish-transcript
~> elvish dice.elv a b c
▶ 4
```
But this hasn't made any difference, because well, our `dice.elv` script doesn't
make use of the arguments.
The arguments are kept in a `$args` variable, as a list. Let's try put this into
a `show-args.elv` file in your home directory:
```elvish
echo $args
```
And we can run it:
```elvish-transcript
~> elvish show-args.elv
[]
~> elvish show-args.elv foo
[foo]
~> elvish show-args.elv foo bar
[foo bar]
```
Since `$args` is a list, we can retrieve the individual elements with
`$args[0]`, `$args[1]`, etc.. Let's rewrite our greet-and-bye script, taking the
name as an argument. Put this in `greet-and-bye.elv`:
```elvish
var name = $args[0]
echo Hello, $name!
echo Bye, $name!
```
We can run it like this:
```elvish-transcript
~> elvish greet-and-bye.elv Jane
Hello, Jane!
Bye, Jane!
~> elvish greet-and-bye.elv John
Hello, John!
Bye, John!
```
# Output capture and multiple values
Environment variables are not the only way to learn about a computer system; we
can also gain more information by invoking commands. The `uname` command tells
you which operation system the computer is running; for instance, if you are
running Linux, it prints `Linux` (unsurprisingly):
```elvish-transcript
~> uname
Linux
```
(If you are running macOS, `uname` will print `Darwin`, the
[open-source core]()
of macOS.)
Let's try to integrate this information into our "hello" message. The Elvish
command-line allows us to run multiple commands in a batch, as long as they are
separated by semicolons. We can build the message by running multiple commands,
using `uname` for the OS part:
```elvish-transcript
~> echo Hello, $E:USER, ; uname ; echo user!
Hello, xiaq,
Linux
user!
```
This has the undesirable effect that "Linux" appears on its own line. Instead of
running this command directly, we can first **capture** its output in a
variable:
```elvish-transcript
~> var os = (uname)
~> echo Hello, $E:USER, $os user!
Hello, elf, Linux user!
```
You can also use the output capture construct directly as an argument to `echo`,
without storing the result in a variable first:
```elvish-transcript
~> echo Hello, $E:USER, (uname) user!
Hello, elf, Linux user!
```
## More arithmetic
You can use output captures to construct do complex arithmetic involving more
than one operation:
```elvish-transcript
~> # compute the answer to life, universe and everything
* (+ 3 4) (- 100 94)
▶ 42
```