You are on page 1of 6

Inception

Bash is a program on your computer like any other. The main difference between
bash and most other programs is that unlike them, bash was not programmed to
perform a certain task. Bash was programmed to take commands from you, the user.
Often, you'll hear people refer to their code as "shell code", which is about as specific
as "source code" is when referring to your Java code.

With bash installed, we can run the binary to start the program. Before we do so, it's
important to take note of the two distinct modes of operation that the bash shell
supports:

interactive mode
In interactive mode, the bash shell waits for your commands before performing them.
Each command you pass it is executed. While a command is being executed, you
cannot interact with the bash shell. As soon as the command is finished, you can
interact with bash again while bash awaits your next command.
non-interactive mode
The bash shell can also execute scripts. A script is a pre-written series of commands
which bash can execute without needing to ask you what to do next. Scripts are
generally saved in files and subsequently used to automate a wide range of tasks.

echo "$BASH_VERSION",

As soon as you confirm the command, the bash shell will execute it and produce the
output on the line below. The bash program is only one of many programs that can
run in a terminal, so it is important to note that bash is not what's making text appear
on your screen. The terminal program takes care of that, taking text from bash and
placing it in its window for you to see. The terminal may do the same for other
programs running in the terminal, completely unrelated to bash, such as a mail
program or an IRC client.

A program is a set of pre-written instructions that can be executed by your system's


kernel. A program gives instructions to the kernel directly. The kernel is technically
also a program, but one that runs constantly and communicates with your hardware
instead. A program generally lives on your disk, waiting to be started. When you
"run" or "execute" a program, your kernel loads its pre-written instructions (its code)
by creating a process for your program to work in. A process relays the instructions
in your program to the kernel. A process also has a few hooks to the outside world
via something called file descriptors. These are essentially plugs we use to connect
processes to files, devices or other processes.
A process isn't limited to just these three file descriptors, it can create new ones with
their own number and connect them to other files, devices or processes as it sees fit.
If a program needs its output to go to another program's input, as opposed to your
display, it will instruct the kernel to connect its standard output to the other program's
standard input. Now all the information it sends to its standard output file descriptor
will flow into the other program's standard input file descriptor. These flows of
information between files, devices and processes are called streams.

A stream is information (specifically, bytes) flowing through the links between files,
devices and processes in a running system. They can transport any kind of bytes,
and the receiving end can only consume their bytes in the order they were sent. If I
have a program that outputs names connected to another program, the second
program can only see the second name after first reading the first name from the
stream. When it's done reading the second name, the next thing in the stream is the
third name. Once a name is read from the stream, the program can store it
somewhere if it may need it again later. Reading a name from the stream consumes
those bytes from the stream and the stream advances. The stream cannot be
rewound and the name cannot be re-read.

It is important to understand that file descriptors are process specific: to speak of


"standard output" only makes sense when referring to a specific process. In the
example above, you'll notice that the first process' standard input is not the same as
the second process' standard input. You'll also notice that the first process' FD 1
(standard output) is connected to the second process' FD 0 (standard input). File
descriptors do not describe the streams that connect processes, they only describe
the process' plugs where these streams can be connected to.

Commands and arguments

At the core of the bash shell language are its commands. Your commands tell bash
what you need it to do, step-by-step, command-by-command. Bash generally takes
one command from you at a time, executes the command, and when completed
returns to you for the next command. We call this synchronous command execution.
It is important to understand that while bash is busy with a command that you give it,
you cannot interact with bash directly: you'll have to wait for it to be ready with
executing its command and return to the script.
Some commands can take a long time to complete, though. In particular, commands
that start other programs with which you can interact. For instance, a command
might start a file editor.
$ ex bash command to run the "ex" program.
:i ex command to "insert" some text.
Hello!
. A line with just a dot tells ex to stop inserting text.
: w greeting.tx tex command to "write" the text to a file.
"greeting.txt" [New] 1L, 7C written
:q ex command to "quit" the program.
$ cat greeting.txt And now we're back in bash!
Hello! The "cat" program shows the contents of the file.
$

A bash command is the smallest unit of code that bash can independently execute.
While executing a command, you cannot interact with the bash shell. As soon as
bash is done executing a command, it returns to you for the next command to
execute.

Bash is mostly a line-based language. Accordingly, when bash reads your commands, it does
so line-by-line. Most commands will only constitute one line and, unless the syntax of your
bash command explicitly indicates that your command is not yet complete, as soon as you
end that line, bash will immediately consider that to be the end of the command. As a result,
typing a line of text and hitting the ⏎ key will generally cause bash to start performing the
command described by your line of text.

Some commands however, span multiple lines. These are usually block commands
or commands with quotes in them:

$ read -p "Your name? " name This command is complete and can be started
immediately.
Your name? Maarten Billemont
$ if [[ $name = $USER ]]; then The "if" block started but wasn't finished.
> echo "Hello, me."
> else
> echo "Hello, $name."
> fi Now the "if" block ends and bash knows enough to start the command
Hello, Maarten Billemont.

Logically, bash cannot execute a command until it has enough information to do its
job. The first line of the if command in the example above (we'll cover what these
commands do in more detail later on) doesn't contain enough information for bash to
know what to do if the test succeeds or if it fails. As a result, bash shows a special
prompt: >. This prompt essentially means: the command you gave me is not yet at
an end. We keep on providing extra lines for the command, until we reach the fi
construct. When we end that line, bash knows that you're done providing conditional
result cases. It immediately begins running all the code in the entire block, from if to
fi. We will soon see the different kinds of commands defined in bash's grammar, but
the if command we just saw is called a Compound Command, because it
compounds a bunch of basic commands into a larger logical block.
It doesn't matter much what you name the file in which you save the code. Let's say
you saved it in a file called hello.txt, we can now run the commands from that file
using bash without it having to ask us for instructions:

$ bash hello.txt This starts a new "bash" process.


Your name? Maarten Billemont
Hello, Maarten Billemont. Our new "bash" process ends when there is no
code left in the file.
$ Now that the "bash" command is done, our interactive bash comes back.

Notice that two bash processes are involved in this example. The bash process we
start off from is our regular interactive shell. We tell that bash process to run a
command which will cause it to start a new bash process. This second bash process
will execute all the commands it finds in the file hello.txt, non-interactively. When it's
done (there are no commands left in the file), the non-interactive bash process ends
and the interactive bash process is ready with your bash hello.txt command; it shows
a new prompt asking you for the next command to run.

It's only a small step from a file with a list of commands in it to a veritable bash script.
Open your hello.txt file again using your favourite text editor and add a hashbang to
the top of it, as the first line of the script:

#!/usr/bin/env bash

You've created your first bash script. What's a bash script? It's a file with bash code
in it that can be executed by the kernel just like any other program on your computer.
In essence, it is a program in itself, although it does need the bash interpreter to do
the work of translating the bash language into instructions the kernel understands.
That's where this "hashbang" line we've just added to the file comes in: It tells the
kernel what interpreter it needs to use to understand the language in this file, and
where to find it. We call it a "hashbang" because it always begins with a "hash" #
followed by a "bang" !. Your hashbang must then specify an absolute pathname to
any program that understands the language in your file and can take a single
argument. Our hashbang is a little special, though: We reference the program
/usr/bin/env, which isn't really a program that understands the bash language. It's a
program that can find and start other programs. In our case, we use an argument to
tell it to find the bash program and use that for interpreting the language in our script.
Why do we use this "inbetween" program called env? It has everything to do with
what comes before the name: the path. We know with relative certainty that the env
program lives in the /usr/bin path. Given the large variety of operating systems and
configurations, however, we don't have any good certainty about where the bash
program is installed. Which is why we use the env program to find it for us. That was
a little complicated! But now, what's the difference between our file before and after
adding the hashbang?
$ chmod +x hello.txt Mark hello.txt as an executable program
$ ./hello.txt Tell bash to start the hello.txt program

Most systems require you to mark a file as executable before the kernel is willing to
allow you to run it as a program. Once we do that, we can start the hello.txt program
like we would any other program. The kernel will look inside the file, find the
hashbang, use that to track down the bash interpreter, and finally use the bash
interpreter to start running the instructions in the file. You have your first real bash
program!

Bash gets commands by reading lines. As soon as it's read enough lines to compose
a complete command, bash begins running that command. Usually, commands are
just a single line long. An interactive bash session reads lines from you at the
prompt. Non-interactive bash processes read their commands from a file or stream.
Files with a hashbang as their first line (and the executable permission) can be
started by your system's kernel like any other program.

The basic grammar


- Simple command: the most common kind of command. It specifies the name
of a command to execute, along with an optional set of arguments,
environment variables and file descriptor redirection.

[ var=value ... ] name [ arg ... ] [ redirection ... ]

In the syntax block, we use square brackets ([ ]) around parts of the syntax
that are optional.

Before the command's name you can optionally put a few var assignments.
These variable assignments apply to the environment of this one command
only. We'll go more in depth on variables and environment later on.

The command's name is the first word (after the optional assignments). Bash
finds the command with that name and starts it. We'll learn more about what
kind of named commands there are and how bash finds them later on.

A command's name is optionally followed by a list of arg words, the


command arguments. We'll soon learn what arguments and their syntax are.

Finally, a command can also have a set of redirection operations applied to


it. If you recall our explanation of file descriptors in an earlier section,
redirections are the operations that change what the file descriptor plugs point
to. They change the streams that connect to our command processes. We'll
learn about the power of redirections in a future section.
- Pipelines: they are a convenient way of "connecting" two commands by way
of linking the first process' standard output to the second process' standard
input. This is the most common way for terminal commands to talk to one
another and convey information.

[time [-p]] [ ! ] command [ [|||&] command2 ... ]

https://guide.bash.academy/commands/

You might also like