Running commands

Table of content

This article is part of the Beginner’s Guide to Elvish series:

1. Introduction

Welcome to Elvish!

This series will teach you the basics of Elvish’s power scripting features and some of its handy interactive features, and will help you become much more productive at the command line. Experience with other shells is useful but not required.

Before you start, make sure that you have installed Elvish. After that, type elvish in the terminal and press Enter to start Elvish ($ represents the existing shell’s prompt and is not part of what you type):

Terminal

$ elvish

All the examples below assume that you have started Elvish. Alternatively, you can use Elvish as your default shell and have Elvish launched by default.

2. Hello World!

Let’s begin with the traditional “Hello, World!” ritual:

Terminal - elvish

~> echo Hello, world! Hello, world!

You can follow this example (and many others) by typing the code and pressing Enter. The ~> part is Elvish’s prompt; you don’t need to type it.

Elvish code often follows a “command + arguments“ structure. In this case, the command is echo, whose job is to just print out its arguments.

3. Builtin commands

The echo command we’ve just seen does a very simple job, but that’s just one of many commands Elvish supports. Invoking other commands follows a similar “command + arguments” structure.

For instance, the randint command takes two arguments a and b and generates a random integer in the range ab-1. You can use it as a digital dice:

Terminal - elvish

~> randint 1 7 ▶ (num 3)

(We’ll get to the notation later in this article, and the (num 3) part in Using data types.)

Arithmetic operations are also commands. Like the echo and randint commands we have seen, the command names comes first, making the syntax a bit different from common mathematical notations (sometimes known as Polish notation):

Terminal - elvish

~> + 2 10 # addition ▶ (num 12) ~> * 2 10 # multiplication ▶ (num 20)

These commands come with Elvish and are thus called builtin commands. The refenrece page for the builtin module contains all the builtin commands you can use directly.

3.1. Commands in modules

Some builtin commands live in modules that you need to import first. For example, mathematical operations such as the power function is provided by the math module:

Terminal - elvish

~> use math # import the "math" module ~> math:pow 2 10 # raise 2 to the power of 10 ▶ (num 1024)

We’ll learn more about modules in Organizing code.

3.2. Comment syntax

You may have noticed that we have annotated some of our commands above with texts like # texts:

Terminal - elvish

~> + 2 10 # addition ▶ (num 12)

The # character marks the rest of the line as a comment, which is ignored by Elvish. When typing out the examples, you can include the comments or leave them out.

4. External commands

While Elvish provides a lot of useful functionalities as builtin commands, it can’t do everything. This is where external commands come in, which are separate programs installed on your machine. If you are using a Unix-like system, it usually comes with many of external commands pre-installed; you may have used ls for listing files, or cat for showing files.

Many useful programs come in the form of external commands, and there is no limit on what they can do. Here are just a few examples:

  • Git provides the git command to manage code repositories

  • Pandoc provides the pandoc command to convert document formats

  • GraphicsMagick provides the gm command, and ImageImagick provides the magick command to process images

  • FFmpeg provides the ffmpeg command to process and videos

  • Curl provides the curl command to make HTTP requests

  • Nmap provides the nmap command to test the security of websites

  • Tcpdump provides the tcpdump command to analyze network traffic

  • Elvish itself can be used as an external command, as elvish

These are all command-line programs, but even graphical programs often provide command-line interfaces that give you access to advanced configuration options. For example, you can invoke Chromium like chromium --js-flags=... to customize internal JavaScript options (the exact command name depending on the OS).

4.1. A concrete example

Much of the power of shells comes from the ease of invoking external commands, and Elvish is no exception. Below is an example of how you can download Elvish, using a combination of external commands:

  • curl to download files over HTTP

  • tar to unpack archive files

  • shasum (on Unix systems) or certutil (on Windows) to calculate the SHA-256 checksum of files

Terminal - elvish

~> curl -s -o elvish-HEAD.tar.gz https://dl.elv.sh/linux-amd64/elvish-HEAD.tar.gz ~> curl -s https://dl.elv.sh/linux-amd64/elvish-HEAD.tar.gz.sha256sum 93b206f7a5b7f807f6b2b2b99dd4074ed678620541f6e9742148fede0a5fefdb elvish-HEAD.tar.gz ~> shasum -a 256 elvish-HEAD.tar.gz # On a Unix system 93b206f7a5b7f807f6b2b2b99dd4074ed678620541f6e9742148fede0a5fefdb elvish-HEAD.tar.gz ~> certutil -hashfile elvish-HEAD.tar.gz SHA256 # On Windows 93b206f7a5b7f807f6b2b2b99dd4074ed678620541f6e9742148fede0a5fefdb ~> tar -xvf elvish-HEAD.tar.gz x elvish ~> ./elvish

(Note: To install Elvish, you’re recommended to use the script generated on the Get Elvish page instead. This example is for illustration, and assumes your OS to be Linux and CPU to be Intel 64-bit. We will see how to avoid making these assumptions in Using variables, lists and loops.)

Invoking external commands follow the same “command + arguments” structure. For example, in the first curl command, the arguments are -s, -o, elvish-HEAD.tar.gz and https://dl.elv.sh/linux-amd64/elvish-HEAD.tar.gz respectively.

External commands often assign special meanings to arguments starting with -, sometimes called flags. In the case of curl, the arguments work as follows:

  • -s means “silent mode”. You can try leaving this argument out to see more output from curl.

  • -o, along with the next argument elvish-HEAD.tar.gz, means that curl should output the result to elvish-HEAD.tar.gz.

  • https://dl.elv.sh/linux-amd64/elvish-HEAD.tar.gz is the URL to request.

The second curl command leaves out the -o or its next argument, directing curl to write the result directly as output. This allows us to see the checksum directly in the terminal.

You can find out what flags each external command accepts in their respective manual. You’ll also find commands that accept flags starting with --, and on Windows, starting with /.

5. Editing the code interactively

If you have followed along the examples and typed them on your computer, it’s possible that you have made some mistakes. (If you haven’t made any, pause and appreciate your typing skills.)

Elvish supports the following keys commonly supported in GUI programs:

  • and move the cursor one character at a time.

  • Alt-◀ and Alt-▶ move the cursor one word at a time.

  • Home and End move the cursor to the start or end of the line.

  • Backspace deletes the character to the left of the cursor.

  • Delete deletes the character on the cursor (or to its right if your cursor is an I-beam).

Elvish also supports some keys found in traditional shells:

  • Ctrl-W deletes the word to the left of the cursor.

  • Ctrl-U deletes the entire line, up to the cursor.

(See readline-binding if you’d like to use more readline-style bindings.)

6. Arguments and quoting

Let’s take a closer look at some of the commands we’ve run:

Terminal - elvish

~> + 2 10 ▶ (num 12) ~> echo Hello, world! Hello, world!

In the first command, + is given two arguments, 2 and 10, separated by spaces – so far so good.

In the second command, echo is given Hello, world!. Following how we read the first command, this should also be two arguments. But this still achieved what we want – printing out Hello, World! – but how?

As it turns out, what’s happening with this simple command is actually not so simple:

  1. Elvish recognizes Hello, World! as two arguments and passes them to echo.

  2. The echo command receives two arguments and it joins them with a space before printing.

The consequence of this process can be better observed when you’d like to print multiple spaces:

Terminal - elvish

~> echo Hello, world! Hello, world!

Although we’ve used three spaces, all echo receives is two arguments Hello, and world!, so it only splices them back with one space between them.

6.1. Quoting

In order to pass spaces to echo, we can pass a quoted argument:

Terminal - elvish

~> echo 'Hello, world!' Hello, world!

A pair of single quotes delimits a quoted string, and tells Elvish that the text inside it is a single argument. In this case, echo only sees one argument containing Hello,  world (with two spaces).

You can use quoted arguments wherever you use an unquoted argument. In fact, you can even quote the command name itself. This applies to all the commands we have seen so far:

Terminal - elvish

~> '+' '2' '10' ▶ (num 12) ~> 'randint' '1' '7' ▶ (num 3)

However, writing commands like this is unnecessary and unreadable. It’s better to reserve quoting to situations when it’s necessary, for example when the argument you’d like to pass has spaces.

6.2. Metacharacters

Another common reason to use quoting is when your argument includes metacharacters, characters with special meaning to Elvish. Some examples of metacharacters are #, which introduces a comment, and ( and ), which we’ll encounter soon. Quoting them stops Elvish from treating them as metacharacters:

Terminal - elvish

~> echo '(1) Rule #1' (1) Rule #1

We will learn more metacharacters as we learn more of Elvish’s syntax. As a rule of thumb, punctuation marks in the ASCII range tend to be metacharacters, except those commonly found in file paths, like _, -, ., / and \.

6.3. Double quotes

You can also create quoted strings with double quotes (like "string"). It’s similar to single quotes, but you can write single quotes within it:

Terminal - elvish

~> echo "It's a wonderful world!" It's a wonderful world!

Another difference is that double quotes allow you to write special characters using escape sequences that start with \. For example, \n inside double quotes represents a newline:

Terminal - elvish

~> echo "old pond\nfrog leaps in\nwater's sound" old pond frog leaps in water's sound

You can read more about single-quoted strings and double-quoted strings in the language refenrece page.

7. Working with command outputs

Let’s go back to our arithmetic examples:

Terminal - elvish

~> + 2 10 # addition ▶ (num 12) ~> * 2 10 # multiplication ▶ (num 20)

What if we want to calculate something that involves multiple operations, like 3 * (2 + 10)? Of course, we can simply run multiple commands:

Terminal - elvish

~> + 2 10 ▶ (num 12) ~> * 3 12 ▶ (num 36)

But this makes Elvish a really clumsy calculator! Instead, we can capture the output of the + command by placing it inside (), and use it as an argument to the * command:

Terminal - elvish

~> * (+ 2 10) 3 ▶ (num 36)

Now is also the time to explain the notation. It indicates that the output is a value in Elvish’s data type system. In this case, (num ...) is a typed number; for the purpose of passing to *, (num 12) and 12 work identically. We will learn more about data types in Using data types.

7.1. Concatenating results

Other than using the output as an argument on its own, you can also concatenate it to something else to build a bigger argument. For example, if we want to add some message explaining what the result is:

Terminal - elvish

~> echo 'And the result is... '(* (+ 2 10) 3) And the result is... 36

(Here, for the purpose of string concatenation, (num 36) “loses” its data type and becomes just 36.)

When the output of a command doesn’t start with , it indicates that the output is a stream of bytes. For example, echo produces bytes:

Terminal - elvish

~> echo Hello! Hello!

Among Elvish’s builtin commands, some output values, while some output bytes. On the other hand, external commands can only output bytes; they don’t have direct access to Elvish’s system of data types.

Let’s finish this section by augmenting our “Hello, World!” example with the output of a useful external commands:

Terminal - elvish

~> echo 'Hello World! My name is: '(whoami) Hello World! My name is: elf

8. Conclusion

Being able to run all sorts of commands easily is one of the greatest strengths of shells. When using Elvish interactively, most of your interactions will consist of simple invocations of various commands.

In this part, we learned the basics of running builtin and external commands, how to edit your command, and dived into the inner workings of arguments and quoting. We also learned how to capture and use the output of commands, including the distinction between value output and byte output.

We are now ready for the next part, Using variables, lists and loops.