This article is part of the Beginner’s Guide to Elvish series:
-
Running commands
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 a … b-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 themagick
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) orcertutil
(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 fromcurl
. -
-o
, along with the next argumentelvish-HEAD.tar.gz
, means thatcurl
should output the result toelvish-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:
-
Elvish recognizes
Hello, World!
as two arguments and passes them toecho
. -
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.