mirror of
https://github.com/rawiriblundell/wiki.bash-hackers.org
synced 2024-12-25 06:00:41 +01:00
330 lines
15 KiB
Markdown
330 lines
15 KiB
Markdown
# Basic grammar rules of Bash
|
|
|
|
![](keywords>bash shell scripting grammar syntax language)
|
|
|
|
Bash builds its features on top of a few basic **grammar rules**. The
|
|
code you see everywhere, the code you use, is based on those rules.
|
|
However, **this is a very theoretical view**, but if you're interested,
|
|
it may help you understand why things look the way they look.
|
|
|
|
If you don't know the commands used in the following examples, just
|
|
trust the explanation.
|
|
|
|
## Simple Commands
|
|
|
|
Bash manual says:
|
|
|
|
A simple command is a sequence of optional variable assignments followed by blank-separated words and redirections,
|
|
and terminated by a control operator. The first word specifies the command to be executed, and is passed as argument
|
|
zero. The remaining words are passed as arguments to the invoked command.
|
|
|
|
Sounds harder than it actually is. It is what you do daily. You enter
|
|
simple commands with parameters, and the shell executes them.
|
|
|
|
Every complex Bash operation can be split into simple commands:
|
|
|
|
ls
|
|
ls > list.txt
|
|
ls -l
|
|
LC_ALL=C ls
|
|
|
|
The last one might not be familiar. That one simply adds "`LC_ALL=C`" to
|
|
the environment of the `ls` program. It doesn't affect your current
|
|
shell. This also works while calling functions, unless Bash runs in
|
|
POSIX(r) mode (in which case it affects your current shell).
|
|
|
|
Every command has an exit code. It's a type of return status. The shell
|
|
can catch it and act on it. Exit code range is from 0 to 255, where 0
|
|
means success, and the rest mean either something failed, or there is an
|
|
issue to report back to the calling program.
|
|
|
|
\<wrap center round info 90%\> The simple command construct is the
|
|
**base** for all higher constructs. Everything you execute, from
|
|
pipelines to functions, finally ends up in (many) simple commands.
|
|
That's why Bash only has one method to [expand and execute a simple
|
|
command](/syntax/grammar/parser_exec). \</wrap\>
|
|
|
|
## Pipelines
|
|
|
|
FIXME Missing an additional article about pipelines and pipelining
|
|
|
|
`[time [-p]] [ ! ] command [ | command2 ... ]`
|
|
|
|
**Don't get confused** about the name "pipeline." It's a grammatic name
|
|
for a construct. Such a pipeline isn't necessarily a pair of commands
|
|
where stdout/stdin is connected via a real pipe.
|
|
|
|
Pipelines are one or more [simple
|
|
commands](basicgrammar##simple_commands) (separated by the `|` symbol
|
|
connects their input and output), for example:
|
|
|
|
ls /etc | wc -l
|
|
|
|
will execute `ls` on `/etc` and **pipe** the output to `wc`, which will
|
|
count the lines generated by the ls command. The result is the number of
|
|
directory entries in /etc.
|
|
|
|
The last command in the pipeline will set the exit code for the
|
|
pipeline. This exit code can be "inverted" by prefixing an exclamation
|
|
mark to the pipeline: An unsuccessful pipeline will exit "successful"
|
|
and vice versa. In this example, the commands in the if stanza will be
|
|
executed if the pattern "^root:" is **not** found in `/etc/passwd`:
|
|
|
|
if ! grep '^root:' /etc/passwd; then
|
|
echo "No root user defined... eh?"
|
|
fi
|
|
|
|
Yes, this is also a pipeline (although there is no pipe!), because the
|
|
**exclamation mark to invert the exit code** can only be used in a
|
|
pipeline. If `grep`'s exit code is 1 (FALSE) (the text was not found),
|
|
the leading `!` will "invert" the exit code, and the shell sees (and
|
|
acts on) exit code 0 (TRUE) and the `then` part of the `if` stanza is
|
|
executed. One could say we checked for "`not grep "^root" /etc/passwd`".
|
|
|
|
The [set option pipefail](/commands/builtin/set#attributes) determines
|
|
the behavior of how bash reports the exit code of a pipeline. If it's
|
|
set, then the exit code (`$?`) is the last command that exits with non
|
|
zero status, if none fail, it's zero. If it's not set, then `$?` always
|
|
holds the exit code of the last command (as explained above).
|
|
|
|
The shell option `lastpipe` will execute the last element in a pipeline
|
|
construct in the current shell environment, i.e. not a subshell.
|
|
|
|
There's also an array `PIPESTATUS[]` that is set after a foreground
|
|
pipeline is executed. Each element of `PIPESTATUS[]` reports the exit
|
|
code of the respective command in the pipeline. Note: (1) it's only for
|
|
foreground pipe and (2) for higher level structure that is built up from
|
|
a pipeline. Like list, `PIPESTATUS[]` holds the exit status of the last
|
|
pipeline command executed.
|
|
|
|
Another thing you can do with pipelines is log their execution time.
|
|
Note that **`time` is not a command**, it is part of the pipeline
|
|
syntax:
|
|
|
|
# time updatedb
|
|
real 3m21.288s
|
|
user 0m3.114s
|
|
sys 0m4.744s
|
|
|
|
## Lists
|
|
|
|
FIXME Missing an additional article about list operators
|
|
|
|
A list is a sequence of one or more [pipelines](basicgrammar#pipelines)
|
|
separated by one of the operators `;`, `&`, `&&`, or `││`, and
|
|
optionally terminated by one of `;`, `&`, or `<newline>`.
|
|
|
|
=\> It's a group of **pipelines** separated or terminated by **tokens**
|
|
that all have **different meanings** for Bash.
|
|
|
|
Your whole Bash script technically is one big single list!
|
|
|
|
| Operator | Description |
|
|
|-------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
| `<PIPELINE1> <newline> <PIPELINE2>` | Newlines completely separate pipelines. The next pipeline is executed without any checks. (You enter a command and press `<RETURN>`!) |
|
|
| `<PIPELINE1> ; <PIPELINE2>` | The semicolon does what `<newline>` does: It separates the pipelines |
|
|
| `<PIPELINE> & <PIPELINE>` | The pipeline in front of the `&` is executed **asynchronously** ("in the background"). If a pipeline follows this, it is executed immediately after the async pipeline starts |
|
|
| `<PIPELINE1> && <PIPELINE2>` | `<PIPELINE1>` is executed and **only** if its exit code was 0 (TRUE), then `<PIPELINE2>` is executed (AND-List) |
|
|
| `<PIPELINE1> || <PIPELINE2>` | `<PIPELINE1>` is executed and **only** if its exit code was **not** 0 (FALSE), then `<PIPELINE2>` is executed (OR-List) |
|
|
|
|
**Note:** POSIX calls this construct a "compound lists".
|
|
|
|
## Compound Commands
|
|
|
|
See also the [list of compound commands](/syntax/ccmd/intro).
|
|
|
|
There are two forms of compound commands:
|
|
|
|
- form a new syntax element using a list as a "body"
|
|
- completly independant syntax elements
|
|
|
|
Essentially, everything else that's not described in this article.
|
|
Compound commands have the following characteristics:
|
|
|
|
- they **begin** and **end** with a specific keyword or operator (e.g.
|
|
`for ... done`)
|
|
- they can be redirected as a whole
|
|
|
|
See the following table for a short overview (no details - just an
|
|
overview):
|
|
|
|
| Compound command syntax | Description |
|
|
|------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
| `( <LIST> )` | Execute `<LIST>` in an extra subshell =\> [article](/syntax/ccmd/grouping_subshell) |
|
|
| `{ <LIST> ; }` | Execute `<LIST>` as separate group (but not in a subshell) =\> [article](/syntax/ccmd/grouping_plain) |
|
|
| `(( <EXPRESSION> ))` | Evaluate the arithmetic expression `<EXPRESSION>` =\> [article](/syntax/ccmd/arithmetic_eval) |
|
|
| `[[ <EXPRESSION> ]]` | Evaluate the conditional expression `<EXPRESSION>` (aka "the new test command") =\> [article](/syntax/ccmd/conditional_expression) |
|
|
| `for <NAME> in <WORDS> ; do <LIST> ; done` | Executes `<LIST>` while setting the variable `<NAME>` to one of `<WORDS>` on every iteration (classic for-loop) =\> [article](/syntax/ccmd/classic_for) |
|
|
| `for (( <EXPR1> ; <EXPR2> ; <EXPR3> )) ; do <LIST> ; done` | C-style for-loop (driven by arithmetic expressions) =\> [article](/syntax/ccmd/c_for) |
|
|
| `select <NAME> in <WORDS> ; do <LIST> ; done` | Provides simple menus =\> [article](/syntax/ccmd/user_select) |
|
|
| `case <WORD> in <PATTERN>) <LIST> ;; ... esac` | Decisions based on pattern matching - executing `<LIST>` on match =\> [article](/syntax/ccmd/case) |
|
|
| `if <LIST> ; then <LIST> ; else <LIST> ; fi` | The if clause: makes decisions based on exit codes =\> [article](/syntax/ccmd/if_clause) |
|
|
| `while <LIST1> ; do <LIST2> ; done` | Execute `<LIST2>` while `<LIST1>` returns TRUE (exit code) =\> [article](/syntax/ccmd/while_loop) |
|
|
| `until <LIST1> ; do <LIST2> ; done` | Execute `<LIST2>` until `<LIST1>` returns TRUE (exit code) =\> [article](/syntax/ccmd/until_loop) |
|
|
|
|
## Shell Function Definitions
|
|
|
|
FIXME Missing an additional article about shell functions
|
|
|
|
A shell function definition makes a [compound
|
|
command](basicgrammar#compound_commands) available via a new name. When
|
|
the function runs, it has its own "private" set of positional parameters
|
|
and I/O descriptors. It acts like a script-within-the-script. Simply
|
|
stated: **You've created a new command.**
|
|
|
|
The definition is easy (one of many possibilities):
|
|
|
|
`<NAME> () <COMPOUND_COMMAND> <REDIRECTIONS>`
|
|
|
|
which is usually used with the `{...; }` compound command, and thus
|
|
looks like:
|
|
|
|
print_help() { echo "Sorry, no help available"; }
|
|
|
|
As above, a function definition can have any [compound
|
|
command](basicgrammar#compound_commands) as a body. Structures like
|
|
|
|
countme() for ((x=1;x<=9;x++)); do echo $x; done
|
|
|
|
are unusual, but perfectly valid, since the for loop construct is a
|
|
compound command!
|
|
|
|
If **redirection** is specified, the redirection is not performed when
|
|
the function is defined. It is performed when the function runs:
|
|
|
|
# this will NOT perform the redirection (at definition time)
|
|
f() { echo ok ; } > file
|
|
|
|
# NOW the redirection will be performed (during EXECUTION of the function)
|
|
f
|
|
|
|
Bash allows three equivalent forms of the function definition:
|
|
|
|
NAME () <COMPOUND_COMMAND> <REDIRECTIONS>
|
|
function NAME () <COMPOUND_COMMAND> <REDIRECTIONS>
|
|
function NAME <COMPOUND_COMMAND> <REDIRECTIONS>
|
|
|
|
The space between `NAME` and `()` is optional, usually you see it
|
|
without the space.
|
|
|
|
I suggest using the first form. It's specified in POSIX and all
|
|
Bourne-like shells seem to support it.
|
|
|
|
<u>**Note:**</u> Before version `2.05-alpha1`, Bash only recognized the
|
|
definition using curly braces (`name() { ... }`), other shells allow the
|
|
definition using **any** command (not just the compound command set).
|
|
|
|
To execute a function like a regular shell script you put it together
|
|
like this:
|
|
|
|
#!/bin/bash
|
|
# Add shebang
|
|
|
|
mycmd()
|
|
{
|
|
# this $1 belongs to the function!
|
|
find / -iname "$1"
|
|
}
|
|
|
|
# this $1 belongs the script itself!
|
|
mycmd "$1" # Execute command immediately after defining function
|
|
|
|
exit 0
|
|
|
|
**Just informational(1):**
|
|
|
|
Internally, for forking, Bash stores function definitions in environment
|
|
variables. Variables with the content "*() ....*".
|
|
|
|
Something similar to the following works without "officially" declaring
|
|
a function:
|
|
|
|
$ export testfn="() { echo test; }"
|
|
$ bash -c testfn
|
|
test
|
|
$
|
|
|
|
**Just informational(2):**
|
|
|
|
It is possible to create function names containing slashes:
|
|
|
|
/bin/ls() {
|
|
echo LS FAKE
|
|
}
|
|
|
|
The elements of this name aren't subject to a path search.
|
|
|
|
Weird function names should not be used. Quote from the maintainer:
|
|
|
|
- * It was a mistake to allow such characters in function names
|
|
(\`unset' doesn't work to unset them without forcing -f, for
|
|
instance). We're stuck with them for backwards compatibility, but I
|
|
don't have to encourage their use. *
|
|
|
|
## Grammar summary
|
|
|
|
- a [simple command](basicgrammar#simple_commands) is just a command and
|
|
its arguments
|
|
- a [pipeline](basicgrammar#pipelines) is one or more [simple
|
|
command](basicgrammar#simple_commands) probably connected in a pipe
|
|
- a [list](basicgrammar#lists) is one or more
|
|
[pipelines](basicgrammar#pipelines) connected by special operators
|
|
- a [compound command](basicgrammar#compound_commands) is a
|
|
[list](basicgrammar#lists) or a special command that forms a new
|
|
meta-command
|
|
- a [function definition](basicgrammar#shell_function_definitions) makes
|
|
a [compound command](basicgrammar#compound_commands) available under a
|
|
new name, and a separate environment
|
|
|
|
## Examples for classification
|
|
|
|
FIXME more...
|
|
|
|
------------------------------------------------------------------------
|
|
|
|
<u>A (very) simple command</u>
|
|
|
|
echo "Hello world..."
|
|
|
|
<u>All of the following are simple commands</u>
|
|
|
|
x=5
|
|
|
|
>tmpfile
|
|
|
|
{x}<"$x" _=${x=<(echo moo)} <&0$(cat <&"$x" >&2)
|
|
|
|
------------------------------------------------------------------------
|
|
|
|
<u>A common compound command</u>
|
|
|
|
if [ -d /data/mp3 ]; then
|
|
cp mymusic.mp3 /data/mp3
|
|
fi
|
|
|
|
- the [compound command](basicgrammar#compound_commands) for the `if`
|
|
clause
|
|
- the [list](basicgrammar#lists) that `if` **checks** actually contains
|
|
the [simple command](basicgrammar#simple_commands) `[ -d /data/mp3 ]`
|
|
- the [list](basicgrammar#lists) that `if` **executes** contains a
|
|
simple command (`cp mymusic.mp3 /data/mp3`)
|
|
|
|
Let's invert test command exit code, only one thing changes:
|
|
|
|
if ! [ -d /data/mp3 ]; then
|
|
cp mymusic.mp3 /data/mp3
|
|
fi
|
|
|
|
- the [list](basicgrammar#lists) that `if` **checks** contains a
|
|
[pipeline](basicgrammar#pipelines) now (because of the `!`)
|
|
|
|
## See also
|
|
|
|
- Internal: [List of compound commands](/syntax/ccmd/intro)
|
|
- Internal: [Parsing and execution of simple
|
|
commands](/syntax/grammar/parser_exec)
|
|
- Internal: [Quoting and escaping](/syntax/quoting)
|
|
- Internal: [Introduction to expansions and
|
|
substitutions](/syntax/expansion/intro)
|
|
- Internal: [Some words about words...](/syntax/words)
|