**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 ```