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.
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.
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 [[syntax:grammar:parser_exec | expand and execute a simple command]].
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 [[basicgrammar##simple_commands | simple commands]] (separated by the ''|'' symbol connects their input and output), for example:
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'':
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 [[commands:builtin:set#attributes | set option pipefail]] 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 ''[[internals:shell_options#lastpipe | 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:
A list is a sequence of one or more [[basicgrammar#pipelines | pipelines]] separated by one of the operators '';'', ''&'', ''&&'', or ''││'', and optionally terminated by one of '';'', ''&'', or ''<newline>''.
|''<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> **<nowiki>||</nowiki>** <PIPELINE2>''|''<PIPELINE1>'' is executed and **only** if its exit code was **not** 0 (FALSE), then ''<PIPELINE2>'' is executed (OR-List)|
|''<nowiki>[[</nowiki> <EXPRESSION> ]]''|Evaluate the conditional expression ''<EXPRESSION>'' (aka "the new test command") => [[syntax:ccmd:conditional_expression | article]]|
|''for <NAME> in <WORDS> ; do <LIST> ; done''|Executes ''<LIST>'' while setting the variable ''<NAME>'' to one of ''<WORDS>'' on every iteration (classic for-loop) => [[syntax:ccmd:classic_for | article]]|
|''select <NAME> in <WORDS> ; do <LIST> ; done''|Provides simple menus => [[syntax:ccmd:user_select | article]]|
|''case <WORD> in <PATTERN>) <LIST> ;; ... esac''|Decisions based on pattern matching - executing ''<LIST>'' on match => [[syntax:ccmd:case | article]]|
|''if <LIST> ; then <LIST> ; else <LIST> ; fi''|The if clause: makes decisions based on exit codes => [[syntax:ccmd:if_clause | article]]|
|''while <LIST1> ; do <LIST2> ; done''|Execute ''<LIST2>'' while ''<LIST1>'' returns TRUE (exit code) => [[syntax:ccmd:while_loop | article]]|
|''until <LIST1> ; do <LIST2> ; done''|Execute ''<LIST2>'' until ''<LIST1>'' returns TRUE (exit code) => [[syntax:ccmd:until_loop | article]]|
FIXME Missing an additional article about shell functions
A shell function definition makes a [[basicgrammar#compound_commands | compound command]] 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):
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.
__**Note:**__ 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:
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 [[basicgrammar#simple_commands | simple command]] is just a command and its arguments
* a [[basicgrammar#pipelines | pipeline]] is one or more [[basicgrammar#simple_commands | simple command]] probably connected in a pipe
* a [[basicgrammar#lists | list]] is one or more [[basicgrammar#pipelines | pipelines]] connected by special operators
* a [[basicgrammar#compound_commands | compound command]] is a [[basicgrammar#lists | list]] or a special command that forms a new meta-command
* a [[basicgrammar#shell_function_definitions | function definition]] makes a [[basicgrammar#compound_commands | compound command]] available under a new name, and a separate environment