First pandoc conversion

This commit is contained in:
Rawiri Blundell 2023-04-16 20:04:24 +12:00
parent ec902fa816
commit 3e5ce3ce31
89 changed files with 18923 additions and 0 deletions

219
bash4.md Normal file
View File

@ -0,0 +1,219 @@
# Bash 4 - a rough overview
:V4:
\<note\>Attention: Since Bash 4 has been around for quite some time now
(4.3 will come soon), I consider it to be "standard". This page is not
maintained anymore and is left here to keep your links working. See the
[bashchanges](/scripting/bashchanges) page for new stuff
introduced.\</note\>
Besides many bugfixes since Bash 3.2, Bash 4 will bring some interesting
new features for shell users and scripters. See also
[bashchanges](/scripting/bashchanges) for a small general overview with
more details.
Not all of the changes and news are included here, just the biggest or
most interesting ones. The changes to completion, and the readline
component are not covered. **Though, if you're familiar with these parts
of Bash (and Bash 4), feel free to write a chapter here.**
The complete list of fixes and changes is in the CHANGES or NEWS file of
your Bash 4 distribution.
\<WRAP center round download 60%\> The current available **stable**
version is 4.4.18 release (February 03, 2018):
- <ftp://ftp.cwru.edu/pub/bash/bash-4.4.18.tar.gz>
- <ftp://ftp.gnu.org/pub/gnu/bash/bash-4.4.18.tar.gz>
\</WRAP\>
## New or changed commands and keywords
### The new "coproc" keyword
Bash 4 introduces the concepts of coprocesses, a well known feature of
other shells. The basic concept is simple: It will start any command in
the background and set up an array that is populated with accessible
files that represent the filedescriptors of the started process.
In other words: It lets you start a process in background and
communicate with its input and output data streams.
See [The coproc keyword](/syntax/keywords/coproc)
### The new "mapfile" builtin
The `mapfile` builtin is able to map the lines of a file directly into
an array. This avoids having to fill an array yourself using a loop. It
enables you to define the range of lines to read, and optionally call a
callback, for example to display a progress bar.
See: [mapfile](/commands/builtin/mapfile)
### Changes to the "case" keyword
The `case` construct understands two new action list terminators:
The `;&` terminator causes execution to continue with the next action
list (rather than terminate the `case` construct).
The `;;&` terminator causes the `case` construct to test the next given
pattern instead of terminating the whole execution.
See [case](/syntax/ccmd/case)
### Changes to the "declare" builtin
The `-p` option now prints all attributes and values of declared
variables (or functions, when used with `-f`). The output is fully
re-usable as input.
The new option `-l` declares a variable in a way that the content is
converted to lowercase on assignment. For uppercase, the same applies to
`-u`. The option `-c` causes the content to be capitalized before
assignment.
`declare -A` declares associative arrays (see below).
### Changes to the "read" builtin
The `read` builtin command has some interesting new features.
The `-t` option to specify a timeout value has been slightly tuned. It
now accepts fractional values and the special value 0 (zero). When
`-t 0` is specified, `read` immediately returns with an exit status
indicating if there's data waiting or not. However, when a timeout is
given, and the `read` builtin times out, any partial data recieved up to
the timeout is stored in the given variable, rather than lost. When a
timeout is hit, `read` exits with a code greater than 128.
A new option, `-i`, was introduced to be able to preload the input
buffer with some text (when Readline is used, with `-e`). The user is
able to change the text, or press return to accept it.
See [read](/commands/builtin/read)
### Changes to the "help" builtin
The builtin itself didn't change much, but the data displayed is more
structured now. The help texts are in a better format, much easier to
read.
There are two new options: `-d` displays the summary of a help text,
`-m` displays a manpage-like format.
### Changes to the "ulimit" builtin
Besides the use of the 512 bytes blocksize everywhere in POSIX mode,
`ulimit` supports two new limits: `-b` for max socket buffer size and
`-T` for max number of threads.
## Expansions
### Brace Expansion
The brace expansion was tuned to provide expansion results with leading
zeros when requesting a row of numbers.
See [brace](/syntax/expansion/brace)
### Parameter Expansion
Methods to modify the case on expansion time have been added.
On expansion time you can modify the syntax by adding operators to the
parameter name.
See [Case modification on parameter
expansion](/syntax/pe#case_modification)
### Substring expansion
When using substring expansion on the positional parameters, a starting
index of 0 now causes \$0 to be prepended to the list (if the positional
parameters are used). Before, this expansion started with \$1:
# this should display $0 on Bash v4, $1 on Bash v3
echo ${@:0:1}
### Globbing
There's a new shell option `globstar`. When enabled, Bash will perform
recursive globbing on `**` -- this means it matches all directories and
files from the current position in the filesystem, rather than only the
current level.
The new shell option `dirspell` enables spelling corrections on
directory names during globbing.
See [globs](/syntax/expansion/globs)
## Associative Arrays
Besides the classic method of integer indexed arrays, Bash 4 supports
associative arrays.
An associative array is an array indexed by an arbitrary string,
something like
declare -A ASSOC
ASSOC[First]=&quot;first element&quot;
ASSOC[Hello]=&quot;second element&quot;
ASSOC[Peter Pan]=&quot;A weird guy&quot;
See [arrays](/syntax/arrays)
## Redirection
There is a new `&>>` redirection operator, which appends the standard
output and standard error to the named file. This is the same as the
good old `>>FILE 2>&1` notation.
The parser now understands `|&` as a synonym for `2>&1 |`, which
redirects the standard error for a command through a pipe.
See [redirection](/syntax/redirection)
## Interesting new shell variables
| Variable | Description |
|----------------------------------------------------|--------------------------------------------------------------------------------|
| [BASHPID](/syntax/shellvars#BASHPID) | contains the PID of the current shell (this is different than what `$$` does!) |
| [PROMPT_DIRTRIM](/syntax/shellvars#PROMPT_DIRTRIM) | specifies the max. level of unshortened pathname elements in the prompt |
| [FUNCNEST](/syntax/shellvars#FUNCNEST) | control the maximum number of shell function recursions |
See [shellvars](/syntax/shellvars)
## Interesting new Shell Options
The mentioned shell options are **off by default** unless otherwise
mentioned.
| Option | Description |
|-------------|--------------------------------------------------------------------------------------------------------|
| `checkjobs` | check for and report any running jobs at shell exit |
| `compat*` | set compatiblity modes for older shell versions (influences regular expression matching in `[[ ... ]]` |
| `dirspell` | enables spelling corrections on directory names during globbing |
| `globstar` | enables recursive globbing with `**` |
| `lastpipe` | (4.2) to execute the last command in a pipeline in the current environment |
See [shell_options](/internals/shell_options)
## Misc
- If a command is not found, the shell attempts to execute a shell
function named `command_not_found_handle`, supplying the command words
as the function arguments. This can be used to display userfriendly
messages or perform different command searches
- The behaviour of the `set -e` (`errexit`) mode was changed, it now
acts more intuitive (and is better documented in the manpage).
- The output target for the `xtrace` (`set -x`/`set +x`) feature is
configurable **since Bash 4.1** (previously, it was fixed to
`stderr`): a variable named
[BASH_XTRACEFD](/syntax/shellvars#BASH_XTRACEFD) can be set to the
filedescriptor that should get the output
- Bash 4.1 is able to log the history to syslog (only to be enabled at
compile time in `config-top.h`)

View File

@ -0,0 +1,80 @@
# The caller builtin command
## Synopsis
caller [FRAMENUMBER]
## Description
The `caller` builtin command is used to print execution frames of
subroutine calls. Without giving a framenumber, the topmost execution
frame information is printed ("who called me") wile linenumber and
filename.
When an execution frame number is given (0 - topmost), the linenumber,
the subroutine (function) and the filename is printed. When an invalid
execution frame number is given, it exists `FALSE`. This way it can be
used in a loop (see the examples section below).
## Examples
### Simple stack trace
The code below defines a function `die` that is used to exit the
program. It prints a list of execution frames, starting with the topmost
frame (0). The topmost frame is the "caller of the die function", in
this case function "f1".
This way, you can print a "stack trace" for debugging or logging
purposes.
The code is made very simple, just to show the basic purposes.
``` bash
#!/bin/bash
die() {
local frame=0
while caller $frame; do
((++frame));
done
echo &quot;$*&quot;
exit 1
}
f1() { die &quot;*** an error occured ***&quot;; }
f2() { f1; }
f3() { f2; }
f3
```
**Output**
12 f1 ./callertest.sh
13 f2 ./callertest.sh
14 f3 ./callertest.sh
16 main ./callertest.sh
*** an error occured ***
## Notes
- `caller` produces no output unless used within a script that's run
from a real file. It isn't particularly useful for interactive use,
but can be used to create a decent `die` function to track down errors
in moderately complex scripts.
`{ bash /dev/stdin; } <<<$'f(){ g; }\ng(){ h; }\nh(){ while caller $((n++)); do :; done; }\nf'`
- For more sophisticated debugging, Bash extended debugging features are
available and a number of special parameters that give more detail
than caller (e.g. BASH_ARG{C,V}). Tools such as
[Bashdb](http://bashdb.sourceforge.net/) can assist in using some of
Bash's more advanced debug features.
- The Bash manpage and help text specifies that the argument to `caller`
is an "expr" (whatever that means). Only an integer is actually
allowed, with no special interpretation of an "expression" as far as
we can tell.
## Portability considerations
- `caller` is not specified by POSIX(R)
- the `caller` builtin command appeared in Bash version 3.0

View File

@ -0,0 +1,139 @@
\<label class="block control-label"
for="focus<u>this"\>\<span\>Username\</span\> \<input type="text"
id="focus</u>this" name="u" class="edit
form-control"/\>\</label\>\<br/\> \<label class="block
control-label"\>\<span\>Password\</span\> \<input type="password"
name="p" class="edit form-control"/\>\</label\>\<br/\> \<label
class="simple control-label" for="remember<u>me"\>\<input
type="checkbox" id="remember</u>me" name="r" value="1"
class="checkbox-inline"/\> \<span\>Remember me\</span\>\</label\> \<div
id="plugin<u>captcha_wrapper"\>\<input type="hidden"
name="6e809c536862962bdd4db54ddd356008"
value="AMMohIAc6LXIfPoUSz5NVCj8gp9c8TyOgzc39+zbTCs="/\>\<label
for="plugin</u>captcha" class="control-label"\>Please fill all the
letters into the box to prove you're human.\</label\> \<img
src="/web/20211028115259im\_/<https://wiki.bash-hackers.org/lib/plugins/captcha/img.php?secret=AMMohIAc6LXIfPoUSz5NVCj8gp9c8TyOgzc39%2BzbTCs%3D&id=commands:builtin:continuebreak>"
width="160" height="40" alt=""/\> \<input type="text" size="5"
name="7e36cb9dbf5259df07958854aeed7af8" class="edit form-control"/\>
\<label class="no control-label"\>Please keep this field empty: \<input
type="text" name="8249e841928ac9fb9ae040198a19711b"
class="form-control"/\>\</label\>\</div\>\<button type="submit"
class="btn btn-success btn btn-default"\>\<span class="iconify"
data-icon="mdi:lock"\>\</span\> Log In\</button\> \</fieldset\> \<p\>You
don't have an account yet? Just get one: \<a
href="/web/20211028115259/<https://wiki.bash-hackers.org/commands/builtin/continuebreak?do=register>"
title="Register" rel="nofollow"
class="register"\>Register\</a\>\</p\>\<p\>Forgotten your password? Get
a new one: \<a
href="/web/20211028115259/<https://wiki.bash-hackers.org/commands/builtin/continuebreak?do=resendpwd>"
title="Set new password" rel="nofollow" class="resendpwd"\>Set new
password\</a\>\</p\>\</div\>\</form\> \</div\> \</div\>\<!-- /content
--\>\</div\>
</div>
</div>
<div class="small text-right">
<span class="docInfo">
</span>
</div>
</article>
</div>
</main>
<footer id="dw__footer" class="dw-container py-5 dokuwiki container-fluid">
<hr/>
\<div align="center"\> \<h3\>\<a target="\_blank"
href="<http://web.archive.org/web/20211028115259/http://www.performing-databases.com/>"\>This
site is supported by Performing Databases - your experts for database
administration\</a\>\</h3\> \</div\> \<hr/\> \<div align="center"\>
\<script async
src="//web.archive.org/web/20211028115259js\_/<https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js>"\>\</script\>
\<!-- BHORG_BOTTOM --\> \<ins class="adsbygoogle"
style="display:inline-block;width:728px;height:90px"
data-ad-client="ca-pub-4658830517838678"
data-ad-slot="1603598940"\>\</ins\> \<script\> (adsbygoogle =
window.adsbygoogle \|\| \[\]).push({}); \</script\> \</div\>
\<!-- footer --\> \<div class="dw-container small container-fluid
mx-5"\>
<div class="footer-dw-title">
<div class="media">
<div class="media-left">
<img src="/web/20211028115259im_/https://wiki.bash-hackers.org/lib/tpl/bootstrap3/images/logo.png" alt="Bash Hackers Wiki" class="media-object" style="height:32px"/>
</div>
<div class="media-body">
<div class="row">
<div class="col-sm-2">
<h4 class="media-heading">Bash Hackers Wiki</h4>
<p>
</p>
</div>
<div class="col-sm-10">
</div>
</div>
</div>
</div>
</div>
<div class="footer-license row">
<hr/>
<div id="dw__license" class="col-sm-6">
<p>
<a href="http://web.archive.org/web/20211028115259/http://www.gnu.org/licenses/fdl-1.3.html" title="GNU Free Documentation License 1.3" target="" itemscope itemtype="http://schema.org/CreativeWork" itemprop="license" rel="license" class="license"><img src="/web/20211028115259im_/https://wiki.bash-hackers.org/lib/tpl/bootstrap3/images/license/gnufdl.png" width="24" height="24" alt="gnufdl"/> </a> </p>
<p class="small">
Except where otherwise noted, content on this wiki is licensed under the following license:<br/><a href="http://web.archive.org/web/20211028115259/http://www.gnu.org/licenses/fdl-1.3.html" title="GNU Free Documentation License 1.3" target="" itemscope itemtype="http://schema.org/CreativeWork" itemprop="license" rel="license" class="license">GNU Free Documentation License 1.3</a> </p>
</div>
<div class="col-sm-6">
</div>
</div>
\</div\> \<!-- /footer --\>
</footer>
<a href="#dokuwiki__top" class="back-to-top hidden-print btn btn-default" title="skip to content" accesskey="t">
<span class="iconify" data-icon="mdi:chevron-up"></span> </a>
<div id="screen__mode"> <span class="visible-xs-block"></span>
<span class="visible-sm-block"></span>
<span class="visible-md-block"></span>
<span class="visible-lg-block"></span>
</div>
<img src="/web/20211028115259im_/https://wiki.bash-hackers.org/lib/exe/indexer.php?id=commands%3Abuiltin%3Acontinuebreak&1635421979" width="2" height="1" alt=""/>
\</div\>
\</body\> \</html\> \<!--
FILE ARCHIVED ON 11:52:59 Oct 28, 2021 AND RETRIEVED FROM THE
INTERNET ARCHIVE ON 11:11:51 Apr 15, 2023.
JAVASCRIPT APPENDED BY WAYBACK MACHINE, COPYRIGHT INTERNET ARCHIVE.
ALL OTHER CONTENT MAY ALSO BE PROTECTED BY COPYRIGHT (17 U.S.C.
SECTION 108(a)(3)).
--\> \<!-- playback timings (ms):
captures_list: 161.849
exclusion.robots: 0.11
exclusion.robots.policy: 0.101
RedisCDXSource: 0.556
esindex: 0.009
LoadShardBlock: 142.254 (3)
PetaboxLoader3.datanode: 168.042 (4)
load_resource: 156.33
PetaboxLoader3.resolve: 105.224
--\>

160
commands/builtin/declare.md Normal file
View File

@ -0,0 +1,160 @@
# The declare builtin command
## Synopsis
declare [-aAfFgilrtux] [-p] [NAME[=VALUE] ...]
# obsolete typeset synonym
typeset [-aAfFgilrtux] [-p] [NAME[=VALUE] ...]
## Description
`declare` is used to display or set variables along with variable
attributes. When used to display variables/functions and their value,
the output is re-usable as input for the shell.
If no `NAME` is given, it displays the values of all variables or
functions when restricted by the `-f` option.
If `NAME` is followed by `=VALUE`, `declare` also sets the value for a
variable.
When used in a function, `declare` makes `NAMEs` local variables, unless
used with the `-g` option.
Don't use it's synonym `typeset` when coding for Bash, since it's tagged
as obsolete.
### Options
Below, `[-+]X` indicates an attribute, use `-X` to set the attribute,
`+X` to remove it.
| Option | Description |
|:--------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `[-+]a` | make NAMEs indexed arrays (removing with `+a` is valid syntax, but leads to an error message) |
| `[-+]A` | make NAMEs associative arrays |
| `[-+]c` | **Undocumented** convert NAMEs to "capcase" on assignment (makes the first letter upper-case and the rest lower). Requires Bash built with `-DCASEMOD_CAPCASE` |
| `-f` | restrict action or display to function names and definitions (removing with `+f` is valid syntax, but leads to an error message) |
| `-F` | restrict display to function names only (plus line number and source file when debugging) |
| `-g` | create global variables when used in a shell function; otherwise ignored (by default, `declare` declares local scope variables when used in shell functions) |
| `[-+]i` | make NAMEs have the "integer" attribute |
| `[-+]l` | convert NAMEs to lower case on assignment (makes sure the variable contains only lower case letters) |
| `[-+]n` | make NAME a reference to the variable named by its value. Introduced in Bash 4.3-alpha |
| `-p` | display the attributes and value of each NAME |
| `[-+]r` | make NAMEs readonly (removing with `+r` is valid syntax, but not possible) |
| `[-+]t` | make NAMEs have the "trace" attribute (effective only for functions) |
| `[-+]u` | convert NAMEs to upper case on assignment (makes sure the variable contains only upper case letters) |
| `[-+]x` | make NAMEs exported |
### Return status
| Status | Reason |
|:-------|:---------------------------------------------------------------------------------------|
| 0 | no error |
| != 0 | invalid option |
| != 0 | invalid variable name given |
| != 0 | attempt to **define** a function using `-f` |
| != 0 | assignment to a readonly variable |
| != 0 | removing the readonly-attribute from a readonly variable |
| != 0 | assignment to an array variable without the compound assignment syntax (`array=(...)`) |
| != 0 | attempt to use `+a` to "destroy" an array |
| != 0 | attemt to display a non-existent function with `-f` |
## Notes
Unix shells offer very few datatypes. Bash and some other shells extend
this by allowing "attributes" to be set on variable names. The only
attributes specified by POSIX are `export` and `readonly`, which are set
by their own dedicated builtins. Datatypes in bash have a few other
interesting capabilities such as the ability to modify data on
assignment.
## Examples
### Display defined functions
`declare -f` can be used to display all defined functions...
$ declare -f
foo ()
{
echo &quot;FOO is BAR&quot;
}
world ()
{
echo &quot;Hello World!&quot;
}
...or just a specific defined function.
$ declare -f foo
foo ()
{
echo &quot;FOO is BAR&quot;
}
### Nameref
Bash 4.3 adds a new way to indirectly reference variables. `typeset -n`
can be used to make a variable indirectly refer to another. In Bash, the
lvalue of the assignment given to `typeset -n` will refer to the
variable whose name is expanded on the RHS.
# Sum a set of arrays and assign the result indirectly, also printing each intermediary result (without portability workarounds)
# sum name arrname [ arrname ... ]
function sum {
typeset -n _result=$1 _arr
typeset IFS=+
_result=0
for _arr in &quot;${@:2}&quot;; do # Demonstrate the special property of &quot;for&quot; on a nameref.
(( _result += ${_arr[*]} ))
printf '%s = %d\n' &quot;${!_result}&quot; &quot;$_result&quot; # Demonstrate the special property of ${!ref} on a nameref.
done
}
a=(1 2 3) b=(6 5 4) c=(2 4 6)
sum total a b c
printf 'Final value of &quot;total&quot; is: %d\n' &quot;$total&quot;
\<div hide\> function sum {
typeset -n _result=$1
shift
typeset IFS=+ _arrx
_result=0
for _arrx in &quot;$@&quot;; do # Demonstrate the special property of &quot;for&quot; on a nameref.
typeset -n _arr=$_arrx
(( _result += ${_arr[*]} ))
printf '%s = %d\n' &quot;${!_result}&quot; &quot;$_result&quot; # Demonstrate the special property of ${!ref} on a nameref.
done
}
a=(1 2 3); b=(6 5 4); c=(2 4 6) sum total a b c printf 'Final value of
"total" is: %d\n' "\$total" \</div\>
`typeset -n` is currently implemented in ksh93, mksh, and Bash 4.3. Bash
and mksh's implementations are quite similar, but much different from
ksh93's. See [Portability considerations](#portability_considerations)
for details. ksh93 namerefs are much more powerful than Bash's.
## Portability considerations
- `declare` is not specified by POSIX(r)
- `declare` is unique to Bash and totally non-portable with the possible
exception of Zsh in Bash compatibility mode. Bash marks the synonym
`typeset` as obsolete, which in Bash behaves identically to `declare`.
All other Korn-like shells use `typeset`, so it probably isn't going
away any time soon. Unfortunately, being a non-standard builtin,
`typeset` differs significantly between shells. ksh93 also considers
`typeset` a special builtin, while Bash does not - even in POSIX mode.
If you use `typeset`, you should attempt to only use it in portable
ways.
- **todo** nameref portability...
## See also
- [arrays](/syntax/arrays)
- [readonly](/commands/builtin/readonly)
- [unset](/commands/builtin/unset)

88
commands/builtin/echo.md Normal file
View File

@ -0,0 +1,88 @@
# The echo builtin command
## Synopsis
echo [-neE] [arg ...]
## Description
`echo` outputs it's args to stdout, separated by spaces, followed by a
newline. The return status is always `0`. If the
[shopt](/commands/builtin/shopt) option `xpg_echo` is set, Bash
dynamically determines whether echo should expand escape characters
(listed below) by default based on the current platform. `echo` doesn't
interpret `--` as the end of options, and will simply print this string
if given.
### Options
| Option | Description |
|:-------|:---------------------------------------------------------------------------------------------------------------|
| `-n` | The trailing newline is suppressed. |
| `-e` | Interpretation of the following backslash-escaped characters (below) is enabled. |
| `-E` | Disables the interpretation of these escape characters, even on systems where they are interpreted by default. |
### Escape sequences
| Escape | Description |
|:-------------|:--------------------------------------------------------------------------------------------------------------|
| `\a` | alert (bell) |
| `\b` | backspace |
| `\c` | suppress further output |
| `\e` | |
| `\E` | an escape character |
| `\f` | form feed |
| `\n` | new line |
| `\r` | carriage return |
| `\t` | horizontal tab |
| `\v` | vertical tab |
| `\\` | backslash |
| `\0nnn` | the eight-bit character whose value is the octal value nnn (zero to three octal digits) |
| `\xHH` | the eight-bit character whose value is the hexadecimal value HH (one or two hex digits) |
| `\uHHHH` | the Unicode (ISO/IEC 10646) character whose value is the hexadecimal value HHHH (one to four hex digits) |
| `\UHHHHHHHH` | the Unicode (ISO/IEC 10646) character whose value is the hexadecimal value HHHHHHHH (one to eight hex digits) |
## Examples
## Portability considerations
- `echo` is a portability train wreck. No major shell follows POSIX
completely, and any shell that attempts to do so should be considered
horribly broken.
[SUSv4](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/echo.html#tag_20_37)
specifies that `echo` **shall not** include any options. Further, it
specifies that the behavior of `-n` as a first argument shall be
determined by the implementation, unless XSI is followed, in which
case `-n` is always treated as a string, and backslash escapes are
interpreted by default. `dash` has the misfeature of following this
and interpreting escapes by default, but includes a `-n` feature for
suppressing newlines nevertheless.
In practice, if you're able to assume a korn-like shell including
bash, mksh, or zsh, `echo` when used in simple cases is generally
reliable. For example, in the very common situation in which echo is
supplied with a single argument and whose output is to have a newline
appended, using `echo` is considered common practice.
<!-- -->
- **Never use options to `echo`! *Ever*!** Any time you feel tempted to
use `echo -e`, `-n`, or any other special feature of echo, **use
[printf](/commands/builtin/printf) instead!** If portability is a
requirement, you should consider using `printf` *exclusively* and just
ignore that `echo` even exists. If you must use `echo -e` and refuse
to use `printf`, it is usually acceptable to use ''echo \$'...' ''if
targeting only shells that support this special quoting style.
<!-- -->
- `ksh93` has a `print` command, which if coding specifically for
`ksh93` should be preferred over `echo`.
[printf](/commands/builtin/printf) still includes most of the
functionality of both, and should usually be the most preferred
option.
## See also
- [printf](/commands/builtin/printf)
- <http://cfajohnson.com/shell/cus-faq.html#Q0b>

170
commands/builtin/eval.md Normal file
View File

@ -0,0 +1,170 @@
# The eval builtin command
## Synopsis
eval: eval [arg ...]
## Description
`eval` takes its arguments, concatenates them separated by spaces, and
executes the resulting string as Bash code in the current execution
environment. `eval` in Bash works in essentially the same way as most
other languages that have an `eval` function. Perhaps the easiest way to
think about `eval` is that it works in the same way as running ''bash -c
"bash code..." ''from a script, except in the case of `eval`, the given
code is executed in the current shell environment rather than a child
process.
## Examples
In this example, the literal text within the
[here-document](/syntax/redirection#here_documents) is executed as Bash
code exactly as though it were to appear within the script in place of
the `eval` command below it.
#!/usr/bin/env bash
{ myCode=$(</dev/stdin); } <<\EOF
... arbitrary bash code here ...
EOF
eval &quot;$myCode&quot;
### Expansion side-effects
Frequently, `eval` is used to cause side-effects by performing a pass of
expansion on the code before executing the resulting string. This allows
for things that otherwise wouldn't be possible with ordinary Bash
syntax. This also, of course, makes `eval` the most powerful command in
all of shell programming (and in most other languages for that matter).
This code defines a set of identical functions using the supplied names.
`eval` is the only way to achieve this effect.
main() {
local fun='() { echo &quot;$FUNCNAME&quot;; }' x
for x in {f..n}; do
eval &quot;${x}${fun}&quot;
done
&quot;$@&quot;
}
main &quot;$@&quot;
### Using printf %q
The `printf %q` format string performs shell escaping on its arguments.
This makes `printf %q` the "anti-eval" - with each pass of a string
through printf requiring another `eval` to peel off the escaping again.
while (( ++n <= 5 )) || ! evalBall=&quot;eval $evalBall&quot;; do
printf -v evalBall 'eval %q' &quot;printf $n;${evalBall-printf '0\n'}&quot;
done
$evalBall
The above example is mostly fun and games but illustrates the
`printf %q` property.
### Higher-order functions
Since all current POSIX-compatible shells lack support for [first-class
functions](http://en.wikipedia.org/wiki/First-class_function), it can be
tempting and sometimes useful to simulate some of their effect using
`eval` to evaluate a string containing code.
This example shows [partial
application](http://en.wikipedia.org/wiki/Partial_application) using
`eval`.
function partial {
eval shift 2 \; function &quot;$1&quot; \{ &quot;$2&quot; &quot;$(printf '%q ' &quot;${@:3}&quot;)&quot; '&quot;$@&quot;; }'
}
function repeat {
[[ $1 == +([0-9]) ]] || return
typeset n
while ((n++ < $1)); do
&quot;${@:2}&quot;
done
}
partial print3 repeat 3 printf '%s ' # Create a new function named print3
print3 hi # Print &quot;hi&quot; 3 times
echo
This is very easy to do incorrectly and not usually considered idiomatic
of Bash if used extensively. However abstracting eval behind functions
that validate their input and/or make clear which input must be
controlled carefully by the caller is a good way to use it.
## Portability considerations
- Unfortunately, because eval is a **special builtin**, it only gets its
own environment in Bash, and only when Bash is not in POSIX mode. In
all other shells plus Bash in POSIX mode, the environment of eval will
leak out into the surrounding environment. It is possible to work
around this limitation by prefixing special builtins with the
`command` regular builtin, but current versions of ~~ksh93~~ and zsh
don't do this properly
([fixed](http://article.gmane.org/gmane.comp.programming.tools.ast.devel/686)
in ksh 93v- 2012-10-24 alpha). Earlier versions of zsh work (with
`setopt POSIX_BUILTINS` -- looks like a regression). This works
correctly in Bash POSIX mode, Dash, and mksh.
<!-- -->
- `eval` is another one of the few Bash builtins with keyword-like
conditional parsing of arguments that are in the form of compound
assignments.
<!-- -->
$ ( eval a=( a b\\ c d ); printf '<%s> ' &quot;${a[@]}&quot;; echo ) # Only works in Bash.
<a> <b c> <d>
$ ( x=a; eval &quot;$x&quot;=( a b\\ c d ); printf '<%s> ' &quot;${a[@]}&quot;; echo ) # Argument is no longer in the form of a valid assignment, therefore ordinary parsing rules apply.
-bash: syntax error near unexpected token `('
$ ( x=a; eval &quot;$x&quot;'=( a b\ c d )'; printf '<%s> ' &quot;${a[@]}&quot;; echo ) # Proper quoting then gives us the expected results.
<a> <b c> <d>
We don't know why Bash does this. Since parentheses are metacharacters,
they must ordinary be quoted or escaped when used as arguments. The
first example above is the same error as the second in all non-Bash
shells, even those with compound assignment.
In the case of `eval` it isn't recommended to use this behavior, because
unlike e.g. [declare](commands/builtin/declare), the initial expansion
is still subject to all expansions including
[word-splitting](syntax/expansion/wordsplit) and [pathname
expansion](syntax/expansion/glob).
$ ( set -x; touch 'x+=(\[[123]\]=*)' 'x+=([3]=yo)'; eval x+=(*); echo &quot;${x[@]}&quot; )
+ touch 'x+=(\[[123]\]=*)' 'x+=([3]=yo)'
+ eval 'x+=(\[[123]\]=*)' 'x+=([3]=yo)'
++ x+=(\[[123]\]=*)
++ x+=([3]=yo)
+ echo '[[123]]=*' yo
[[123]]=* yo
Other commands known to be affected by compound assignment arguments
include: [let](commands/builtin/let),
[declare](commands/builtin/declare),
[typeset](commands/builtin/typeset), [local](commands/builtin/local),
[export](commands/builtin/export), and
[readonly](commands/builtin/readonly). More oddities below show both
similarities and differences to commands like
[declare](commands/builtin/declare). The rules for `eval` appear
identical to those of [let](commands/builtin/let).
## See also
- [BashFAQ 48 - eval and security
issues](http://mywiki.wooledge.org/BashFAQ/048) -- **IMPORTANT**
- [Another eval
article](http://fvue.nl/wiki/Bash:_Why_use_eval_with_variable_expansion%3F)
- [Indirection via
eval](http://mywiki.wooledge.org/BashFAQ/006#Assigning_indirect.2BAC8-reference_variables)
- [More indirection via
eval](http://fvue.nl/wiki/Bash:_Passing_variables_by_reference)
- [Martin Väth's "push"](https://github.com/vaeth/push) -- `printf %q`
work-alike for POSIX.

85
commands/builtin/exec.md Normal file
View File

@ -0,0 +1,85 @@
# The exec builtin command
## Synopsis
exec [-a NAME] [-cl] [COMMAND] [ARG...] [REDIRECTION...]
## Description
The `exec` builtin command is used to
- **replace** the shell with a given program (executing it, **not as new
process**)
- set redirections for the program to execute or for the current shell
If only redirections are given, the redirections affect the current
shell without executing any program.
### Options
| Option | Description |
|-----------|----------------------------------------------------------------------------------------------------------------------|
| `-a NAME` | Passes `NAME` as zeroth argument for the program to be executed |
| `-c` | Execute the program with an empty (cleared) environment |
| `-l` | Prepends a dash (`-`) to the zeroth argument of the program to be executed, similar to what the `login` program does |
### Exit status
- on redirection errors it returns 1, otherwise 0
- on exec failures:
- a non-interactive shell terminates; if the [shell option
execfail](/internals/shell_options#execfail) is set `exec` returns
failure
- in an interactive shell, `exec` returns failure
## Examples
### Wrapper around a program
``` bash
myprog=/bin/ls
echo &quot;This is the wrapper script, it will exec $myprog&quot;
# do some vodoo here, probably change the arguments etc.
# well, stuff a wrapper is there for
exec &quot;$myprog&quot; &quot;$@&quot;
```
### Open a file as input for the script
``` bash
# open it
exec 3< input.txt
# for example: read one line from the file(-descriptor)
read -u 3 LINE
# or
read LINE <&3
# finally, close it
exec 3<&-
```
### Overall script logfile
To redirect the whole `stdout` and `stderr` of the shell or shellscript
to a file, you can use the `exec` builtin command:
``` bash
exec >/var/adm/my.log 2>&1
# script continues here...
```
## Portability considerations
*POSIX(r) specifies error code ranges:
* if ''exec'' can't find the program to execute, the error code shall be 126
* on a redirection error, the error code shall be between 1 and 125
* the ''-a NAME'' option appeared in Bash 4.2-alpha
* POSIX(r) does **not** specify any options for ''exec'' (like ''-c'', ''-l'', ''-a NAME'').
## See also
- [redirection](/syntax/redirection)

46
commands/builtin/exit.md Normal file
View File

@ -0,0 +1,46 @@
# The exit builtin command
## Synopsis
exit [N]
## Description
The `exit` command terminates the current shell (or script).
If `N` is given, the return code to the parent process is set to `N`. If
not, the returned status the the status of the most recently executed
command (i.e. `$?`).
A [trap](/commands/builtin/trap) on `EXIT` is executed before the shell
exits, except the executed `exit` command is part of an already running
trap.
### Options
There are no options.
### Exit status
Naturally, you can't ask for the exit status from within the shell that
executed the `exit` command, because the shell exits.
| Status | Reason |
|:-------|:---------------------------------------------------------------------------|
| 255 | invalid (e.g. non-numeric) argument - this staus is returned to the parent |
## Examples
### Exit the shell and explicitely set its exit status
exit 3
## Portability considerations
- if `N` is specified, but its value is not between 0 and 255
inclusively, the exit status is undefined.
## See also
- [The trap builtin command](/commands/builtin/trap)
- [The exit status](/dict/terms/exit_status)

View File

@ -0,0 +1,52 @@
# The export builtin command
## Synopsis
export [-fn] [NAME[=VALUE] ...]
export -p
## Description
The `export` builtin command is used to mark variables or functions
referenced by `NAME` for automatic export to the environment. If `NAME`
is a shell variable, a value `VALUE` can be assigned before exporting
it.
### Options
| Option | Description |
|:-------|:-----------------------------------------------------------------------------------------------------------|
| `-f` | refer to shell functions |
| `-n` | remove the export property from any referenced `NAME` |
| `-p` | print all exported variables, with `-f`, print all exported functions - all in a format re-usable as input |
An argument of `--` disables further option processing.
### Return status
| Status | Reason |
|:-------|:--------------------------|
| 0 | no error |
| !=0 | invalid option |
| !=0 | a given `NAME` is invalid |
## Examples
Set the display to use when launching a GUI application (useful during
SSH sessions):
export DISPLAY=&quot;:0&quot;
Set your default text editor (e.g. SublimeText):
export EDITOR=subl
## Portability considerations
- in POSIX(r), only the `-p` option is specified
- in POSIX(r), only variables (with value assignment) are to be
exported, not shell functions
## See also
- [declare](/commands/builtin/declare)

139
commands/builtin/false.md Normal file
View File

@ -0,0 +1,139 @@
\<label class="block control-label"
for="focus<u>this"\>\<span\>Username\</span\> \<input type="text"
id="focus</u>this" name="u" class="edit
form-control"/\>\</label\>\<br/\> \<label class="block
control-label"\>\<span\>Password\</span\> \<input type="password"
name="p" class="edit form-control"/\>\</label\>\<br/\> \<label
class="simple control-label" for="remember<u>me"\>\<input
type="checkbox" id="remember</u>me" name="r" value="1"
class="checkbox-inline"/\> \<span\>Remember me\</span\>\</label\> \<div
id="plugin<u>captcha_wrapper"\>\<input type="hidden"
name="13ecbdf86c5379f064d2ee7d7cfc9263"
value="guYldxsoHtyI6Sc3BwVa2fZOrJpGvuyc+1/gLza+u+c="/\>\<label
for="plugin</u>captcha" class="control-label"\>Please fill all the
letters into the box to prove you're human.\</label\> \<img
src="/web/20211127235659im\_/<https://wiki.bash-hackers.org/lib/plugins/captcha/img.php?secret=guYldxsoHtyI6Sc3BwVa2fZOrJpGvuyc%2B1%2FgLza%2Bu%2Bc%3D&id=commands:builtin:false>"
width="160" height="40" alt=""/\> \<input type="text" size="5"
name="39a7b6227de0dfa3ad2e6cd2892b501d" class="edit form-control"/\>
\<label class="no control-label"\>Please keep this field empty: \<input
type="text" name="2d1da72a29a3ddc9ad8739c90a71b911"
class="form-control"/\>\</label\>\</div\>\<button type="submit"
class="btn btn-success btn btn-default"\>\<span class="iconify"
data-icon="mdi:lock"\>\</span\> Log In\</button\> \</fieldset\> \<p\>You
don't have an account yet? Just get one: \<a
href="/web/20211127235659/<https://wiki.bash-hackers.org/commands/builtin/false?do=register>"
title="Register" rel="nofollow"
class="register"\>Register\</a\>\</p\>\<p\>Forgotten your password? Get
a new one: \<a
href="/web/20211127235659/<https://wiki.bash-hackers.org/commands/builtin/false?do=resendpwd>"
title="Set new password" rel="nofollow" class="resendpwd"\>Set new
password\</a\>\</p\>\</div\>\</form\> \</div\> \</div\>\<!-- /content
--\>\</div\>
</div>
</div>
<div class="small text-right">
<span class="docInfo">
</span>
</div>
</article>
</div>
</main>
<footer id="dw__footer" class="dw-container py-5 dokuwiki container-fluid">
<hr/>
\<div align="center"\> \<h3\>\<a target="\_blank"
href="<http://web.archive.org/web/20211127235659/http://www.performing-databases.com/>"\>This
site is supported by Performing Databases - your experts for database
administration\</a\>\</h3\> \</div\> \<hr/\> \<div align="center"\>
\<script async
src="//web.archive.org/web/20211127235659js\_/<https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js>"\>\</script\>
\<!-- BHORG_BOTTOM --\> \<ins class="adsbygoogle"
style="display:inline-block;width:728px;height:90px"
data-ad-client="ca-pub-4658830517838678"
data-ad-slot="1603598940"\>\</ins\> \<script\> (adsbygoogle =
window.adsbygoogle \|\| \[\]).push({}); \</script\> \</div\>
\<!-- footer --\> \<div class="dw-container small container-fluid
mx-5"\>
<div class="footer-dw-title">
<div class="media">
<div class="media-left">
<img src="/web/20211127235659im_/https://wiki.bash-hackers.org/lib/tpl/bootstrap3/images/logo.png" alt="Bash Hackers Wiki" class="media-object" style="height:32px"/>
</div>
<div class="media-body">
<div class="row">
<div class="col-sm-2">
<h4 class="media-heading">Bash Hackers Wiki</h4>
<p>
</p>
</div>
<div class="col-sm-10">
</div>
</div>
</div>
</div>
</div>
<div class="footer-license row">
<hr/>
<div id="dw__license" class="col-sm-6">
<p>
<a href="http://web.archive.org/web/20211127235659/http://www.gnu.org/licenses/fdl-1.3.html" title="GNU Free Documentation License 1.3" target="" itemscope itemtype="http://schema.org/CreativeWork" itemprop="license" rel="license" class="license"><img src="/web/20211127235659im_/https://wiki.bash-hackers.org/lib/tpl/bootstrap3/images/license/gnufdl.png" width="24" height="24" alt="gnufdl"/> </a> </p>
<p class="small">
Except where otherwise noted, content on this wiki is licensed under the following license:<br/><a href="http://web.archive.org/web/20211127235659/http://www.gnu.org/licenses/fdl-1.3.html" title="GNU Free Documentation License 1.3" target="" itemscope itemtype="http://schema.org/CreativeWork" itemprop="license" rel="license" class="license">GNU Free Documentation License 1.3</a> </p>
</div>
<div class="col-sm-6">
</div>
</div>
\</div\> \<!-- /footer --\>
</footer>
<a href="#dokuwiki__top" class="back-to-top hidden-print btn btn-default" title="skip to content" accesskey="t">
<span class="iconify" data-icon="mdi:chevron-up"></span> </a>
<div id="screen__mode"> <span class="visible-xs-block"></span>
<span class="visible-sm-block"></span>
<span class="visible-md-block"></span>
<span class="visible-lg-block"></span>
</div>
<img src="/web/20211127235659im_/https://wiki.bash-hackers.org/lib/exe/indexer.php?id=commands%3Abuiltin%3Afalse&1638057419" width="2" height="1" alt=""/>
\</div\>
\</body\> \</html\> \<!--
FILE ARCHIVED ON 23:56:59 Nov 27, 2021 AND RETRIEVED FROM THE
INTERNET ARCHIVE ON 11:11:49 Apr 15, 2023.
JAVASCRIPT APPENDED BY WAYBACK MACHINE, COPYRIGHT INTERNET ARCHIVE.
ALL OTHER CONTENT MAY ALSO BE PROTECTED BY COPYRIGHT (17 U.S.C.
SECTION 108(a)(3)).
--\> \<!-- playback timings (ms):
captures_list: 93.728
exclusion.robots: 0.118
exclusion.robots.policy: 0.109
cdx.remote: 0.057
esindex: 0.008
LoadShardBlock: 67.677 (3)
PetaboxLoader3.datanode: 98.312 (4)
load_resource: 1012.815
PetaboxLoader3.resolve: 957.971
--\>

70
commands/builtin/kill.md Normal file
View File

@ -0,0 +1,70 @@
# The kill builtin command
## Synopsis
kill [-s SIGNAL | -n SIGNALNUMBER | -SIGNAL] PID|JOB
kill -l|-L [SIGNAL...]
## Description
The `kill` command is used to send signals to processes specified by
their `PID` or their `JOB`-specification.
The signal(s) to be specified can have the following formats:
- Numerical: The signal is specified using its constant numeric value.
Be aware that not all systems have identical numbers for the signals.
- Symbolic (long): The signal is specified using the same name that is
used for the constant/macro in the C API (`SIG<name>`)
- Symbolic (short): The signal is specified using the name from the C
API without the `SIG`-prefix (`<name>`)
Without any specified signal, the command sends the `SIGTERM`-signal.
The `kill` command is a Bash builtin command instead of relying on the
external `kill` command of the operating system to
- be able to use shell job specifications instead of Unix process IDs
- be able to send signals ("kill something") also, when your process
limit is reached
### Options
| Option | Description |
|:------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `-s SIGNAL` | specifies the signal to send |
| `-n SIGNALNUMBER` | specifies the signal to send |
| `-SIGNAL` | specifies the signal to send |
| `-l [SIGNAL...]` | Lists supported/known signal numbers and their symbolic name. If `SIGNAL` is given, only list this signal, translated (if a number is given the symbolic name is printed, and vice versa) |
| `-L [SIGNAL...]` | Same as `-l [SIGNAL]` (compatiblity option) |
### Return status
| Status | Reason |
|:-------|:----------------------------------------------------------------------------------------------------|
| 0 | no error/success |
| !=0 | invalid option |
| !=0 | invalid signal specification |
| !=0 | error returned by the system function (e.g. insufficient permissions to send to a specific process) |
## Examples
### List supported signals
kill -l
### Send KILL to a process ID
kill -9 12345
kill -KILL 12345
kill -SIGKILL 12345
## Portability considerations
- POSIX(R) and ISO C only standardize symbolic signal names (no numbers)
and a default action
## See also

110
commands/builtin/let.md Normal file
View File

@ -0,0 +1,110 @@
# The let builtin command
## Synopsis
let arg [arg ...]
## Description
The `let` builtin command evaluates each supplied word from left to
right as an [arithmetic expression](/syntax/arith_expr) and returns an
exit code according to the truth value of the rightmost expression.
- 0 (TRUE) when `arg` evaluated to not 0 (arithmetic "true")
- 1 (FALSE) when `arg` evaluated to 0 (arithmetic "false")
For this return code mapping, please see [this
section](/syntax/arith_expr#arithmetic_expressions_and_return_codes).
They work in the same way as `((`.
## Examples
`let` is very similar to [((](/syntax/ccmd/arithmetic_eval) - the only
difference being `let` is a builtin (simple command), and `((` is a
compound command. The arguments to `let` are therefore subject to all
the same expansions and substitutions as any other simple command -
requiring proper quoting and escaping - whereas the contents of `((`
aren't subject to [word-splitting](/syntax/expansion/wordsplit) or
[pathname expansion](/syntax/expansion/globs) (almost never desirable
for arithmetic). For this reason, **the [arithmetic compound
command](/syntax/ccmd/arithmetic_eval) should generally be preferred
over `let`**.
$ let 'b = a' &quot;(a += 3) + $((a = 1)), b++&quot;
$ echo &quot;$a - $b - $?&quot;
4 - 2 - 0
Is equivalent to the [arithmetic evaluation compound
command](/syntax/ccmd/arithmetic_eval):
$ (( b = a, (a += 3) + $((a = 1)), b++ ))
$ echo &quot;$a - $b - $?&quot;
4 - 2 - 0
\<WRAP info\> Remember that inside arithmetic evaluation contexts, all
other expansions are processed as usual (from left-to-right), and the
resulting text is evaluated as an arithmetic expression. Arithmetic
already has a way to control precedence using parentheses, so it's very
rare to need to nest arithmetic expansions within one another. It's used
above only to illustrate how this precedence works. \</WRAP\>
Unlike `((`, being a simple command `let` has its own environment. In
Bash, built-ins that can set variables process any arithmetic under
their own environment, which makes the variable effectively "local" to
the builtin unless the variable is also set or modified by the builtin.
This differs in other shells, such as ksh93, where environment
assignments to regular builtins are always local even if the variable is
modified by the builtin.
~ $ ( y=1+1 let x=y; declare -p x y )
declare -- x=&quot;2&quot;
bash: declare: y: not found
~ $ ( y=1+1 let x=y++; declare -p x y )
declare -- x=&quot;2&quot;
declare -- y=&quot;3&quot;
This can be useful in certain situations where a temporary variable is
needed.
## Portability considerations
- the `let` command is not specified by POSIX(r). The portable
alternative is: `[ &quot;$(( <EXPRESSION> ))&quot; -ne 0 ]`. To make
portable scripts simpler and cleaner, `let` can be defined as:
`# POSIX
let() {
IFS=, command eval test '$(($*))' -ne 0
}
` Aside from differences in supported arithmetic features, this should
be identical to the Bash/Ksh `let`.
- It seems to be a common misunderstanding that `let` has some legacy
purpose. Both `let` and [[^1]](syntax/ccmd/arithmetic_eval) were ksh88
features and almost identical in terms of portability as everything
that inherited one also tended to get the other. Don't choose `let`
over `((` expecting it to work in more places.
- [expr(1)](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/expr.html#tag_20_42)
is a command one is likely to come across sooner or later. While it is
more "standard" than `let`, the above should always be preferred. Both
[arithmetic expansion](/syntax/arith_expr)s and the `[` test operator
are specified by POSIX(r) and satisfy almost all of expr's use-cases.
Unlike `let`, `expr` cannot assign directly to bash variables but
instead returns a result on stdout. `expr` takes each operator it
recognizes as a separate word and then concatenates them into a single
expression that's evaluated according to it's own rules (which differ
from shell arithmetic). `let` parses each word it recieves on its own
and evaluates it as an expression without generating any output other
than a return code.
- For unknown reasons, `let` is one of the Bash commands with special
parsing for arguments formatted like compound array assignments. See:
[eval](commands/builtin/eval#portability_considerations) for details.
There are no known practical uses for this. Parentheses are treated as
grouping operators and compound assignment is not possible by
arithmetic expressions.
## See also
- Internal: [arithmetic expansion](/syntax/expansion/arith)
- Internal: [arithmetic expressions](/syntax/arith_expr)
[^1]: ...

44
commands/builtin/local.md Normal file
View File

@ -0,0 +1,44 @@
# The local builtin command
## Synopsis
local [option] name[=value] ...
## Description
`local` is identical to [declare](/commands/builtin/declare) in every
way, and takes all the same options, with two exceptions:
- Usage outside of a function is an error. Both `declare` and `local`
within a function have the same effect on variable scope, including
the -g option.
- `local` with no options prints variable names and values in the same
format as `declare` with no options, except the variables are filtered
to print only locals that were set in the same scope from which
`local` was called. Variables in parent scopes are not printed.
## Portability considerations
- `local` is not specified by POSIX. Most bourne-like shells don't have
a builtin called `local`, but some such as `dash` and the busybox
shell do.
<!-- -->
- The behavior of function scope is not defined by POSIX, however local
variables are implemented widely by bourne-like shells, and behavior
differs substantially. Even the`dash` shell has local variables.
<!-- -->
- In ksh93, using POSIX-style function definitions, `typeset` doesn't
set `local` variables, but rather acts upon variables of the
next-outermost scope (e.g. setting attributes). Using `typeset` within
functions defined using ksh `function name {` syntax, variables follow
roughly
[lexical-scoping](http://community.schemewiki.org/?lexical-scope),
except that functions themselves don't have scope, just like Bash.
This means that even functions defined within a "function's scope"
don't have access to non-local variables except through `namerefs`.
## See also

220
commands/builtin/mapfile.md Normal file
View File

@ -0,0 +1,220 @@
# The mapfile builtin command
## Synopsis
mapfile [-n COUNT] [-O ORIGIN] [-s COUNT] [-t] [-u FD] [-C CALLBACK] [-c QUANTUM] [ARRAY]
readarray [-n COUNT] [-O ORIGIN] [-s COUNT] [-t] [-u FD] [-C CALLBACK] [-c QUANTUM] [ARRAY]
## Description
This builtin is also accessible using the command name `readarray`.
`mapfile` is one of the two builtin commands primarily intended for
handling standard input (the other being `read`). `mapfile` reads lines
of standard input and assigns each to the elements of an indexed array.
If no array name is given, the default array name is `MAPFILE`. The
target array must be a "normal" integer indexed array.
`mapfile` returns success (0) unless an invalid option is given or the
given array `ARRAY` is set readonly.
| Option | Description |
|---------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `-c QUANTUM` | Specifies the number of lines that have to be read between every call to the callback specified with `-C`. The default QUANTUM is 5000 |
| `-C CALLBACK` | Specifies a callback. The string `CALLBACK` can be any shell code, the index of the array that will be assigned, and the line is appended at evaluation time. |
| `-n COUNT` | Reads at most `COUNT` lines, then terminates. If `COUNT` is 0, then all lines are read (default). |
| `-O ORIGIN` | Starts populating the given array `ARRAY` at the index `ORIGIN` rather than clearing it and starting at index 0. |
| `-s COUNT` | Discards the first `COUNT` lines read. |
| `-t` | Remove any trailing newline from a line read, before it is assigned to an array element. |
| `-u FD` | Read from filedescriptor `FD` rather than standard input. |
While `mapfile` isn't a common or portable shell feature, it's
functionality will be familiar to many programmers. Almost all
programming languages (aside from shells) with support for compound
datatypes like arrays, and which handle open file objects in the
traditional way, have some analogous shortcut for easily reading all
lines of some input as a standard feature. In Bash, `mapfile` in itself
can't do anything that couldn't already be done using read and a loop,
and if portability is even a slight concern, should never be used.
However, it does *significantly* outperform a read loop, and can make
for shorter and cleaner code - especially convenient for interactive
use.
## Examples
Here's a real-world example of interactive use borrowed from Gentoo
workflow. Xorg updates require rebuilding drivers, and the
Gentoo-suggested command is less than ideal, so let's Bashify it. The
first command produces a list of packages, one per line. We can read
those into the array named "args" using `mapfile`, stripping trailing
newlines with the '-t' option. The resulting array is then expanded into
the arguments of the "emerge" command - an interface to Gentoo's package
manager. This type of usage can make for a safe and effective
replacement for xargs(1) in certain situations. Unlike xargs, all
arguments are guaranteed to be passed to a single invocation of the
command with no wordsplitting, pathname expansion, or other monkey
business.
# eix --only-names -IC x11-drivers | { mapfile -t args; emerge -av1 &quot;${args[@]}&quot; <&1; }
Note the use of command grouping to keep the emerge command inside the
pipe's subshell and within the scope of "args". Also note the unusual
redirection. This is because the -a flag makes emerge interactive,
asking the user for confirmation before continuing, and checking with
isatty(3) to abort if stdin isn't pointed at a terminal. Since stdin of
the entire command group is still coming from the pipe even though
mapfile has read all available input, we just borrow FD 1 as it just so
happens to be pointing where we want it. More on this over at greycat's
wiki: <http://mywiki.wooledge.org/BashFAQ/024>
### The callback
This is one of the more unusual features of a Bash builtin. As far as
I'm able to tell, the exact behavior is as follows: If defined, as each
line is read, the code contained within the string argument to the -C
flag is evaluated and executed *before* the assignment of each array
element. There are no restrictions to this string, which can be any
arbitrary code, however, two additional "words" are automatically
appended to the end before evaluation: the index, and corresponding line
of data to be assigned to the next array element. Since all this happens
before assignment, the callback feature cannot be used to modify the
element to be assigned, though it can read and modify any array elements
already assigned.
A very simple example might be to use it as a kind of progress bar. This
will print a dot for each line read. Note the escaped comment to hide
the appended words from printf.
$ printf '%s\n' {1..5} | mapfile -c 1 -C 'printf . \#' )
.....
Really, the intended usage is for the callback to just contain the name
of a function, with the extra words passed to it as arguments. If you're
going to use callbacks at all, this is probably the best way because it
allows for easy access to the arguments with no ugly "code in a string".
$ foo() { echo &quot;|$1|&quot;; }; mapfile -n 11 -c 2 -C 'foo' <file
|2|
|4|
etc..
For the sake of completeness, here are some more complicated examples
inspired by a question asked in \#bash - how to prepend something to
every line of some input, and then output even and odd lines to separate
files. This is far from the best possible answer, but hopefully
illustrates the callback behavior:
$ { printf 'input%s\n' {1..10} | mapfile -c 1 -C '>&$(( (${#x[@]} % 2) + 3 )) printf -- &quot;%.sprefix %s&quot;' x; } 3>outfile0 4>outfile1
$ cat outfile{0,1}
prefix input1
prefix input3
prefix input5
prefix input7
prefix input9
prefix input2
prefix input4
prefix input6
prefix input8
prefix input10
Since redirects are syntactically allowed anywhere in a command, we put
it before the printf to stay out of the way of additional arguments.
Rather than opening "outfile\<n\>" for appending on each call by
calculating the filename, open an FD for each first and calculate which
FD to send output to by measuring the size of x mod 2. The zero-width
format specification is used to absorb the index number argument.
Another variation might be to add each of these lines to the elements of
separate arrays. I'll leave dissecting this one as an exercise for the
reader. This is quite the hack but illustrates some interesting
properties of printf -v and mapfile -C (which you should probably never
use in real code).
$ y=( 'odd[j]' 'even[j++]' ); printf 'input%s\n' {1..10} | { mapfile -tc 1 -C 'printf -v &quot;${y[${#x[@]} % 2]}&quot; -- &quot;%.sprefix %s&quot;' x; printf '%s\n' &quot;${odd[@]}&quot; '' &quot;${even[@]}&quot;; }
prefix input1
prefix input3
prefix input5
prefix input7
prefix input9
prefix input2
prefix input4
prefix input6
prefix input8
prefix input10
This example based on yet another \#bash question illustrates mapfile in
combination with read. The sample input is the heredoc to `main`. The
goal is to build a "struct" based upon records in the input file made up
of the numbers following the colon on each line. Every 3rd line is a key
followed by 2 corresponding fields. The showRecord function takes a key
and returns the record.
#!/usr/bin/env bash
showRecord() {
printf 'key[%d] = %d, %d\n' &quot;$1&quot; &quot;${vals[@]:keys[$1]*2:2}&quot;
}
parseRecords() {
trap 'unset -f _f' RETURN
_f() {
local x
IFS=: read -r _ x
((keys[x]=n++))
}
local n
_f
mapfile -tc2 -C _f &quot;$1&quot;
eval &quot;$1&quot;'=(&quot;${'&quot;$1&quot;'[@]##*:}&quot;)' # Return the array with some modification
}
main() {
local -a keys vals
parseRecords vals
showRecord &quot;$1&quot;
}
main &quot;$1&quot; <<-&quot;EOF&quot;
fabric.domain:123
routex:1
routey:2
fabric.domain:321
routex:6
routey:4
EOF
For example, running `scriptname 321` would output `key[321] = 6, 4`.
Every 2 lines read by `mapfile`, the function `_f` is called, which
reads one additional line. Since the first line in the file is a key,
and `_f` is responsible for the keys, it gets called first so that
`mapfile` starts by reading the second line of input, calling `_f` with
each subsequent 2 iterations. The RETURN trap is unimportant.
## Bugs
- Early implementations were buggy. For example, `mapfile` filling the
readline history buffer with calls to the `CALLBACK`. This was fixed
in 4.1 beta.
- `mapfile -n` reads an extra line beyond the last line assigned to the
array, through Bash. [Fixed in
4.2.35](ftp://ftp.gnu.org/gnu/bash/bash-4.2-patches/bash42-035).
- `mapfile` callbacks could cause a crash if the variable being assigned
is manipulated in certain ways.
<https://lists.gnu.org/archive/html/bug-bash/2013-01/msg00039.html>.
Fixed in 4.3.
## To Do
- Create an implementation as a shell function that's portable between
Ksh, Zsh, and Bash (and possibly other bourne-like shells with array
support).
## See also
- [arrays](/syntax/arrays)
- [read](/commands/builtin/read) - If you don't know about this yet, why
are you reading this page?
- <http://mywiki.wooledge.org/BashFAQ/001> - It's FAQ 1 for a reason.

465
commands/builtin/printf.md Normal file
View File

@ -0,0 +1,465 @@
# The printf command
\<div center round todo box 70%\> FIXME Stranger, this is a very big
topic that needs experience - please fill in missing information, extend
the descriptions, and correct the details if you can! \</div\> \<div
center round tip 70%\> <u>**Attention:**</u> This is about the
Bash-builtin command `printf` - however, the description should be
nearly identical for an external command that follows POSIX(r).
[GNU Awk](http://www.gnu.org/software/gawk/manual/gawk.html#Printf)
expects a comma after the format string and between each of the
arguments of a **printf** command. For examples, see: [code
snippet](printf?&#using_printf_inside_of_awk). \</div\>
Unlike other documentations, I don't want to redirect you to the manual
page for the `printf()` C function family. However, if you're more
experienced, that should be the most detailed description for the format
strings and modifiers.
Due to conflicting historical implementations of the `echo` command,
POSIX(r) recommends that `printf` is preferred over `echo`.
## General
The `printf` command provides a method to print preformatted text
similar to the `printf()` system interface (C function). It's meant as
successor for `echo` and has far more features and possibilities.
Beside other reasons, POSIX(r) has a very good argument to recommend it:
Both historical main flavours of the `echo` command are mutual
exclusive, they collide. A "new" command had to be invented to solve the
issue.
## Syntax
printf <FORMAT> <ARGUMENTS...>
The text format is given in `<FORMAT>`, while all arguments the
formatstring may point to are given after that, here, indicated by
`<ARGUMENTS...>`.
Thus, a typical `printf`-call looks like:
printf &quot;Surname: %s\nName: %s\n&quot; &quot;$SURNAME&quot; &quot;$FIRSTNAME&quot;
where `"Surname: %s\nName: %s\n"` is the format specification, and the
two variables are passed as arguments, the `%s` in the formatstring
points to (for every format specifier you give, `printf` awaits one
argument!).
### Options
| | |
|----------|-------------------------------------------------------------------------------------------------------------------------------|
| `-v VAR` | If given, the output is assigned to the variable `VAR` instead of printed to `stdout` (comparable to `sprintf()` in some way) |
The `-v` Option can't assign directly to array indexes in Bash versions
older than Bash 4.1.
\<note warning\> In versions newer than 4.1, one must be careful when
performing expansions into the first non-option argument of printf as
this opens up the possibility of an easy code injection vulnerability.
$ var='-vx[$(echo hi >&2)]'; printf &quot;$var&quot; hi; declare -p x
hi
declare -a x='([0]=&quot;hi&quot;)'
...where the echo can of course be replaced with any arbitrary command.
If you must, either specify a hard-coded format string or use -- to
signal the end of options. The exact same issue also applies to
[read](commands/builtin/read), and a similar one to
[mapfile](commands/builtin/mapfile), though performing expansions into
their arguments is less common. \</note\>
### Arguments
Of course in shell-meaning the arguments are just strings, however, the
common C-notations plus some additions for number-constants are
recognized to give a number-argument to `printf`:
| Number-Format | Description |
|---------------|-----------------------------------------------------------------------------------------------------------------------|
| `N` | A normal decimal number |
| `0N` | An octal number |
| `0xN` | A hexadecimal number |
| `0XN` | A hexadecimal number |
| `"X` | (a literal double-quote infront of a character): interpreted as number (underlying codeset) **don't forget escaping** |
| `'X` | (a literal single-quote infront of a character): interpreted as number (underlying codeset) **don't forget escaping** |
<u>**If more arguments than format specifiers**</u> are present, then
the format string is re-used until the last argument is interpreted. If
fewer format specifiers than arguments are present, then number-formats
are set to zero, while string-formats are set to null (empty).
Take care to avoid [word splitting](/syntax/expansion/wordsplit), as
accidentally passing the wrong number of arguments can produce wildly
different and unexpected results. See [this article](/syntax/words).
\<note warning\> <u>**Again, attention:**</u> When a numerical format
expects a number, the internal `printf`-command will use the common Bash
arithmetic rules regarding the base. A command like the following
example **will** throw an error, since `08` is not a valid octal number
(`00` to `07`!):
printf '%d\n' 08
\</note\>
### Format strings
The format string interpretion is derived from the C `printf()` function
family. Only format specifiers that end in one of the letters
`diouxXfeEgGaAcs` are recognized.
To print a literal `%` (percent-sign), use `%%` in the format string.
<u>**Again:**</u> Every format specifier expects an associated argument
provided!
These specifiers have different names, depending who you ask. But they
all mean the same: A placeholder for data with a specified format:
- format placeholder
- conversion specification
- formatting token
- ...
| Format | Description |
|--------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `%b` | Print the associated argument while interpreting backslash escapes in there |
| `%q` | Print the associated argument **shell-quoted**, reusable as input |
| `%d` | Print the associated argument as **signed decimal** number |
| `%i` | Same as `%d` |
| `%o` | Print the associated argument as **unsigned octal** number |
| `%u` | Print the associated argument as **unsigned decimal** number |
| `%x` | Print the associated argument as **unsigned hexadecimal** number with lower-case hex-digits (a-f) |
| `%X` | Same as `%x`, but with upper-case hex-digits (A-F) |
| `%f` | Interpret and print the associated argument as **floating point** number |
| `%e` | Interpret the associated argument as **double**, and print it in `<N>±e<N>` format |
| `%E` | Same as `%e`, but with an upper-case `E` in the printed format |
| `%g` | Interprets the associated argument as **double**, but prints it like `%f` or `%e` |
| `%G` | Same as `%g`, but print it like `%E` |
| `%c` | Interprets the associated argument as **char**: only the first character of a given argument is printed |
| `%s` | Interprets the associated argument literally as string |
| `%n` | Assigns the number of characters printed so far to the variable named in the corresponding argument. Can't specify an array index. If the given name is already an array, the value is assigned to the zeroth element. |
| `%a` | Interprets the associated argument as **double**, and prints it in the form of a C99 [hexadecimal floating-point literal](http://www.exploringbinary.com/hexadecimal-floating-point-constants/). |
| `%A` | Same as `%a`, but print it like `%E` |
| `%(FORMAT)T` | output the date-time string resulting from using `FORMAT` as a format string for `strftime(3)`. The associated argument is the number of seconds since Epoch, or `-1` (current time) or `-2` (shell startup time). If no corresponding argument is supplies, the current time is used as default |
| `%%` | No conversion is done. Produces a `%` (percent sign) |
Some of the mentioned format specifiers can modify their behaviour by
getting a format modifier:
### Modifiers
To be more flexible in the output of numbers and strings, the `printf`
command allows format modifiers. These are specified **between** the
introductory `%` and the character that specifies the format:
printf &quot;%50s\n&quot; &quot;This field is 50 characters wide...&quot;
#### Field and printing modifiers
| Field output format | |
|---------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `<N>` | **Any number**: Specifies a **minimum field width**, if the text to print is shorter, it's padded with spaces, if the text is longer, the field is expanded |
| `.` | **The dot**: Together with a field width, the field is **not** expanded when the text is longer, the text is truncated instead. "`%.s`" is an undocumented equivalent for "`%.0s`", which will force a field width of zero, effectively hiding the field from output |
| `*` | **The asterisk**: the width is given as argument before the string or number. Usage (the "`*`" corresponds to the "`20`"): `printf "%*s\n" 20 "test string"` |
| `#` | "Alternative format" for numbers: see table below |
| `-` | **Left-bound** text printing in the field (standard is **right-bound**) |
| `0` | Pads numbers with zeros, not spaces |
| `<space>` | Pad a positive number with a space, where a minus (`-`) is for negative numbers |
| `+` | Prints all numbers **signed** (`+` for positive, `-` for negative) |
| `'` | For decimal conversions, the thousands grouping separator is applied to the integer portion of the output according to the current LC_NUMERIC |
<u>**The "alternative format" modifier `#`:**</u>
| Alternative Format | |
|--------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `%#o` | The octal number is printed with a leading zero, unless it's zero itself |
| `%#x`, `%#X` | The hex number is printed with a leading "`0x`"/"`0X`", unless it's zero |
| `%#g`, `%#G` | The float number is printed with **trailing zeros** until the number of digits for the current precision is reached (usually trailing zeros are not printed) |
| all number formats except `%d`, `%o`, `%x`, `%X` | Always print a decimal point in the output, even if no digits follow it |
#### Precision
The precision for a floating- or double-number can be specified by using
`.<DIGITS>`, where `<DIGITS>` is the number of digits for precision. If
`<DIGITS>` is an asterisk (`*`), the precision is read from the argument
that precedes the number to print, like (prints 4,3000000000):
printf &quot;%.*f\n&quot; 10 4,3
The format `.*N` to specify the N'th argument for precision does not
work in Bash.
For strings, the precision specifies the maximum number of characters to
print (i.e., the maximum field width). For integers, it specifies the
number of digits to print (zero-padding!).
### Escape codes
These are interpreted if used anywhere in the format string, or in an
argument corresponding to a `%b` format.
| Code | Description |
|----------------|---------------------------------------------------------------------------------------------------------------------------|
| `\\` | Prints the character `\` (backslash) |
| `\a` | Prints the alert character (ASCII code 7 decimal) |
| `\b` | Prints a backspace |
| `\f` | Prints a form-feed |
| `\n` | Prints a newline |
| `\r` | Prints a carriage-return |
| `\t` | Prints a horizontal tabulator |
| `\v` | Prints a vertical tabulator |
| `\"` | Prints a `'` |
| `\?` | Prints a `?` |
| `\<NNN>` | Interprets `<NNN>` as **octal** number and prints the corresponding character from the character set |
| `\0<NNN>` | same as `\<NNN>` |
| `\x<NNN>` | Interprets `<NNN>` as **hexadecimal** number and prints the corresponding character from the character set (**3 digits**) |
| `\u<NNNN>` | same as `\x<NNN>`, but **4 digits** |
| `\U<NNNNNNNN>` | same as `\x<NNN>`, but **8 digits** |
The following additional escape and extra rules apply only to arguments
associated with a `%b` format:
| | |
|------|-------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `\c` | Terminate output similarly to the `\c` escape used by `echo -e`. printf produces no additional output after coming across a `\c` escape in a `%b` argument. |
- Backslashes in the escapes: `\'`, `\"`, and `\?` are not removed.
- Octal escapes beginning with `\0` may contain up to four digits.
(POSIX specifies up to three).
These are also respects in which `%b` differs from the escapes used by
[\$'...'](syntax/quoting#ansi_c_like_strings) style quoting.
## Examples
### Snipplets
- print the decimal representation of a hexadecimal number (preserve the
sign)
- `printf "%d\n" 0x41`
- `printf "%d\n" -0x41`
- `printf "%+d\n" 0x41`
- print the octal representation of a decimal number
- `printf "%o\n" 65`
- `printf "%05o\n" 65` (5 characters width, padded with zeros)
- this prints a 0, since no argument is specified
- `printf "%d\n"`
- print the code number of the character `A`
- `printf "%d\n" \'A`
- `printf "%d\n" "'A"`
- Generate a greeting banner and assign it to the variable `GREETER`
- `printf -v GREETER "Hello %s" "$LOGNAME"`
- Print a text at the end of the line, using `tput` to get the current
line width
- `printf "%*s\n" $(tput cols) "Hello world!"`
### Small code table
This small loop prints all numbers from 0 to 127 in
- decimal
- octal
- hex
<!-- -->
for ((x=0; x <= 127; x++)); do
printf '%3d | %04o | 0x%02x\n' &quot;$x&quot; &quot;$x&quot; &quot;$x&quot;
done
### Ensure well-formatted MAC address
This code here will take a common MAC address and rewrite it into a
well-known format (regarding leading zeros or upper/lowercase of the hex
digits, ...):
the_mac=&quot;0:13:ce:7:7a:ad&quot;
# lowercase hex digits
the_mac=&quot;$(printf &quot;%02x:%02x:%02x:%02x:%02x:%02x&quot; 0x${the_mac//:/ 0x})&quot;
# or the uppercase-digits variant
the_mac=&quot;$(printf &quot;%02X:%02X:%02X:%02X:%02X:%02X&quot; 0x${the_mac//:/ 0x})&quot;
### Replacement echo
This code was found in Solaris manpage for echo(1).
Solaris version of `/usr/bin/echo` is equivalent to:
printf &quot;%b\n&quot; &quot;$*&quot;
Solaris `/usr/ucb/echo` is equivalent to:
if [ &quot;X$1&quot; = &quot;X-n&quot; ]
then
shift
printf &quot;%s&quot; &quot;$*&quot;
else
printf &quot;%s\n&quot; &quot;$*&quot;
fi
### prargs Implementation
Working off the replacement echo, here is a terse implementation of
prargs:
printf '&quot;%b&quot;\n' &quot;$0&quot; &quot;$@&quot; | nl -v0 -s&quot;: &quot;
### repeating a character (for example to print a line)
A small trick: Combining printf and parameter expansion to draw a line
length=40
printf -v line '%*s' &quot;$length&quot;
echo ${line// /-}
or:
length=40
eval printf -v line '%.0s-' {1..$length}
### Replacement for some calls to date(1)
The `%(...)T` format string is a direct interface to `strftime(3)`.
$ printf 'This is week %(%U/%Y)T.\n' -1
This is week 52/2010.
Please read the manpage of `strftime(3)` to get more information about
the supported formats.
## differences from awk printf
Awk also derives its *printf()* function from C, and therefore has
similar format specifiers. However, in all versions of awk the space
character is used as a string concatenation operator, so it cannot be
used as an argument separator. **Arguments to awk printf must be
separated by commas.** Some versions of awk do not require printf
arguments to be surrounded by parentheses, but you should use them
anyway to provide portability.
In the following example, the two strings are concatenated by the
intervening space so that no argument remains to fill the format.
$ echo &quot;Foo&quot; | awk '{ printf &quot;%s\n&quot; $1 }'
awk: (FILENAME=- FNR=1) fatal: not enough arguments to satisfy format string
`%s
Foo'
^ ran out for this one
Simply replacing the space with a comma and adding parentheses yields
correct awk syntax.
$ echo &quot;Foo&quot; | awk '{ printf( &quot;%s\n&quot;, $1 ) }'
Foo
With appropriate metacharacter escaping the bash printf can be called
from inside awk (as from perl and other languages that support shell
callout) as long as you don't care about program efficiency or
readability.
echo &quot;Foo&quot; | awk '{ system( &quot;printf \&quot;%s\\n \&quot; \&quot;&quot; $1 &quot;\&quot;&quot; ) }'
Foo
## Differences from C, and portability considerations
- The a, A, e, E, f, F, g, and G conversions are supported by Bash, but
not required by POSIX.
<!-- -->
- There is no wide-character support (wprintf). For instance, if you use
`%c`, you're actually asking for the first byte of the argument.
Likewise, the maximum field width modifier (dot) in combination with
`%s` goes by bytes, not characters. This limits some of printf's
functionality to working with ascii only. ksh93's `printf` supports
the `L` modifier with `%s` and `%c` (but so far not `%S` or `%C`) in
order to treat precision as character width, not byte count. zsh
appears to adjust itself dynamically based upon `LANG` and `LC_CTYPE`.
If `LC_CTYPE=C`, zsh will throw "character not in range" errors, and
otherwise supports wide characters automatically if a variable-width
encoding is set for the current locale.
<!-- -->
- Bash recognizes and skips over any characters present in the length
modifiers specified by POSIX during format string parsing.
``` c|builtins/printf.def
#define LENMODS &quot;hjlLtz&quot;
...
/* skip possible format modifiers */
modstart = fmt;
while (*fmt && strchr (LENMODS, *fmt))
fmt++;
```
- mksh has no built-in printf by default (usually). There is an
unsupported compile-time option to include a very poor, basically
unusable implementation. For the most part you must rely upon the
system's `/usr/bin/printf` or equivalent. The mksh maintainer
recommends using `print`. The development version (post- R40f) adds a
new parameter expansion in the form of `${name@Q}` which fills the
role of `printf %q` -- expanding in a shell-escaped format.
<!-- -->
- ksh93 optimizes builtins run from within a command substitution and
which have no redirections to run in the shell's process. Therefore
the `printf -v` functionality can be closely matched by
`var=$(printf ...)` without a big performance hit.
<!-- -->
# Illustrates Bash-like behavior. Redefining printf is usually unnecessary / not recommended.
function printf {
case $1 in
-v)
shift
nameref x=$1
shift
x=$(command printf &quot;$@&quot;)
;;
*)
command printf &quot;$@&quot;
esac
}
builtin cut
print $$
printf -v 'foo[2]' '%d\n' &quot;$(cut -d ' ' -f 1 /proc/self/stat)&quot;
typeset -p foo
# 22461
# typeset -a foo=([2]=22461)
- The optional Bash loadable `print` may be useful for ksh compatibility
and to overcome some of [echo](commands/builtin/echo)'s portability
pitfalls. Bash, ksh93, and zsh's `print` have an `-f` option which
takes a `printf` format string and applies it to the remaining
arguments. Bash lists the synopsis as:
`print: print [-Rnprs] [-u unit] [-f format] [arguments]`. However,
only `-Rrnfu` are actually functional. Internally, `-p` is a noop (it
doesn't tie in with Bash coprocs at all), and `-s` only sets a flag
but has no effect. `-Cev` are unimplemented.
<!-- -->
- Assigning to variables: The `printf -v` way is slightly different to
the way using command-substitution. [Command
substitution](/syntax/expansion/cmdsubst) removes trailing newlines
before substituting the text, `printf -v` preserves all output.
## See also
- SUS: [printf
utility](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/printf.html)
and [printf()
function](http://pubs.opengroup.org/onlinepubs/9699919799/functions/printf.html)
- [Code snip: Print a horizontal line](/snipplets/print_horizontal_line)
uses some `printf` examples

269
commands/builtin/read.md Normal file
View File

@ -0,0 +1,269 @@
# The read builtin command
*read something about read here!*
## Synopsis
read [-ers] [-u <FD>] [-t <TIMEOUT>] [-p <PROMPT>] [-a <ARRAY>] [-n <NCHARS>] [-N <NCHARS>] [-d <DELIM>] [-i <TEXT>] [<NAME...>]
## Description
The `read` builtin reads **one line** of data (text, user input, ...)
from standard input or a supplied filedescriptor number into one or more
variables named by `<NAME...>`.
Since Bash 4.3-alpha, `read` skips any `NUL` (ASCII code 0) characters
in input.
If `<NAME...>` is given, the line is word-split using
[IFS](/syntax/shellvars#IFS) variable, and every word is assigned to one
`<NAME>`. The remaining words are all assigned to the last `<NAME>` if
more words than variable names are present.
\<WRAP center round info 90%\> If no `<NAME>` is given, the whole line
read (without performing word-splitting!) is assigned to the shell
variable [REPLY](/syntax/shellvars#REPLY). Then, `REPLY` really contains
the line as it was read, without stripping pre- and postfix spaces and
other things!
while read -r; do
printf '&quot;%s&quot;\n' &quot;$REPLY&quot;
done <<<&quot; a line with prefix and postfix space &quot;
\</WRAP\>
If a timeout is given, or if the shell variable
[TMOUT](/syntax/shellvars#TMOUT) is set, it is counted from initially
waiting for input until the completion of input (i.e. until the complete
line is read). That means the timeout can occur during input, too.
### Options
| Option | Description |
|----------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `-a <ARRAY>` | read the data word-wise into the specified array `<ARRAY>` instead of normal variables |
| `-d <DELIM>` | recognize `<DELIM>` as data-end, rather than `<newline>` |
| `-e` | on interactive shells: use Bash's readline interface to read the data. Since version 5.1-alpha, this can also be used on specified file descriptors using `-u` |
| `-i <STRING>` | preloads the input buffer with text from `<STRING>`, only works when Readline (`-e`) is used |
| `-n <NCHARS>` | reads `<NCHARS>` characters of input, then quits |
| `-N <NCHARS>` | reads `<NCHARS>` characters of input, *ignoring any delimiter*, then quits |
| `-p <PROMPT>` | the prompt string `<PROMPT>` is output (without a trailing automatic newline) before the read is performed |
| `-r` | raw input - **disables** interpretion of **backslash escapes** and **line-continuation** in the read data |
| `-s` | secure input - don't echo input if on a terminal (passwords!) |
| `-t <TIMEOUT>` | wait for data `<TIMEOUT>` seconds, then quit (exit code 1). Fractional seconds ("5.33") are allowed since Bash 4. A value of 0 immediately returns and indicates if data is waiting in the exit code. Timeout is indicated by an exit code greater than 128. If timeout arrives before data is read completely (before end-of-line), the partial data is saved. |
| `-u <FD>` | use the filedescriptor number `<FD>` rather than `stdin` (0) |
When both, `-a <ARRAY>` and a variable name `<NAME>` is given, then the
array is set, but not the variable.
Of course it's valid to set individual array elements without using
`-a`:
read MYARRAY[5]
\<WRAP center round important 90%\>
Reading into array elements using the syntax above **may cause [pathname
expansion](/syntax/expansion/globs) to occur**.
Example: You are in a directory with a file named `x1`, and you want to
read into an array `x`, index `1` with
read x[1]
then pathname expansion will expand to the filename `x1` and break your
processing!
Even worse, if `nullglob` is set, your array/index will disappear.
To avoid this, either **disable pathname expansion** or **quote** the
array name and index:
read 'x[1]'
\</WRAP\>
### Return status
| Status | Reason |
|:-------|:--------------------------------------------------|
| 0 | no error |
| 0 | error when assigning to a read-only variable [^1] |
| 2 | invalid option |
| \>128 | timeout (see `-t`) |
| !=0 | invalid filedescriptor supplied to `-u` |
| !=0 | end-of-file reached |
### read without -r
Essentially all you need to know about `-r` is to **ALWAYS** use it. The
exact behavior you get without `-r` is completely useless even for weird
purposes. It basically allows the escaping of input which matches
something in IFS, and also escapes line continuations. It's explained
pretty well in the [POSIX
read](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/read.html#tag_20_109)
spec.
2012-05-23 13:48:31 geirha it should only remove the backslashes, not change \n and \t and such into newlines and tabs
2012-05-23 13:49:00 ormaaj so that's what read without -r does?
2012-05-23 13:49:16 geirha no, -r doesn't remove the backslashes
2012-05-23 13:49:34 ormaaj I thought read <<<'str' was equivalent to read -r <<<$'str'
2012-05-23 13:49:38 geirha # read x y <<< 'foo\ bar baz'; echo &quot;<$x><$y>&quot;
2012-05-23 13:49:40 shbot geirha: <foo bar><baz>
2012-05-23 13:50:32 geirha no, read without -r is mostly pointless. Damn bourne
2012-05-23 13:51:08 ormaaj So it's mostly (entirely) used to escape spaces
2012-05-23 13:51:24 ormaaj and insert newlines
2012-05-23 13:51:47 geirha ormaaj: you mostly get the same effect as using \ at the prompt
2012-05-23 13:52:04 geirha echo \&quot; outputs a &quot; , read x <<< '\&quot;' reads a &quot;
2012-05-23 13:52:32 ormaaj oh weird
2012-05-23 13:52:46 * ormaaj struggles to think of a point to that...
2012-05-23 13:53:01 geirha ormaaj: ask Bourne :P
2012-05-23 13:53:20 geirha (not Jason)
2012-05-23 13:53:56 ormaaj hm thanks anyway :)
## Examples
### Rudimentary cat replacement
A rudimentary replacement for the `cat` command: read lines of input
from a file and print them on the terminal.
opossum() {
while read -r; do
printf &quot;%s\n&quot; &quot;$REPLY&quot;
done <&quot;$1&quot;
}
<u>**Note:**</u> Here, `read -r` and the default `REPLY` is used,
because we want to have the real literal line, without any mangeling.
`printf` is used, because (depending on settings), `echo` may interpret
some baskslash-escapes or switches (like `-n`).
### Press any key...
Remember the MSDOS `pause` command? Here's something similar:
pause() {
local dummy
read -s -r -p &quot;Press any key to continue...&quot; -n 1 dummy
}
Notes:
- `-s` to suppress terminal echo (printing)
- `-r` to not interpret special characters (like waiting for a second
character if somebody presses the backslash)
### Reading Columns
#### Simple Split
Read can be used to split a string:
var=&quot;one two three&quot;
read -r col1 col2 col3 <<< &quot;$var&quot;
printf &quot;col1: %s col2: %s col3 %s\n&quot; &quot;$col1&quot; &quot;$col2&quot; &quot;$col3&quot;
Take care that you cannot use a pipe:
echo &quot;$var&quot; | read col1 col2 col3 # does not work!
printf &quot;col1: %s col2: %s col3 %s\n&quot; &quot;$col1&quot; &quot;$col2&quot; &quot;$col3&quot;
Why? because the commands of the pipe run in subshells that cannot
modify the parent shell. As a result, the variables `col1`, `col2` and
`col3` of the parent shell are not modified (see article:
[processtree](/scripting/processtree)).
If the variable has more fields than there are variables, the last
variable get the remaining of the line:
read col1 col2 col3 <<< &quot;one two three four&quot;
printf &quot;%s\n&quot; &quot;$col3&quot; #prints three four
#### Changing The Separator
By default reads separates the line in fields using spaces or tabs. You
can modify this using the *special variable*
[IFS](/syntax/shellvars#IFS), the Internal Field Separator.
IFS=&quot;:&quot; read -r col1 col2 <<< &quot;hello:world&quot;
printf &quot;col1: %s col2: %s\n&quot; &quot;$col1&quot; &quot;$col2&quot;
Here we use the `var=value command` syntax to set the environment of
`read` temporarily. We could have set `IFS` normally, but then we would
have to take care to save its value and restore it afterward
(`OLD=$IFS IFS=":"; read ....;IFS=$OLD`).
The default `IFS` is special in that 2 fields can be separated by one or
more space or tab. When you set `IFS` to something besides whitespace
(space or tab), the fields are separated by **exactly** one character:
IFS=&quot;:&quot; read -r col1 col2 col3 <<< &quot;hello::world&quot;
printf &quot;col1: %s col2: %s col3 %s\n&quot; &quot;$col1&quot; &quot;$col2&quot; &quot;$col3&quot;
See how the `::` in the middle infact defines an additional *empty
field*.
The fields are separated by exactly one character, but the character can
be different between each field:
IFS=&quot;:|@&quot; read -r col1 col2 col3 col4 <<< &quot;hello:world|in@bash&quot;
printf &quot;col1: %s col2: %s col3 %s col4 %s\n&quot; &quot;$col1&quot; &quot;$col2&quot; &quot;$col3&quot; &quot;$col4&quot;
### Are you sure?
asksure() {
echo -n &quot;Are you sure (Y/N)? &quot;
while read -r -n 1 -s answer; do
if [[ $answer = [YyNn] ]]; then
[[ $answer = [Yy] ]] && retval=0
[[ $answer = [Nn] ]] && retval=1
break
fi
done
echo # just a final linefeed, optics...
return $retval
}
### using it
if asksure; then
echo &quot;Okay, performing rm -rf / then, master....&quot;
else
echo &quot;Pfff...&quot;
fi
### Ask for a path with a default value
<u>**Note:**</u> The `-i` option was introduced with Bash 4
read -e -p &quot;Enter the path to the file: &quot; -i &quot;/usr/local/etc/&quot; FILEPATH
The user will be prompted, he can just accept the default, or edit it.
### Multichar-IFS: Parsing a simple date/time string
Here, `IFS` contains both, a colon and a space. The fields of the
date/time string are recognized correctly.
datetime=&quot;2008:07:04 00:34:45&quot;
IFS=&quot;: &quot; read -r year month day hour minute second <<< &quot;$datetime&quot;
## Portability considerations
- POSIX(r) only specified the `-r` option (raw read); `-r` is not only
POSIX, you can find it in earlier Bourne source code
- POSIX(r) doesn't support arrays
- `REPLY` is not POSIX(r), you need to set `IFS` to the empty string to
get the whole line for shells that don't know `REPLY`.
`while IFS= read -r line; do
...
done < text.txt
`
## See also
- Internal: [The printf builtin command](/commands/builtin/printf)
[^1]: fixed in 4.2-rc1

View File

@ -0,0 +1,44 @@
# The readonly builtin command
## Synopsis
readonly [-p] [-a] [-A] [-f] [NAME[=VALUE] ...]
## Description
The `readonly` builtin command is used to mark variables or functions as
read-only, which means unchangeable. This implies that it can't be unset
anymore. A `readonly` variable may not be redefined in child scopes. A
readonly global may not be redefined as a function local variable.
Simple command environment assignments may not reference readonly
variables.
### Options
| Option | Description |
|:-------|:-----------------------------------------------------------------------------------------------------------------------|
| `-a` | refer to normal arrays |
| `-A` | refer to associative arrays |
| `-f` | refer to functions |
| `-p` | print all read-only variables or functions, `-a`, `-A` and `-f` can be used to filter. The output is reusable as input |
An argument of `--` disables further option processing.
### Return status
| Status | Reason |
|:-------|:-------------------------------|
| 0 | no error |
| !=0 | invalid option |
| !=0 | invalid combination of options |
| !=0 | a given `NAME` is invalid |
## Examples
## Portability considerations
- in POSIX(r), only the `-p` option is specified
## See also
- [declare](/commands/builtin/declare)

View File

@ -0,0 +1,35 @@
# The return builtin command
## Synopsis
return [N]
## Description
The `return` command returns from a shell function.
If `N` is given, the return code to the caller is set to `N`. If not,
the returned status the the status of the most recently executed command
(i.e. `$?`).
### Options
There are no options.
### Exit status
If everything is okay, the `return` command doesn't come back. If it
comes back, there was a problem in doing the return.
| Status | Reason |
|:-------|:------------------------------------------------------------------------|
| 1 | `return` was called while not being in a shell function or sourced file |
## Examples
## Portability considerations
## See also
- [The exit builtin command](/commands/builtin/exit)
- [The exit status](/dict/terms/exit_status)

81
commands/builtin/set.md Normal file
View File

@ -0,0 +1,81 @@
# The set builtin command
FIXME incomplete - text, examples, maybe extended description
## Synopsis
set [--abefhkmnptuvxBCHP] <-o OPTIONNAME> [-][--] <POSPARAMS>
## Description
`set` is primarily made to
- set the positional parameters (see [handling positional
parameters](/scripting/posparams)) to `<POSPARAMS>`
- set shell attributes with short options (see below)
- set shell attributes with long option names (see below)
Without any options, `set` displays all shell- and environment-variables
(only is POSIX-mode) in a re-usable format `NAME=VALUE`.
### Attributes
All attributes below can be switched on using `-X` and switched off
using `+X`. This is done because of the historical meaning of the `-` to
set flags (true for most commands on UNIX(r)).
| Flag | Optionname | Description |
|------|----------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `-a` | `allexport` | Automatically mark new and altered variables to be exported to subsequent environments. |
| `-b` | `notify` | Don't wait for the next prompt to print when showing the reports for a terminated background job (only with job control) |
| `-e` | `errexit` | When set, the shell exits when a simple command in a command list exits non-zero (`FALSE`). This is not done in situations, where the exit code is already checked (`if`, `while`, `until`, `||`, `&&`) |
| `-f` | `noglob` | Disable [pathname expansion](/syntax/expansion/globs) (globbing) |
| `-h` | `hashall` | Remembers the location of commands when they're called (hashing). Enabled by default. |
| `-k` | `keyword` | Allows to place environment-assignments everywhere in the commandline, not only infront of the called command. |
| `-m` | `monitor` | **Monitor mode**. With job control, a short descriptive line is printed when a backgroud job ends. Default is "on" for interactive shells (with job control). |
| `-n` | `noexec` | Read and parse but **do not execute commands** - useful for checking scripts for syntax errors. Ignored by interactive shells. |
| `-o` | | Set/unset attributes with long option names, e.g. `set -o noglob`. The long option names are in the second column of this table. If no option name is given, all options are printed with their current status. |
| `-p` | `privileged` | Turn on privileged mode. |
| `-t` | `onecmd` | Exit after reading and executing **one** command. |
| `-u` | `nounset` | Treat unset variables as an error when performing parameter expansion. Non-interactive shells exit on this error. |
| `-v` | `verbose` | Print shell input lines as they are read - useful for debugging. |
| `-x` | `xtrace` | Print commands just before execution - with all expansions and substitutions done, and words marked - useful for debugging. |
| `-B` | `braceexpand` | The shell performs [brace expansion](/syntax/expansion/brace) This is on by default. |
| `-C` | \<BOOKMARK:tag_noclobber\>`noclobber` | Don't overwrite files on redirection operations. You can override that by specifying the `>|` redirection operator when needed. See [redirection](/syntax/redirection) |
| `-E` | `errtrace` | `ERR`-traps are inherited by by shell functions, command substitutions, and commands executed in a subshell environment. |
| `-H` | `histexpand` | Enable `!`-style history expansion. Defaults to `on` for interactive shells. |
| `-P` | `physical` | Don't follow symlinks when changing directories - use the physical filesystem structure. |
| `-T` | `functrace` | `DEBUG`- and `RETURN`-traps are inherited by subsequent environments, like `-E` for `ERR` trap. |
| `-` | | "End of options" - all following arguments are assigned to the positional parameters, even when they begin with a dash. `-x` and `-v` options are turned off. Positional parameters are unchanged (unlike using `--`!) when no further arguments are given. |
| `--` | | If no arguments follow, the positional parameters are unset. With arguments, the positional parameters are set, even if the strings begin with a `-` (dash) like an option. |
| | Long options usable with `-o` without a short equivalent | |
| | `emacs` | Use an emacs-style command line editing interface. This is enabled by default when the shell is interactive, unless the shell is started with `--noediting` option. |
| | `history` | If set, command historization is done (enabled by default on interactive shells) |
| | `ignoreeof` | The effect is as if the shell command `IGNOREEOF=10` had been executed. See [shell variables](/syntax/shellvars). |
| | `nolog` | **(currently ignored)** |
| | `pipefail` | If set, the exit code from a pipeline is different from the normal ("last command in pipeline") behaviour: `TRUE` when no command failed, `FALSE` when something failed (code of the rightmost command that failed) |
| | `posix` | When set, Bash runs in POSIX mode. |
| | `vi` | Enables a `vi`-style command line editing interface. |
## Examples
Tag a part of a shell script to output debugging information (`-x`):
``` bash
#!/bin/bash
...
set -x # on
...
set +x # off
...
```
## Portability considerations
`set` and its basic behaviour and options are specified by POSIX(r).
However, options that influence Bash-specific things are not portable,
naturally.
## See also
- Internal: [The shopt builtin command](/commands/builtin/shopt)

87
commands/builtin/shift.md Normal file
View File

@ -0,0 +1,87 @@
# The shift builtin command
## Synopsis
shift [n]
## Description
The `shift` builtin command is used to "shift" the positional parameters
by the given number `n` or by 1, if no number is given.
This means, the number and the position of the positional parameters are
changed. The very first positional parameter is discarded, the second
becomes the first one, etc.
Imagine the following set of positional parameters (`$1` to `$4`):
| 1 | This |
|:----|:-----|
| 2 | is |
| 3 | a |
| 4 | test |
When you use `shift 1`, they will be changed to:
| 1 | is |
|:----|:-----|
| 2 | a |
| 3 | test |
The [special parameter](/syntax/shellvars#special_parameters) `$#` will
reflect the final number of positional parameters.
If the number given is 0, no changes are made to the positional
parameters.
### Options
There are no options.
### Return status
| Status | Reason |
|:-------|:----------------------------------------------------------------------------------------------------|
| 0 | no error |
| 1 | non-numeric argument |
| 1 | given number (or the default 1) is bigger than the number of actually present positional parameters |
| 1 | given number is negative |
## Examples
## Portability considerations
- The `shift` builtin command is specified by POSIX(r).
- Many shells will throw a fatal error when attempting to `shift` more
than the number of positional parameters. **POSIX does not require
that behavior**. Bash (even in POSIX mode) and Zsh return 1 when there
are no args, and no error output is produced unless the
[shift_verbose](internals/shell_options#shift_verbose)
[shopt](commands/builtin/shopt) option is enabled. Ksh93, pdksh, posh,
mksh, and dash, all throw useless fatal shell
errors.`$ dash -c 'f() { if shift; then echo &quot;$1&quot;; else echo &quot;no args&quot;; fi; }; f'
dash: 1: shift: can't shift that many
` In most shells, you can work around this problem using the
[command](/commands/builtin/command) builtin to suppress fatal errors
caused by *special builtins*. \<code\> \$ dash -c 'f() { if command
shift 2\>/dev/null; then echo "\$1"; else echo "no args"; fi; }; f'
no args \</code\> While, POSIX requires this behavior, it isn't very
obvious and some shells don't do it correctly. To work around this, you
can use something like:
\<code\> \$ mksh -c 'f() { if ! \${1+false} && shift; then echo "\$1";
else echo "no args"; fi; }; f' no args \</code\> ~~The mksh maintainer
refuses to change either the `shift` or `command` builtins.~~
[Fixed](https://github.com/MirBSD/mksh/commit/996e05548ab82f7ef2dea61f109cc7b6d13837fa).
(Thanks!)
- Perhaps almost as bad as the above, busybox sh's `shift` always
returns success, even when attempting to shift beyond the final
argument. \<code\> \$ bb -c 'f() { if shift; then echo "\$1"; else
echo "no args"; fi; }; f'
(no output) \</code\> The above mksh workaround will work in this case
too.
## See also

51
commands/builtin/shopt.md Normal file
View File

@ -0,0 +1,51 @@
# The shopt builtin command
The `shopt` builtin manages [shell options](/internals/shell_options), a
set of boolean (`on`/`off`) configuration variables that control the
behaviour of the shell.
## Synopsis
shopt [-pqsu] [-o] <OPTNAME...>
## Description
Note: Some of these options and other shell options can also be set with
[the set builtin](/commands/builtin/set).
### Options
| Option | Description |
|--------|--------------------------------------------------------------------------------------------------------------------------|
| `-o` | Restrict the values of `<OPTNAME...>` to only those also known by [the set builtin](/commands/builtin/set) |
| `-p` | Print all shell options and their current value. **Default**. |
| `-q` | Quiet mode. Set exit code if named option is set. For multiple options: `TRUE` if all options are set, `FALSE` otherwise |
| `-s` | Enable (<u>s</u>et) the shell options named by `<OPTNAME...>` or list all *enabled* options if no names are given |
| `-u` | Disabe (<u>u</u>nset) the shell options named by `<OPTNAME...>` or list all *disabled* options if no names are given |
As noted above, if only `-s` or `-u` are given without any option names,
only the currently enabled (`-s`) or disabled (`-u`) options are
printed.
### Exit code
When listing options, the exit code is `TRUE` (0), if all options are
enabled, `FALSE` otherwise.
When setting/unsetting an option, the exit code is `TRUE` unless the
named option doesn't exitst.
## Examples
Enable the `nullglob` option:
shopt -s nullglob
## Portability considerations
The `shopt` command is not portable accross different shells.
## See also
- Internal: [The set builtin command](/commands/builtin/set)
- Internal: [List of shell options](/internals/shell_options)

139
commands/builtin/source.md Normal file
View File

@ -0,0 +1,139 @@
\<label class="block control-label"
for="focus<u>this"\>\<span\>Username\</span\> \<input type="text"
id="focus</u>this" name="u" class="edit
form-control"/\>\</label\>\<br/\> \<label class="block
control-label"\>\<span\>Password\</span\> \<input type="password"
name="p" class="edit form-control"/\>\</label\>\<br/\> \<label
class="simple control-label" for="remember<u>me"\>\<input
type="checkbox" id="remember</u>me" name="r" value="1"
class="checkbox-inline"/\> \<span\>Remember me\</span\>\</label\> \<div
id="plugin<u>captcha_wrapper"\>\<input type="hidden"
name="80d13662bc018ff4d7809a8d517a0cfb"
value="42FOZdS2ttGgeyJAq6xhPpjk5crSblSEQXmyW2Yg3W8="/\>\<label
for="plugin</u>captcha" class="control-label"\>Please fill all the
letters into the box to prove you're human.\</label\> \<img
src="/web/20221209023650im\_/<https://wiki.bash-hackers.org/lib/plugins/captcha/img.php?secret=42FOZdS2ttGgeyJAq6xhPpjk5crSblSEQXmyW2Yg3W8%3D&id=commands:builtin:source>"
width="160" height="40" alt=""/\> \<input type="text" size="5"
name="40d641ec62438c7a0daa80e665d812c6" class="edit form-control"/\>
\<label class="no control-label"\>Please keep this field empty: \<input
type="text" name="ce7eb8fa38586f8a0eaf3e46319431ea"
class="form-control"/\>\</label\>\</div\>\<button type="submit"
class="btn btn-success btn btn-default"\>\<span class="iconify"
data-icon="mdi:lock"\>\</span\> Log In\</button\> \</fieldset\> \<p\>You
don't have an account yet? Just get one: \<a
href="/web/20221209023650/<https://wiki.bash-hackers.org/commands/builtin/source?do=register>"
title="Register" rel="nofollow"
class="register"\>Register\</a\>\</p\>\<p\>Forgotten your password? Get
a new one: \<a
href="/web/20221209023650/<https://wiki.bash-hackers.org/commands/builtin/source?do=resendpwd>"
title="Set new password" rel="nofollow" class="resendpwd"\>Set new
password\</a\>\</p\>\</div\>\</form\> \</div\> \</div\>\<!-- /content
--\>\</div\>
</div>
</div>
<div class="small text-right">
<span class="docInfo">
</span>
</div>
</article>
</div>
</main>
<footer id="dw__footer" class="dw-container py-5 dokuwiki container-fluid">
<hr/>
\<div align="center"\> \<h3\>\<a target="\_blank"
href="<http://web.archive.org/web/20221209023650/http://www.performing-databases.com/>"\>This
site is supported by Performing Databases - your experts for database
administration\</a\>\</h3\> \</div\> \<hr/\> \<div align="center"\>
\<script async
src="//web.archive.org/web/20221209023650js\_/<https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js>"\>\</script\>
\<!-- BHORG_BOTTOM --\> \<ins class="adsbygoogle"
style="display:inline-block;width:728px;height:90px"
data-ad-client="ca-pub-4658830517838678"
data-ad-slot="1603598940"\>\</ins\> \<script\> (adsbygoogle =
window.adsbygoogle \|\| \[\]).push({}); \</script\> \</div\>
\<!-- footer --\> \<div class="dw-container small container-fluid
mx-5"\>
<div class="footer-dw-title">
<div class="media">
<div class="media-left">
<img src="/web/20221209023650im_/https://wiki.bash-hackers.org/lib/tpl/bootstrap3/images/logo.png" alt="Bash Hackers Wiki" class="media-object" style="height:32px"/>
</div>
<div class="media-body">
<div class="row">
<div class="col-sm-2">
<h4 class="media-heading">Bash Hackers Wiki</h4>
<p>
</p>
</div>
<div class="col-sm-10">
</div>
</div>
</div>
</div>
</div>
<div class="footer-license row">
<hr/>
<div id="dw__license" class="col-sm-6">
<p>
<a href="http://web.archive.org/web/20221209023650/http://www.gnu.org/licenses/fdl-1.3.html" title="GNU Free Documentation License 1.3" target="" itemscope itemtype="http://schema.org/CreativeWork" itemprop="license" rel="license" class="license"><img src="/web/20221209023650im_/https://wiki.bash-hackers.org/lib/tpl/bootstrap3/images/license/gnufdl.png" width="24" height="24" alt="gnufdl"/> </a> </p>
<p class="small">
Except where otherwise noted, content on this wiki is licensed under the following license:<br/><a href="http://web.archive.org/web/20221209023650/http://www.gnu.org/licenses/fdl-1.3.html" title="GNU Free Documentation License 1.3" target="" itemscope itemtype="http://schema.org/CreativeWork" itemprop="license" rel="license" class="license">GNU Free Documentation License 1.3</a> </p>
</div>
<div class="col-sm-6">
</div>
</div>
\</div\> \<!-- /footer --\>
</footer>
<a href="#dokuwiki__top" class="back-to-top hidden-print btn btn-default" title="skip to content" accesskey="t">
<span class="iconify" data-icon="mdi:chevron-up"></span> </a>
<div id="screen__mode"> <span class="visible-xs-block"></span>
<span class="visible-sm-block"></span>
<span class="visible-md-block"></span>
<span class="visible-lg-block"></span>
</div>
<img src="/web/20221209023650im_/https://wiki.bash-hackers.org/lib/exe/indexer.php?id=commands%3Abuiltin%3Asource&1670553440" width="2" height="1" alt=""/>
\</div\>
\</body\> \</html\> \<!--
FILE ARCHIVED ON 02:36:50 Dec 09, 2022 AND RETRIEVED FROM THE
INTERNET ARCHIVE ON 11:11:47 Apr 15, 2023.
JAVASCRIPT APPENDED BY WAYBACK MACHINE, COPYRIGHT INTERNET ARCHIVE.
ALL OTHER CONTENT MAY ALSO BE PROTECTED BY COPYRIGHT (17 U.S.C.
SECTION 108(a)(3)).
--\> \<!-- playback timings (ms):
captures_list: 182.459
exclusion.robots: 0.116
exclusion.robots.policy: 0.107
cdx.remote: 0.065
esindex: 0.008
LoadShardBlock: 148.166 (3)
PetaboxLoader3.datanode: 164.467 (4)
load_resource: 168.622
PetaboxLoader3.resolve: 113.645
--\>

139
commands/builtin/times.md Normal file
View File

@ -0,0 +1,139 @@
\<label class="block control-label"
for="focus<u>this"\>\<span\>Username\</span\> \<input type="text"
id="focus</u>this" name="u" class="edit
form-control"/\>\</label\>\<br/\> \<label class="block
control-label"\>\<span\>Password\</span\> \<input type="password"
name="p" class="edit form-control"/\>\</label\>\<br/\> \<label
class="simple control-label" for="remember<u>me"\>\<input
type="checkbox" id="remember</u>me" name="r" value="1"
class="checkbox-inline"/\> \<span\>Remember me\</span\>\</label\> \<div
id="plugin<u>captcha_wrapper"\>\<input type="hidden"
name="cf98d82c03000fa22bcf7e63f85de09a"
value="E7+c/5cYp8oDlzqwHkwFoVVx8H0vjRftuE9fGEqBYWM="/\>\<label
for="plugin</u>captcha" class="control-label"\>Please fill all the
letters into the box to prove you're human.\</label\> \<img
src="/web/20221209024037im\_/<https://wiki.bash-hackers.org/lib/plugins/captcha/img.php?secret=E7%2Bc%2F5cYp8oDlzqwHkwFoVVx8H0vjRftuE9fGEqBYWM%3D&id=commands:builtin:times>"
width="160" height="40" alt=""/\> \<input type="text" size="5"
name="0f995be3ac0650966b134ec90c04f5fa" class="edit form-control"/\>
\<label class="no control-label"\>Please keep this field empty: \<input
type="text" name="e19cdb943969d31b57d5fc3a3b54f1ba"
class="form-control"/\>\</label\>\</div\>\<button type="submit"
class="btn btn-success btn btn-default"\>\<span class="iconify"
data-icon="mdi:lock"\>\</span\> Log In\</button\> \</fieldset\> \<p\>You
don't have an account yet? Just get one: \<a
href="/web/20221209024037/<https://wiki.bash-hackers.org/commands/builtin/times?do=register>"
title="Register" rel="nofollow"
class="register"\>Register\</a\>\</p\>\<p\>Forgotten your password? Get
a new one: \<a
href="/web/20221209024037/<https://wiki.bash-hackers.org/commands/builtin/times?do=resendpwd>"
title="Set new password" rel="nofollow" class="resendpwd"\>Set new
password\</a\>\</p\>\</div\>\</form\> \</div\> \</div\>\<!-- /content
--\>\</div\>
</div>
</div>
<div class="small text-right">
<span class="docInfo">
</span>
</div>
</article>
</div>
</main>
<footer id="dw__footer" class="dw-container py-5 dokuwiki container-fluid">
<hr/>
\<div align="center"\> \<h3\>\<a target="\_blank"
href="<http://web.archive.org/web/20221209024037/http://www.performing-databases.com/>"\>This
site is supported by Performing Databases - your experts for database
administration\</a\>\</h3\> \</div\> \<hr/\> \<div align="center"\>
\<script async
src="//web.archive.org/web/20221209024037js\_/<https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js>"\>\</script\>
\<!-- BHORG_BOTTOM --\> \<ins class="adsbygoogle"
style="display:inline-block;width:728px;height:90px"
data-ad-client="ca-pub-4658830517838678"
data-ad-slot="1603598940"\>\</ins\> \<script\> (adsbygoogle =
window.adsbygoogle \|\| \[\]).push({}); \</script\> \</div\>
\<!-- footer --\> \<div class="dw-container small container-fluid
mx-5"\>
<div class="footer-dw-title">
<div class="media">
<div class="media-left">
<img src="/web/20221209024037im_/https://wiki.bash-hackers.org/lib/tpl/bootstrap3/images/logo.png" alt="Bash Hackers Wiki" class="media-object" style="height:32px"/>
</div>
<div class="media-body">
<div class="row">
<div class="col-sm-2">
<h4 class="media-heading">Bash Hackers Wiki</h4>
<p>
</p>
</div>
<div class="col-sm-10">
</div>
</div>
</div>
</div>
</div>
<div class="footer-license row">
<hr/>
<div id="dw__license" class="col-sm-6">
<p>
<a href="http://web.archive.org/web/20221209024037/http://www.gnu.org/licenses/fdl-1.3.html" title="GNU Free Documentation License 1.3" target="" itemscope itemtype="http://schema.org/CreativeWork" itemprop="license" rel="license" class="license"><img src="/web/20221209024037im_/https://wiki.bash-hackers.org/lib/tpl/bootstrap3/images/license/gnufdl.png" width="24" height="24" alt="gnufdl"/> </a> </p>
<p class="small">
Except where otherwise noted, content on this wiki is licensed under the following license:<br/><a href="http://web.archive.org/web/20221209024037/http://www.gnu.org/licenses/fdl-1.3.html" title="GNU Free Documentation License 1.3" target="" itemscope itemtype="http://schema.org/CreativeWork" itemprop="license" rel="license" class="license">GNU Free Documentation License 1.3</a> </p>
</div>
<div class="col-sm-6">
</div>
</div>
\</div\> \<!-- /footer --\>
</footer>
<a href="#dokuwiki__top" class="back-to-top hidden-print btn btn-default" title="skip to content" accesskey="t">
<span class="iconify" data-icon="mdi:chevron-up"></span> </a>
<div id="screen__mode"> <span class="visible-xs-block"></span>
<span class="visible-sm-block"></span>
<span class="visible-md-block"></span>
<span class="visible-lg-block"></span>
</div>
<img src="/web/20221209024037im_/https://wiki.bash-hackers.org/lib/exe/indexer.php?id=commands%3Abuiltin%3Atimes&1670553667" width="2" height="1" alt=""/>
\</div\>
\</body\> \</html\> \<!--
FILE ARCHIVED ON 02:40:37 Dec 09, 2022 AND RETRIEVED FROM THE
INTERNET ARCHIVE ON 11:12:10 Apr 15, 2023.
JAVASCRIPT APPENDED BY WAYBACK MACHINE, COPYRIGHT INTERNET ARCHIVE.
ALL OTHER CONTENT MAY ALSO BE PROTECTED BY COPYRIGHT (17 U.S.C.
SECTION 108(a)(3)).
--\> \<!-- playback timings (ms):
captures_list: 99.766
exclusion.robots: 0.172
exclusion.robots.policy: 0.16
RedisCDXSource: 18.001
esindex: 0.007
LoadShardBlock: 55.571 (3)
PetaboxLoader3.datanode: 52.34 (4)
load_resource: 826.247
PetaboxLoader3.resolve: 805.576
--\>

72
commands/builtin/trap.md Normal file
View File

@ -0,0 +1,72 @@
# The trap builtin command
## Synopsis
trap [-lp] [[ARGUMENT] SIGNAL]
## Description
The `trap` command is used to "trap" signals and other events. In this
context, "trapping" means to install handler code.
The shell code `ARGUMENT` is to be read and executed whenever the shell
receives a signal or another event `SIGNAL`. The given `SIGNAL`
specification can be
- the name of a signal with the SIG prefix, e.g. `SIGTERM`
- the name of a signal without the SIG prefix, e.g. `TERM`
- the number of a signal (see `trap -l`), e.g. `15`
- the name or number of a special event (see table below), e.g. `EXIT`
Without any options or operands, `trap` prints a list of installed traps
in a reusable format (equivalent to the `-p` option).
Special `ARGUMENT`s
- if `ARGUMENT` is absent or `-` (dash), the signal/event handler is
reset to its original value
- if `ARGUMENT` is the null string, the signal/event is ignored
Special events
| Name | Code | Description |
|:---------|:-----|:------------------------------------------------------------------------------------------------------------------------------------------|
| `EXIT` | 0 | executed on shell exit |
| `DEBUG` | | executed before every simple command |
| `RETURN` | | executed when a shell function or a sourced code finishes executing |
| `ERR` | | executed each time a command's failure would cause the shell to exit when the [`-e` option (`errexit`)](/commands/builtin/set) is enabled |
### Options
| Option | Description |
|:-------|:-----------------------------------------------------------------------------------------|
| `-l` | print a list of signal names and their corresponding numbers |
| `-p` | display the trap commands associated with each signal specification in a reusable format |
### Return status
| Status | Reason |
|:-------|:-----------------------------|
| 0 | no error/success |
| !=0 | invalid option |
| !=0 | invalid signal specification |
## Examples
### List installed traps
trap
### Ignore terminal interrupt (Ctrl-C, SIGINT)
trap '' INT
## Portability considerations
- `trap` is specified by POSIX(R) without the `-l` and `-p` options
- in POSIX(R), beside signals, only `EXIT` (0) is valid as an event
## See also
- [the set command](/commands/builtin/set) for the `-e` (`errexit`)
option

139
commands/builtin/true.md Normal file
View File

@ -0,0 +1,139 @@
\<label class="block control-label"
for="focus<u>this"\>\<span\>Username\</span\> \<input type="text"
id="focus</u>this" name="u" class="edit
form-control"/\>\</label\>\<br/\> \<label class="block
control-label"\>\<span\>Password\</span\> \<input type="password"
name="p" class="edit form-control"/\>\</label\>\<br/\> \<label
class="simple control-label" for="remember<u>me"\>\<input
type="checkbox" id="remember</u>me" name="r" value="1"
class="checkbox-inline"/\> \<span\>Remember me\</span\>\</label\> \<div
id="plugin<u>captcha_wrapper"\>\<input type="hidden"
name="b838780b201e25c64701dd6d96c9c690"
value="vWqvEC1F2pR6Zk6YCMXV6ZcWI9LCKDF1yaEDdcP/PjA="/\>\<label
for="plugin</u>captcha" class="control-label"\>Please fill all the
letters into the box to prove you're human.\</label\> \<img
src="/web/20220811050800im\_/<https://wiki.bash-hackers.org/lib/plugins/captcha/img.php?secret=vWqvEC1F2pR6Zk6YCMXV6ZcWI9LCKDF1yaEDdcP%2FPjA%3D&id=commands:builtin:true>"
width="160" height="40" alt=""/\> \<input type="text" size="5"
name="9d247c032db95f8003c233e914e5027d" class="edit form-control"/\>
\<label class="no control-label"\>Please keep this field empty: \<input
type="text" name="b958f2e243b53be04250ddfcf60c18a6"
class="form-control"/\>\</label\>\</div\>\<button type="submit"
class="btn btn-success btn btn-default"\>\<span class="iconify"
data-icon="mdi:lock"\>\</span\> Log In\</button\> \</fieldset\> \<p\>You
don't have an account yet? Just get one: \<a
href="/web/20220811050800/<https://wiki.bash-hackers.org/commands/builtin/true?do=register>"
title="Register" rel="nofollow"
class="register"\>Register\</a\>\</p\>\<p\>Forgotten your password? Get
a new one: \<a
href="/web/20220811050800/<https://wiki.bash-hackers.org/commands/builtin/true?do=resendpwd>"
title="Set new password" rel="nofollow" class="resendpwd"\>Set new
password\</a\>\</p\>\</div\>\</form\> \</div\> \</div\>\<!-- /content
--\>\</div\>
</div>
</div>
<div class="small text-right">
<span class="docInfo">
</span>
</div>
</article>
</div>
</main>
<footer id="dw__footer" class="dw-container py-5 dokuwiki container-fluid">
<hr/>
\<div align="center"\> \<h3\>\<a target="\_blank"
href="<http://web.archive.org/web/20220811050800/http://www.performing-databases.com/>"\>This
site is supported by Performing Databases - your experts for database
administration\</a\>\</h3\> \</div\> \<hr/\> \<div align="center"\>
\<script async
src="//web.archive.org/web/20220811050800js\_/<https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js>"\>\</script\>
\<!-- BHORG_BOTTOM --\> \<ins class="adsbygoogle"
style="display:inline-block;width:728px;height:90px"
data-ad-client="ca-pub-4658830517838678"
data-ad-slot="1603598940"\>\</ins\> \<script\> (adsbygoogle =
window.adsbygoogle \|\| \[\]).push({}); \</script\> \</div\>
\<!-- footer --\> \<div class="dw-container small container-fluid
mx-5"\>
<div class="footer-dw-title">
<div class="media">
<div class="media-left">
<img src="/web/20220811050800im_/https://wiki.bash-hackers.org/lib/tpl/bootstrap3/images/logo.png" alt="Bash Hackers Wiki" class="media-object" style="height:32px"/>
</div>
<div class="media-body">
<div class="row">
<div class="col-sm-2">
<h4 class="media-heading">Bash Hackers Wiki</h4>
<p>
</p>
</div>
<div class="col-sm-10">
</div>
</div>
</div>
</div>
</div>
<div class="footer-license row">
<hr/>
<div id="dw__license" class="col-sm-6">
<p>
<a href="http://web.archive.org/web/20220811050800/http://www.gnu.org/licenses/fdl-1.3.html" title="GNU Free Documentation License 1.3" target="" itemscope itemtype="http://schema.org/CreativeWork" itemprop="license" rel="license" class="license"><img src="/web/20220811050800im_/https://wiki.bash-hackers.org/lib/tpl/bootstrap3/images/license/gnufdl.png" width="24" height="24" alt="gnufdl"/> </a> </p>
<p class="small">
Except where otherwise noted, content on this wiki is licensed under the following license:<br/><a href="http://web.archive.org/web/20220811050800/http://www.gnu.org/licenses/fdl-1.3.html" title="GNU Free Documentation License 1.3" target="" itemscope itemtype="http://schema.org/CreativeWork" itemprop="license" rel="license" class="license">GNU Free Documentation License 1.3</a> </p>
</div>
<div class="col-sm-6">
</div>
</div>
\</div\> \<!-- /footer --\>
</footer>
<a href="#dokuwiki__top" class="back-to-top hidden-print btn btn-default" title="skip to content" accesskey="t">
<span class="iconify" data-icon="mdi:chevron-up"></span> </a>
<div id="screen__mode"> <span class="visible-xs-block"></span>
<span class="visible-sm-block"></span>
<span class="visible-md-block"></span>
<span class="visible-lg-block"></span>
</div>
<img src="/web/20220811050800im_/https://wiki.bash-hackers.org/lib/exe/indexer.php?id=commands%3Abuiltin%3Atrue&1660194486" width="2" height="1" alt=""/>
\</div\>
\</body\> \</html\> \<!--
FILE ARCHIVED ON 05:08:00 Aug 11, 2022 AND RETRIEVED FROM THE
INTERNET ARCHIVE ON 11:11:46 Apr 15, 2023.
JAVASCRIPT APPENDED BY WAYBACK MACHINE, COPYRIGHT INTERNET ARCHIVE.
ALL OTHER CONTENT MAY ALSO BE PROTECTED BY COPYRIGHT (17 U.S.C.
SECTION 108(a)(3)).
--\> \<!-- playback timings (ms):
captures_list: 233.348
exclusion.robots: 0.182
exclusion.robots.policy: 0.168
RedisCDXSource: 1.293
esindex: 0.009
LoadShardBlock: 213.868 (3)
PetaboxLoader3.datanode: 158.831 (4)
load_resource: 121.724
PetaboxLoader3.resolve: 28.974
--\>

179
commands/builtin/unset.md Normal file
View File

@ -0,0 +1,179 @@
# The unset builtin command
## Synopsis
unset [-f|v] [-n] [NAME ...]
## Description
The `unset` builtin command is used to unset values and attributes of
shell variables and functions. Without any option, `unset` tries to
unset a variable first, then a function.
### Options
| Option | Description |
|:-------|:-------------------------------------------------------------------------------------------------------------|
| `-f` | treats each `NAME` as a function name |
| `-v` | treats each `NAME` as a variable name |
| `-n` | treats each `NAME` as a name reference and unsets the variable itself rather than the variable it references |
### Exit status
| Status | Reason |
|:-------|:---------------------------------------------------|
| 0 | no error |
| !=0 | invalid option |
| !=0 | invalid combination of options (`-v` **and** `-f`) |
| !=0 | a given `NAME` is read-only |
## Examples
unset -v EDITOR
unset -f myfunc1 myfunc2
### Scope
In bash, unset has some interesting properties due to its unique dynamic
scope. If a local variable is both declared and unset (by calling unset
on the local) from within the same function scope, then the variable
appears unset to that scope and all child scopes until either returning
from the function, or another local variable of the same name is
declared underneath where the original variable was unset. In other
words, the variable looks unset to everything until returning from the
function in which the variable was set (and unset), at which point
variables of the same name from higher scopes are uncovered and
accessible once again.
If however unset is called from a child scope relative to where a local
variable has been set, then the variable of the same name in the
next-outermost scope becomes visible to its scope and all children - as
if the variable that was unset was never set to begin with. This
property allows looking upwards through the stack as variable names are
unset, so long as unset and the local it unsets aren't together in the
same scope level.
Here's a demonstration of this behavior.
#!/usr/bin/env bash
FUNCNEST=10
# Direct recursion depth.
# Search up the stack for the first non-FUNCNAME[1] and count how deep we are.
callDepth() {
# Strip &quot;main&quot; off the end of FUNCNAME[@] if current function is named &quot;main&quot; and
# Bash added an extra &quot;main&quot; for non-interactive scripts.
if [[ main == !(!(&quot;${FUNCNAME[1]}&quot;)|!(&quot;${FUNCNAME[-1]}&quot;)) && $- != *i* ]]; then
local -a 'fnames=(&quot;${FUNCNAME[@]:1:${#FUNCNAME[@]}-2}&quot;)'
else
local -a 'fnames=(&quot;${FUNCNAME[@]:1}&quot;)'
fi
if (( ! ${#fnames[@]} )); then
printf 0
return
fi
local n
while [[ $fnames == ${fnames[++n]} ]]; do
:
done
printf -- $n
}
# This function is the magic stack walker.
unset2() {
unset -v -- &quot;$@&quot;
}
f() {
local a
if (( (a=$(callDepth)) <= 4 )); then
(( a == 1 )) && unset a
(( a == 2 )) && declare -g a='global scope yo'
f
else
trap 'declare -p a' DEBUG
unset2 a # declare -- a=&quot;5&quot;
unset a a # declare -- a=&quot;4&quot;
unset a # declare -- a=&quot;2&quot;
unset a # ./unset-tests: line 44: declare: a: not found
: # declare -- a=&quot;global scope yo&quot;
fi
}
a='global scope'
f
# vim: set fenc=utf-8 ff=unix ts=4 sts=4 sw=4 ft=sh nowrap et:
output:
declare -- a=&quot;5&quot;
declare -- a=&quot;4&quot;
declare -- a=&quot;2&quot;
./unset-tests: line 44: declare: a: not found
declare -- a=&quot;global scope yo&quot;
Some things to observe:
- `unset2` is only really needed once. We remain 5 levels deep in `f`'s
for the remaining `unset` calls, which peel away the outer layers of
`a`'s.
- Notice that the "a" is unset using an ordinary unset command at
recursion depth 1, and subsequently calling unset reveals a again in
the global scope, which has since been modified in a lower scope using
declare -g.
- Declaring a global with declare -g bypasses all locals and sets or
modifies the variable of the global scope (outside of all functions).
It has no affect on the visibility of the global.
- This doesn't apply to individual array elements. If two local arrays
of the same name appear in different scopes, the entire array of the
inner scope needs to be unset before any elements of the outer array
become visible. This makes "unset" and "unset2" identical for
individual array elements, and for arrays as a whole, unset and unset2
behave as they do for scalar variables.
### Args
Like several other Bash builtins that take parameter names, unset
expands its arguments.
~ $ ( a=({a..d}); unset 'a[2]'; declare -p a )
declare -a a='([0]=&quot;a&quot; [1]=&quot;b&quot; [3]=&quot;d&quot;)'
As usual in such cases, it's important to quote the args to avoid
accidental results such as globbing.
~ $ ( a=({a..d}) b=a c=d d=1; set -x; unset &quot;${b}[&quot;{2..3}-c\]; declare -p a )
+ unset 'a[2-1]' 'a[3-1]'
+ declare -p a
declare -a a='([0]=&quot;a&quot; [3]=&quot;d&quot;)'
Of course hard to follow indirection is still possible whenever
arithmetic is involved, also as shown above, even without extra
expansions.
In Bash, the `unset` builtin only evaluates array subscripts if the
array itself is set.
~ $ ( unset -v 'a[$(echo a was set >&2)0]' )
~ $ ( a=(); unset -v 'a[$(echo a was set >&2)0]' )
a was set
## Portability considerations
Quoting POSIX:
If neither -f nor -v is specified, name refers to a variable; if a variable by that name does not exist, it is unspecified whether a function by that name, if any, shall be unset.
Therefore, it is recommended to explicitly specify `-f` or `-v` when
using `unset`. Also, I prefer it as a matter of style.
## See also
- [declare](/commands/builtin/declare)
- [unset](/commands/builtin/unset)

44
commands/builtin/wait.md Normal file
View File

@ -0,0 +1,44 @@
# The wait builtin command
## Synopsis
wait [-f] [-n] [-p VARNAME] [ID...]
## Description
The `wait` builtin command is used to wait for job completion and return
exit status.
- if `ID` is a job specification, it waits for all processes in the
pipeline of this job
- waits for a specific job (asynchronous command) and report its exit
status if one or more `ID` is given
- waits for all running jobs (asynchronous commands)
- waits for "the next" job (`-n` option)
- waits for termination instead of status change (`-f` option)
`ID` may be an operating system process identifier or a shell job
specification.
### Options
| Option | Description |
|:-------------|:--------------------------------------------------------------------------------------------------------------------------------|
| `-n` | Waits for "the next" child to exit (as opposed to "all children" without this option). Accepts a list of IDs (jobs) |
| `-f` | Waits for the termination of the given `ID` (instead of waiting for a status change only) |
| `-p VARNAME` | When waiting for a list (-n) or all jobs, writes the job ID to the job that was actually terminated into the variable `VARNAME` |
### Return status
The return status is the return status of the job waited for, or
| Status | Reason |
|:-------|:------------------------------------------------|
| 0 | waited for all jobs in shell's job list |
| 1 | the given `ID` is not a valid job or process ID |
## Examples
## Portability considerations
## See also

571
commands/classictest.md Normal file
View File

@ -0,0 +1,571 @@
# The classic test command
`test <EXPRESSION>`
`[ <EXPRESSION> ]`
## General syntax
This command allows you to do various tests and sets its exit code to 0
(*TRUE*) or 1 (*FALSE*) whenever such a test succeeds or not. Using this
exit code, it's possible to let Bash react on the result of such a test,
here by using the command in an if-statement:
#!/bin/bash
# test if /etc/passwd exists
if test -e /etc/passwd; then
echo &quot;Alright man...&quot; >&2
else
echo &quot;Yuck! Where is it??&quot; >&2
exit 1
fi
The syntax of the test command is relatively easy. Usually it's the
command name "`test`" followed by a test type (here "`-e`" for "file
exists") followed by test-type-specific values (here the filename to
check, "`/etc/passwd`").
There's a second standardized command that does exactly the same: the
command "`[`" - the difference just is that it's called "`[`" and the
last argument to the command must be a "`]`": It forms
"`[ <EXPRESSION> ]`"
Let's rewrite the above example to use it:
#!/bin/bash
# test if /etc/passwd exists
if [ -e /etc/passwd ]; then
echo &quot;Alright man...&quot; >&2
else
echo &quot;Yuck! Where is it??&quot; >&2
exit 1
fi
One might **think** now that these "\[" and "\]" belong to the syntax of
Bash's if-clause: **No they don't! It's a simple, ordinary command,
still!**
Another thing you have to remember is that if the test command wants one
parameter for a test, you have to give it one parameter. Let's check for
some of your music files:
#!/bin/bash
mymusic=&quot;/data/music/Van Halen/Van Halen - Right Now.mp3&quot;
if [ -e &quot;$mymusic&quot; ]; then
echo &quot;Let's rock&quot; >&2
else
echo &quot;No music today, sorry...&quot; >&2
exit 1
fi
As you definitely noted, the filename contains spaces. Since we call a
normal ordinary command ("test" or "\[") the shell will word-split the
expansion of the variable `mymusic`: You need to quote it when you don't
want the `test`-command to complain about too many arguments for this
test-type! If you didn't understand it, please read the [article about
words...](/syntax/words)
Please also note that the file-tests want **one filename** to test.
Don't give a glob (filename-wildcards) as it can expand to many
filenames =\> **too many arguments!**
<u>**Another common mistake**</u> is to provide too **few** arguments:
[ &quot;$mystring&quot;!=&quot;test&quot; ]
This provides exactly **one** test-argument to the command. With one
parameter, it defaults to the `-n` test: It tests if a provided string
is empty (`FALSE`) or not (`TRUE`) - due to the lack of **spaces to
separate the arguments** the shown command always ends `TRUE`!
Well, I addressed several basic rules, now let's see what the
test-command can do for you. The Bash test-types can be split into
several sections: **file tests**, **string tests**, **arithmetic
tests**, **misc tests**. Below, the tests marked with :!: are
non-standard tests (i.e. not in SUS/POSIX/etc..).
## File tests
This section probably holds the most tests, I'll list them in some
logical order. Since Bash 4.1, all tests related to permissions respect
ACLs, if the underlying filesystem/OS supports them.
| Operator syntax | Description | |
|:----------------------------|:-------------------------------------------------------------------------------------------|:---:|
| **-a** \<FILE\> | True if \<FILE\> exists. :!: (not recommended, may collide with `-a` for `AND`, see below) | |
| **-e** \<FILE\> | True if \<FILE\> exists. | |
| **-f** \<FILE\> | True, if \<FILE\> exists and is a **regular** file. | |
| **-d** \<FILE\> | True, if \<FILE\> exists and is a **directory**. | |
| **-c** \<FILE\> | True, if \<FILE\> exists and is a **character special** file. | |
| **-b** \<FILE\> | True, if \<FILE\> exists and is a **block special** file. | |
| **-p** \<FILE\> | True, if \<FILE\> exists and is a **named pipe** (FIFO). | |
| **-S** \<FILE\> | True, if \<FILE\> exists and is a **socket** file. | |
| **-L** \<FILE\> | True, if \<FILE\> exists and is a **symbolic link**. | |
| **-h** \<FILE\> | True, if \<FILE\> exists and is a **symbolic link**. | |
| **-g** \<FILE\> | True, if \<FILE\> exists and has **sgid bit** set. | |
| **-u** \<FILE\> | True, if \<FILE\> exists and has **suid bit** set. | |
| **-r** \<FILE\> | True, if \<FILE\> exists and is **readable**. | |
| **-w** \<FILE\> | True, if \<FILE\> exists and is **writable**. | |
| **-x** \<FILE\> | True, if \<FILE\> exists and is **executable**. | |
| **-s** \<FILE\> | True, if \<FILE\> exists and has size bigger than 0 (**not empty**). | |
| **-t** \<fd\> | True, if file descriptor \<fd\> is open and refers to a terminal. | |
| \<FILE1\> **-nt** \<FILE2\> | True, if \<FILE1\> is **newer than** \<FILE2\> (mtime). :!: | |
| \<FILE1\> **-ot** \<FILE2\> | True, if \<FILE1\> is **older than** \<FILE2\> (mtime). :!: | |
| \<FILE1\> **-ef** \<FILE2\> | True, if \<FILE1\> and \<FILE2\> refer to the **same device and inode numbers**. :!: | |
## String tests
| Operator syntax | Description |
|--------------------------------|------------------------------------------------------------------------------------------------------------------------------------|
| **-z** \<STRING\> | True, if \<STRING\> is **empty**. |
| **-n** \<STRING\> | True, if \<STRING\> is **not empty** (this is the default operation). |
| \<STRING1\> **=** \<STRING2\> | True, if the strings are **equal**. |
| \<STRING1\> **!=** \<STRING2\> | True, if the strings are **not equal**. |
| \<STRING1\> **\<** \<STRING2\> | True if \<STRING1\> sorts **before** \<STRING2\> lexicographically (pure ASCII, not current locale!). Remember to escape! Use `\<` |
| \<STRING1\> **\>** \<STRING2\> | True if \<STRING1\> sorts **after** \<STRING2\> lexicographically (pure ASCII, not current locale!). Remember to escape! Use `\>` |
## Arithmetic tests
| Operator syntax | Description |
|-----------------------------------|---------------------------------------------------------------------|
| \<INTEGER1\> **-eq** \<INTEGER2\> | True, if the integers are **equal**. |
| \<INTEGER1\> **-ne** \<INTEGER2\> | True, if the integers are **NOT equal**. |
| \<INTEGER1\> **-le** \<INTEGER2\> | True, if the first integer is **less than or equal** second one. |
| \<INTEGER1\> **-ge** \<INTEGER2\> | True, if the first integer is **greater than or equal** second one. |
| \<INTEGER1\> **-lt** \<INTEGER2\> | True, if the first integer is **less than** second one. |
| \<INTEGER1\> **-gt** \<INTEGER2\> | True, if the first integer is **greater than** second one. |
## Misc syntax
| Operator syntax | Description |
|:---------------------------|:-------------------------------------------------------------------------------------------------------------------------|
| \<TEST1\> **-a** \<TEST2\> | True, if \<TEST1\> **and** \<TEST2\> are true (AND). Note that `-a` also may be used as a file test (see above) |
| \<TEST1\> **-o** \<TEST2\> | True, if either \<TEST1\> **or** \<TEST2\> is true (OR). |
| **!** \<TEST\> | True, if \<TEST\> is **false** (NOT). |
| **(** \<TEST\> **)** | Group a test (for precedence). **Attention:** In normal shell-usage, the "(" and ")" must be escaped; use "\\" and "\\"! |
| **-o** \<OPTION_NAME\> | True, if the [shell option](/internals/shell_options) \<OPTION_NAME\> is set. |
| **-v** \<VARIABLENAME\> | True if the variable \<VARIABLENAME\> has been set. Use `var[n]` for array elements. |
| **-R** \<VARIABLENAME\> | True if the variable \<VARIABLENAME\> has been set and is a nameref variable (since 4.3-alpha) |
## Number of Arguments Rules
The `test` builtin, especially hidden under its `[` name, may seem
simple but is in fact **causing a lot of trouble sometimes**. One of the
difficulty is that the behaviour of `test` not only depends on its
arguments but also on the **number of its arguments**.
Here are the rules taken from the manual (<u>**Note:**</u> This is for
the command `test`, for `[` the number of arguments is calculated
without the final `]`, for example `[ ]` follows the "zero arguments"
rule):
- **0 arguments**
- The expression is false.
- **1 argument**
- The expression is true if, and only if, the argument is not null
- **2 arguments**
- If the first argument is `!` (exclamation mark), the expression is
true if, and only if, the second argument is null
- If the first argument is one of the unary conditional operators
listed above under the syntax rules, the expression is true if the
unary test is true
- If the first argument is not a valid unary conditional operator, the
expression is false
- **3 arguments**
- If the second argument is one of the binary conditional operators
listed above under the syntax rules, the result of the expression is
the result of the binary test using the first and third arguments as
operands
- If the first argument is `!`, the value is the negation of the
two-argument test using the second and third arguments
- If the first argument is exactly `(` and the third argument is
exactly `)`, the result is the one-argument test of the second
argument. Otherwise, the expression is false. The `-a` and `-o`
operators are considered binary operators in this case
(**Attention:** This means the operator `-a` is not a file operator
in this case!)
- **4 arguments**
- If the first argument is `!`, the result is the negation of the
three-argument expression composed of the remaining arguments.
Otherwise, the expression is parsed and evaluated according to
precedence using the rules listed above
- **5 or more arguments**
- The expression is parsed and evaluated according to precedence using
the rules listed above
These rules may seem complex, but it's not so bad in practice. Knowing
them might help you to explain some of the "unexplicable" behaviours you
might encounter:
var=&quot;&quot;
if [ -n $var ]; then echo &quot;var is not empty&quot;; fi
This code prints "var is not empty", even though `-n something` is
supposed to be true if `$var` is not empty - **why?**
Here, as `$var` is **not quoted**, word splitting occurs and `$var`
results in actually nothing (Bash removes it from the command's argument
list!). So the test is in fact `[ -n ]` **and falls into the "one
argument" rule**, the only argument is "-n" which is not null and so the
test returns true. The solution, as usual, is to **quote the parameter
expansion**: `[ -n "$var" ]` so that the test has always 2 arguments,
even if the second one is the null string.
These rules also explain why, for instance, -a and -o can have several
meanings.
## AND and OR
### The Prefered Way
The way often recommended to logically connect several tests with AND
and OR is to use **several single test commands** and to **combine**
them with the shell `&&` and `||` **list control operators**.
See this:
if [ -n &quot;$var&quot;] && [ -e &quot;$var&quot;]; then
echo &quot;\$var is not null and a file named $var exists!&quot;
fi
The return status of AND and OR lists is the exit status of the last
command executed in the list
- With `command1 && command2`, `command2` is executed if, and only if,
`command1` returns an exit status of zero (true)
- With `command1 ││ command2`, `command2` is executed if, and only if,
`command1` returns a non-zero exit status (false)
### The other way: -a and -o
The logical operators AND and OR for the test-command itself are `-a`
and `-o`, thus:
if [ -n &quot;$var&quot; -a -e &quot;$var&quot; ] ; then
echo &quot;\$var is not null and a file named $var exists&quot;
fi
They are **not** `&&` or `||`:
$ if [ -n &quot;/tmp&quot; && -d &quot;/tmp&quot;]; then echo true; fi # DOES NOT WORK
bash: [: missing `]'
You might find the error message confusing, `[` does not find the
required final `]`, because as seen above `&&` is used to write a **list
of commands**. The `if` statement actually **sees two commands**:
- `[ -n "/tmp"`
- `-d "/tmp" ]`
...which **must** fail.
### Why you should avoid using -a and -o
#### If portability is a concern
POSIX(r)/SUSv3 does **not** specify the behaviour of `test` in cases
where there are more than 4 arguments. If you write a script that might
not be executed by Bash, the behaviour might be different! [^1]
#### If you want the cut behaviour
Let's say, we want to check the following two things (AND):
1. if a string is null (empty)
2. if a command produced an output
Let's see:
if [ -z &quot;false&quot; -a -z &quot;$(echo I am executed >&2)&quot; ] ; then ...
=\> The arguments are all expanded **before** `test` runs, thus the
echo-command **is executed**.
if [ -z &quot;false&quot; ] && [ -z &quot;$(echo I am not executed >&2)&quot; ]; then...
=\> Due to the nature of the `&&` list operator, the second test-command
runs only if the first test-command returns true, our echo-command **is
not executed**.
<u>**Note:**</u> In my opinion, `-a` and `-o` are also less readable
`[pgas]`
### Precedence and Parenthesis
Take care if you convert your scripts from using `-a` and `-o` to use
the list way (`&&` and `||`):
- in the test-command rules, `-a` has **precedence over** `-o`
- in the shell grammar rules, `&&` and `||` have **equal precedence**
That means, **you can get different results**, depending on the manner
of use:
$ if [ &quot;true&quot; ] || [ -e /does/not/exist ] && [ -e /does/not/exist ]; then echo true; else echo false; fi
false
$ if [ &quot;true&quot; -o -e /does/not/exist -a -e /does/not/exist ]; then echo true; else echo false;fi
true
As a result you have to think about it a little or add precedence
control (parenthesis).
For `&&` and `||` parenthesis means (shell-ly) grouping the commands,
and since `( ... )` introduces a subshell we will use `{ ... }` instead:
$ if [ &quot;true&quot; ] || { [ -e /does/not/exist ] && [ -e /does/not/exist ] ;} ; then echo true; else echo false; fi
true
For the test command, the precedence parenthesis are, as well, `( )`,
but you need to escape or quote them, so that the shell doesn't try to
interpret them:
$ if [ \( &quot;true&quot; -o -e /does/not/exist \) -a -e /does/not/exist ]; then echo true; else echo false; fi
false
# equivalent, but less readable IMHO:
$ if [ '(' &quot;true&quot; -o -e /does/not/exist ')' -a -e /does/not/exist ]; then echo true; else echo false; fi
false
## NOT
As for AND and OR, there are 2 ways to negate a test with the shell
keyword `!` or passing `!` as an argument to `test`.
Here `!` negates the exit status of the command `test` which is 0
(true), and the else part is executed:
if ! [ -d '/tmp' ]; then echo &quot;/tmp doesn't exists&quot;; else echo &quot;/tmp exists&quot;; fi
Here the `test` command itself exits with status 1 (false) and the else
is also executed:
if [ ! -d '/tmp' ]; then echo &quot;/tmp doesn't exists&quot;; else echo &quot;/tmp exists&quot;; fi
Unlike for AND and OR, both methods for NOT have an identical behaviour,
at least for doing one single test.
## Pitfalls summarized
In this section you will get all the mentioned (and maybe more) possible
pitfalls and problems in a summary.
### General
Here's the copy of a mail on bug-bash list. A user asking a question
about using the test command in Bash, **he's talking about a problem,
which you may have already had yourself**:
From: (PROTECTED)
Subject: -d option not working. . .?
Date: Tue, 11 Sep 2007 21:51:59 -0400
To: bug-bash@gnu.org
Hi All,
I've got a script that I'm trying to set up, but it keeps telling me
that &quot;[-d command not found&quot;. Can someone please explain what is
wrong with this?:
#!/bin/sh
for i in $*
do
{
if [-d $i]
then
echo &quot;$i is a directory! Yay!&quot;
else
echo &quot;$i is not a directory!&quot;
fi
}
done
Regards
See the problem regarding the used test-command (the other potential
problems are not of interest here)?
[-d $i]
He simply didn't know that `test` or `[` is a normal, simple command.
Well, here's the answer he got. I quote it here, because it's a well
written text that addresses most of the common issues with the "classic"
test command:
From: Bob Proulx (EMAIL PROTECTED)
Subject: Re: -d option not working. . .?
Date: Wed, 12 Sep 2007 10:32:35 -0600
To: bug-bash@gnu.org
> (QUOTED TEXT WAS REMOVED)
The shell is first and foremost a way to launch other commands. The
syntax is simply &quot;if&quot; followed by a command-list, (e.g. if /some/foo;
or even if cmd1; cmd2; cmd3; then). Plus the '( ... )' syntax is
already taken by the use of starting a subshell.
As I recall in the original shell language the file test operator was
not built-in. It was provided by the standalone '/bin/test' command.
The result was effectively this:
if /bin/test -d somedir
Although the full path /bin/test was never used. I showed it that way
here for emphasis that following the 'if' statement is a command list.
Normally it would simply have been:
if test -d somedir
Of course that is fine and for the best portability that style is
still the recommended way today to use the test command. But many
people find that it looks different from other programming languages.
To make the test operator (note I mention the test operator and not
the shell language, this is a localized change not affecting the
language as a whole) look more like other programming languages the
'test' program was coded to ignore the last argument if it was a ']'.
Then a copy of the test program could be used as the '[' program.
...modify /bin/test to ignore ']' as last argument...
cp /bin/test /bin/[
This allows:
if [ -d somedir ]
Doesn't that look more normal? People liked it and it caught on. It
was so popular that both 'test' and '[' are now shell built-ins. They
don't launch an external '/bin/test' program anymore. But they *used*
to launch external programs. Therefore argument parsing is the same
as if they still did launch an external program. This affects
argument parsing.
it test -f *.txt
test: too many arguments
Oops. I have twenty .txt files and so test got one -f followed by the
first file followed by the remaining files. (e.g. test -f 1.txt 2.txt
3.txt 4.txt)
if test -d $file
test: argument expected
Oops. I meant to set file.
file=/path/some/file
if test -d $file
If variables such as that are not set then they wlll be expanded by
the shell before passing them to the (possibly external) command and
disappear entirely. This is why test arguments should always be quoted.
if test -d &quot;$file&quot;
if [ -d &quot;$file&quot; ]
Actually today test is defined that if only one argument is given as
in this case &quot;test FOO&quot; then then test returns true if the argument is
non-zero in text length. Because &quot;-d&quot; is non-zero length &quot;test -d&quot; is
true. The number of arguments affects how test parses the args. This
avoids a case where depending upon the data may look like a test
operator.
DATA=&quot;something&quot;
if test &quot;$DATA&quot; # true, $DATA is non-zero length
DATA=&quot;&quot;
if test &quot;$DATA&quot; # false, $DATA is zero length
But the problem case is how should test handle an argument that looks
like an operator? This used to generate errors but now because it is
only one argument is defined to be the same as test -n $DATA.
DATA=&quot;-d&quot;
if test &quot;$DATA&quot; # true, $DATA is non-zero length
if test -d # true, same as previous case.
Because test and [ are possibly external commands all of the parts of
them are chosen to avoid shell metacharacters. The Fortran operator
naming was well known at the time (e.g. .gt., .eq., etc.) and was
pressed into service for the shell test operator too. Comming from
Fortran using -gt, -eq, etc. looked very normal.
Incorrect use generating unlikely to be intended results:
if test 5 > 2 # true, &quot;5&quot; is non-zero length, creates file named &quot;2&quot;
Intended use:
if test 5 -gt 2 # true (and no shell meta characters needing quoting)
Then much later, sometime in the mid 1980's, the Korn sh decided to
improve upon this situation. A new test operator was introduced.
This one was always a shell built-in and therefore could act upon the
shell arguments directly. This is '[[' which is a shell keyword.
(Keyword, metacharacters, builtins, all are different.) Because the
shell processes [[ internally all arguments are known and do not need
to be quoted.
if [[ -d $file ]] # okay
if [[ 5 > 2 ]] # okay
I am sure that I am remembering a detail wrong but hopefully this is
useful as a gentle introduction and interesting anyway.
Bob
I hope this text protects you a bit from stepping from one pitfall into
the next.
I find it very interesting and informative, that's why I quoted it here.
Many thanks, Bob, also for the permission to copy the text here!
## Code examples
### Snipplets
Some code snipplets follow, different ways of shell reaction is used.
- **check if a variable is defined/non-NULL**
- `test "$MYVAR"`
- `[ "$MYVAR" ]`
- **Note:** There are possibilities to make a difference if a variable
is *undefined* or *NULL* - see [Parameter Expansion - Using an
alternate value](/syntax/pe#use_an_alternate_value)
- **check if a directory exists, if not, create it**
- `test ! -d /home/user/foo && mkdir /home/user/foo`
- `[ ! -d /home/user/foo ] && mkdir /home/user/foo`
- `if [ ! -d /home/user/foo ]; then mkdir /home/user/foo; fi`
- **check if minimum one parameter was given, and that one is "Hello"**
- `test $# -ge 1 -a "$1" = "Hello" || exit 1`
- `[ $# -ge 1 ] && [ "$1" = "Hello" ] || exit 1` (see [lists
description](/syntax/basicgrammar#lists))
### Listing directories
Using a [for-loop](/syntax/ccmd/classic_for) to iterate through all
entries of a directory, if an entry is a directory (`[ -d "$fn" ]`),
print its name:
for fn in *; do
[ -d &quot;$fn&quot; ] && echo &quot;$fn&quot;
done
## See also
- Internal: [conditional
expression](/syntax/ccmd/conditional_expression) (aka "the new test
command")
[^1]: \<rant\>Of course, one can wonder what is the use of including the
parenthesis in the specification without defining the behaviour with
more than 4 arguments or how usefull are the examples with 7 or 9
arguments attached to the specification.\</rant\>

10
dict/index.md Normal file
View File

@ -0,0 +1,10 @@
# Small Bash- and commandline-related dictionary
This small dictionary is not meant as complete super online computer and
UNIX(r) dictionary, there are just small articles for every term for a
quick reference on other wiki pages.
To get a full description, you should query a real UNIX(r) dictionary or
FAQ, this is just for the most important concepts.
![](pagequery>@terms *;sort=a,name;group;proper;cols=3)

276
howto/calculate-dc.md Normal file
View File

@ -0,0 +1,276 @@
# Calculating with dc
![](keywords>bash shell scripting arithmetic calculate)
## Introduction
dc(1) is a non standard, but commonly found, reverse-polish Desk
Calculator. According to Ken Thompson, "dc is the oldest language on
Unix; it was written on the PDP-7 and ported to the PDP-11 before Unix
\[itself\] was ported".
Historically the standard bc(1) has been implemented as a *front-end to
dc*.
## Simple calculation
In brief, the *reverse polish notation* means the numbers are put on the
stack first, then an operation is applied to them. Instead of writing
`1+1`, you write `1 1+`.
By default `dc`, unlike `bc`, doesn't print anything, the result is
pushed on the stack. You have to use the "p" command to print the
element at the top of the stack. Thus a simple operation looks like:
$ dc <<< '1 1+pq'
2
I used a "here string" present in bash 3.x, ksh93 and zsh. if your shell
doesn't support this, you can use `echo '1 1+p' | dc` or if you have GNU
`dc`, you can use `dc -e '1 1 +p`'.
Of course, you can also just run `dc` and enter the commands.
The classic operations are:
- addition: `+`
- subtraction: `-`
- division: `/`
- multiplication: `*`
- remainder (modulo): `%`
- exponentiation: `^`
- square root: `v`
GNU `dc` adds a couple more.
To input a negative number you need to use the `_` (underscore)
character:
$ dc <<< '1_1-p'
2
You can use the *digits* `0` to `9` and the *letters* `A` to `F` as
numbers, and a dot (`.`) as a decimal point. The `A` to `F` **must** be
capital letters in order not to be confused with the commands specified
with lower case characters. A number with a letter is considered
hexadecimal:
dc <<< 'Ap'
10
The **output** is converted to **base 10** by default
## Scale And Base
`dc` is a calulator with abitrary precision, by default this precision
is 0. thus `dc <<< &quot;5 4/p&quot;` prints "1".
We can increase the precision using the `k` command. It pops the value
at the top of the stack and uses it as the precision argument:
dc <<< '2k5 4/p' # prints 1.25
dc <<< '4k5 4/p' # prints 1.2500
dc <<< '100k 2vp'
1.4142135623730950488016887242096980785696718753769480731766797379907\
324784621070388503875343276415727
dc supports *large* precision arguments.
You can change the base used to output (*print*) the numbers with `o`
and the base used to input (*type*) the numbers with `i`:
dc << EOF
20 p# prints 20, output is in base 10
16o # the output is now in base 2 16
20p # prints 14, in hex
16i # the output is now in hex
p # prints 14 this doesn't modify the number in the stack
10p # prints 10 the output is done in base 16
EOF
Note: when the input value is modified, the base is modified for all
commands, including `i`:
dc << EOF
16i 16o # base is 16 for input and output
10p # prints 10
10i # ! set the base to 10 i.e. to 16 decimal
17p # prints 17
EOF
This code prints 17 while we might think that `10i` reverts the base
back to 10 and thus the number should be converted to hex and printed as
11. The problem is 10 was typed while the input base 16, thus the base
was set to 10 hexadecimal, i.e. 16 decimal.
dc << EOF
16o16o10p #prints 10
Ai # set the base to A in hex i.e. 10
17p # prints 11 in base 16
EOF
## Stack
There are two basic commands to manipulate the stack:
- `d` duplicates the top of the stack
- `c` clears the stack
<!-- -->
$ dc << EOF
2 # put 2 on the stack
d # duplicate i.e. put another 2 on the stack
*p # multiply and print
c p # clear and print
EOF
4
dc: stack empty
`c p` results in an error, as we would expect, as c removes everything
on the stack. *Note: we can use `#` to put comments in the script.*
If you are lost, you can inspect (i.e. print) the stack using the
command `f`. The stack remains unchanged:
dc <<< '1 2 d 4+f'
6
2
1
Note how the first element that will be popped from the stack is printed
first, if you are used to an HP calculator, it's the reverse.
Don't hesitate to put `f` in the examples of this tutorial, it doesn't
change the result, and it's a good way to see what's going on.
## Registers
The GNU `dc` manual says that dc has at least **256 registers**
depending on the range of unsigned char. I'm not sure how you are
supposed to use the NUL byte. Using a register is easy:
dc <<EOF
12 # put 12 on the stack
sa # remove it from the stack (s), and put it in register 'a'
10 # put 10 on the stack
la # read (l) the value of register 'a' and push it on the stack
+p # add the 2 values and print
EOF
The above snippet uses newlines to embed comments, but it doesn't really
matter, you can use `echo '12sa10la+p'| dc`, with the same results.
The register can contain more than just a value, **each register is a
stack on its own**.
dc <<EOF
12sa #store 12 in 'a'
6Sa # with a capital S the 6 is removed
# from the main stack and pushed on the 'a' stack
lap # prints 6, the value at the top of the 'a' stack
lap # still prints 6
Lap # prints 6 also but with a capital L, it pushes the value in 'a'
# to the main stack and pulls it from the 'a' stack
lap # prints 12, which is now at the top of the stack
EOF
## Macros
`dc` lets you push arbitrary strings on the stack when the strings are
enclosed in `[]`. You can print it with `p`: `dc <<< '[Hello World!]p'`
and you can evalute it with x: `dc <<< '[1 2+]xp'`.
This is not that interesting until combined with registers. First, let's
say we want to calculate the square of a number (don't forget to include
`f` if you get lost!):
dc << EOF
3 # push our number on the stack
d # duplicate it i.e. push 3 on the stack again
d**p # duplicate again and calculate the product and print
EOF
Now we have several cubes to calculate, we could use `dd**` several
times, or use a macro.
dc << EOF
[dd**] # push a string
sa # save it in register a
3 # push 3 on the stack
lax # push the string &quot;dd**&quot; on the stack and execute it
p # print the result
4laxp # same operation for 4, in one line
EOF
## Conditionals and Loops
`dc` can execute a macro stored in a register using the `lR x` combo,
but it can also execute macros conditionally. `>a` will execute the
macro stored in the register `a`, if the top of the stack is *greater
than* the second element of the stack. Note: the top of the stack
contains the last entry. When written, it appears as the reverse of what
we are used to reading:
dc << EOF
[[Hello World]p] sR # store in 'R' a macro that prints Hello World
2 1 >R # do nothing 1 is at the top 2 is the second element
1 2 >R # prints Hello World
EOF
Some `dc` have `>R <R =R`, GNU `dc` had some more, check your manual.
Note that the test "consumes" its operands: the 2 first elements are
popped off the stack (you can verify that
`dc <<< &quot;[f]sR 2 1 >R 1 2 >R f&quot;` doesn't print anything)
Have you noticed how we can *include* a macro (string) in a macro? and
as `dc` relies on a stack we can, in fact, use the macro recursively
(have your favorite control-c key combo ready ;)) :
dc << EOF
[ [Hello World] p # our macro starts by printing Hello World
lRx ] # and then executes the macro in R
sR # we store it in the register R
lRx # and finally executes it.
EOF
We have recursivity, we have test, we have loops:
dc << EOF
[ li # put our index i on the stack
p # print it, to see what's going on
1 - # we decrement the index by one
si # store decremented index (i=i-1)
0 li >L # if i > 0 then execute L
] sL # store our macro with the name L
10 si # let's give to our index the value 10
lLx # and start our loop
EOF
Of course code written this way is far too easy to read! Make sure to
remove all those extra spaces newlines and comments:
dc <<< '[lip1-si0li>L]sL10silLx'
dc <<< '[p1-d0<L]sL10lLx' # use the stack instead of a register
I'll let you figure out the second example, it's not hard, it uses the
stack instead of a register for the index.
## Next
Check your dc manual, i haven't decribed everything, like arrays (only
documented with "; : are used by bc(1) for array operations" on solaris,
probably because *echo '1 0:a 0Sa 2 0:a La 0;ap' \| dc* results in
//Segmentation Fault (core dump) //, the latest solaris uses GNU dc)
You can find more info and dc programs here:
- <http://en.wikipedia.org/wiki/Dc_(Unix)>
And more example, as well as a dc implementation in python here:
- <http://en.literateprograms.org/Category:Programming_language:dc>
- <http://en.literateprograms.org/Desk_calculator_%28Python%29>
The manual for the 1971 dc from Bell Labs:

View File

@ -0,0 +1,99 @@
# Collapsing Functions
![](keywords>bash shell scripting example function collapse)
## What is a "Collapsing Function"?
A collapsing function is a function whose behavior changes depending
upon the circumstances under which it's run. Function collapsing is
useful when you find yourself repeatedly checking a variable whose value
never changes.
## How do I make a function collapse?
Function collapsing requires some static feature in the environment. A
common example is a script that gives the user the option of having
"verbose" output.
#!/bin/bash
[[ $1 = -v || $1 = --verbose ]] && verbose=1
chatter() {
if [[ $verbose ]]; then
chatter() {
echo &quot;$@&quot;
}
chatter &quot;$@&quot;
else
chatter() {
:
}
fi
}
echo &quot;Waiting for 10 seconds.&quot;
for i in {1..10}; do
chatter &quot;$i&quot;
sleep 1
done
## How does it work?
The first time you run chatter(), the function redefines itself based on
the value of verbose. Thereafter, chatter doesn't check \$verbose, it
simply is. Further calls to the function reflect its collapsed nature.
If verbose is unset, chatter will echo nothing, with no extra effort
from the developer.
## More examples
FIXME Add more examples!
# Somewhat more portable find -executable
# FIXME/UNTESTED (I don't have access to all of the different versions of find.)
# Usage: find PATH ARGS -- use find like normal, except use -executable instead of
# various versions of -perm /+ blah blah and hacks
find() {
hash find || { echo 'find not found!'; exit 1; }
# We can be pretty sure &quot;$0&quot; should be executable.
if [[ $(command find &quot;$0&quot; -executable 2> /dev/null) ]]; then
unset -f find # We can just use the command find
elif [[ $(command find &quot;$0&quot; -perm /u+x 2> /dev/null) ]]; then
find() {
typeset arg args
for arg do
[[ $arg = -executable ]] && args+=(-perm /u+x) || args+=(&quot;$arg&quot;)
done
command find &quot;${args[@]}&quot;
}
elif [[ $(command find &quot;$0&quot; -perm +u+x 2> /dev/null) ]]; then
find() {
typeset arg args
for arg do
[[ $arg = -executable ]] && args+=(-perm +u+x) || args+=(&quot;$arg&quot;)
done
command find &quot;${args[@]}&quot;
}
else # Last resort
find() {
typeset arg args
for arg do
[[ $arg = -executable ]] && args+=(-exec test -x {} \; -print) || args+=(&quot;$arg&quot;)
done
command find &quot;${args[@]}&quot;
}
fi
find &quot;$@&quot;
}
\<code\> \#!/bin/bash \# Using collapsing functions to turn debug
messages on/off
\[ "--debug" = "\$1" \] && dbg=echo \|\| dbg=:
\# From now on if you use \$dbg instead of echo, you can select if
messages will be shown
\$dbg "This message will only be displayed if --debug is specified at
the command line

105
howto/conffile.md Normal file
View File

@ -0,0 +1,105 @@
# Config files for your script
![](keywords>bash shell scripting config files include configuration)
## General
For this task, you don't have to write large parser routines (unless you
want it 100% secure or you want a special file syntax) - you can use the
Bash source command. The file to be sourced should be formated in
key="value" format, otherwise bash will try to interpret commands:
#!/bin/bash
echo &quot;Reading config....&quot; >&2
source /etc/cool.cfg
echo &quot;Config for the username: $cool_username&quot; >&2
echo &quot;Config for the target host: $cool_host&quot; >&2
So, where do these variables come from? If everything works fine, they
are defined in /etc/cool.cfg which is a file that's sourced into the
current script or shell. Note: this is **not** the same as executing
this file as a script! The sourced file most likely contains something
like:
cool_username=&quot;guest&quot;
cool_host=&quot;foo.example.com&quot;
These are normal statements understood by Bash, nothing special. Of
course (and, a big disadvantage under normal circumstances) the sourced
file can contain **everything** that Bash understands, including
malicious code!
The `source` command also is available under the name `.` (dot). The
usage of the dot is identical:
#!/bin/bash
echo &quot;Reading config....&quot; >&2
. /etc/cool.cfg #note the space between the dot and the leading slash of /etc.cfg
echo &quot;Config for the username: $cool_username&quot; >&2
echo &quot;Config for the target host: $cool_host&quot; >&2
## Per-user configs
There's also a way to provide a system-wide config file in /etc and a
custom config in ~/(user's home) to override system-wide defaults. In
the following example, the if/then construct is used to check for the
existance of a user-specific config:
#!/bin/bash
echo &quot;Reading system-wide config....&quot; >&2
. /etc/cool.cfg
if [ -r ~/.coolrc ]; then
echo &quot;Reading user config....&quot; >&2
. ~/.coolrc
fi
## Secure it
As mentioned earlier, the sourced file can contain anything a Bash
script can. Essentially, it **is** an included Bash script. That creates
security issues. A malicicios person can "execute" arbitrary code when
your script is sourcing its config file. You might want to allow only
constructs in the form `NAME=VALUE` in that file (variable assignment
syntax) and maybe comments (though technically, comments are
unimportant). Imagine the following "config file", containing some
malicious code:
# cool config file for my even cooler script
username=god_only_knows
hostname=www.example.com
password=secret ; echo rm -rf ~/*
parameter=foobar && echo &quot;You've bene pwned!&quot;;
# hey look, weird code follows...
echo &quot;I am the skull virus...&quot;
echo rm -fr ~/*
mailto=netadmin@example.com
You don't want these `echo`-commands (which could be any other
commands!) to be executed. One way to be a bit safer is to filter only
the constructs you want, write the filtered results to a new file and
source the new file. We also need to be sure something nefarious hasn't
been added to the end of one of our name=value parameters, perhaps using
; or && command separators. In those cases, perhaps it is simplest to
just ignore the line entirely. Egrep (`grep -E`) will help us here, it
filters by description:
#!/bin/bash
configfile='/etc/cool.cfg'
configfile_secured='/tmp/cool.cfg'
# check if the file contains something we don't want
if egrep -q -v '^#|^[^ ]*=[^;]*' &quot;$configfile&quot;; then
echo &quot;Config file is unclean, cleaning it...&quot; >&2
# filter the original to a new file
egrep '^#|^[^ ]*=[^;&]*' &quot;$configfile&quot; > &quot;$configfile_secured&quot;
configfile=&quot;$configfile_secured&quot;
fi
# now source it, either the original or the filtered variant
source &quot;$configfile&quot;
**<u>To make clear what it does:</u>** egrep checks if the file contains
something we don't want, if yes, egrep filters the file and writes the
filtered contents to a new file. If done, the original file name is
changed to the name stored in the variable `configfile`. The file named
by that variable is sourced, as if it were the original file.

View File

@ -0,0 +1,124 @@
# Dissect a bad oneliner
``` bash
$ ls *.zip | while read i; do j=`echo $i | sed 's/.zip//g'`; mkdir $j; cd $j; unzip ../$i; cd ..; done
```
This is an actual one-liner someone asked about in `#bash`. **There are
several things wrong with it. Let's break it down!**
``` bash
$ ls *.zip | while read i; do ...; done
```
(Please read <http://mywiki.wooledge.org/ParsingLs>.) This command
executes `ls` on the expansion of `*.zip`. Assuming there are filenames
in the current directory that end in '.zip', ls will give a
human-readable list of those names. The output of ls is not for parsing.
But in sh and bash alike, we can loop safely over the glob itself:
``` bash
$ for i in *.zip; do j=`echo $i | sed 's/.zip//g'`; mkdir $j; cd $j; unzip ../$i; cd ..; done
```
Let's break it down some more!
``` bash
j=`echo $i | sed 's/.zip//g'` # where $i is some name ending in '.zip'
```
The goal here seems to be get the filename without its `.zip` extension.
In fact, there is a POSIX(r)-compliant command to do this: `basename`
The implementation here is suboptimal in several ways, but the only
thing that's genuinely error-prone with this is "`echo $i`". Echoing an
*unquoted* variable means [wordsplitting](/syntax/expansion/wordsplit)
will take place, so any whitespace in `$i` will essentially be
normalized. In `sh` it is necessary to use an external command and a
subshell to achieve the goal, but we can eliminate the pipe (subshells,
external commands, and pipes carry extra overhead when they launch, so
they can really hurt performance in a loop). Just for good measure,
let's use the more readable, [modern](/syntax/expansion/cmdsubst) `$()`
construct instead of the old style backticks:
``` bash
sh $ for i in *.zip; do j=$(basename &quot;$i&quot; &quot;.zip&quot;); mkdir $j; cd $j; unzip ../$i; cd ..; done
```
In Bash we don't need the subshell or the external basename command. See
[Substring removal with parameter
expansion](/syntax/pe#substring_removal):
``` bash
bash $ for i in *.zip; do j=&quot;${i%.zip}&quot;; mkdir $j; cd $j; unzip ../$i; cd ..; done
```
Let's keep going:
``` bash
$ mkdir $j; cd $j; ...; cd ..
```
As a programmer, you **never** know the situation under which your
program will run. Even if you do, the following best practice will never
hurt: When a following command depends on the success of a previous
command(s), check for success! You can do this with the "`&&`"
conjunction, that way, if the previous command fails, bash will not try
to execute the following command(s). It's fully POSIX(r). Oh, and
remember what I said about [wordsplitting](/syntax/expansion/wordsplit)
in the previous step? Well, if you don't quote `$j`, wordsplitting can
happen again.
``` bash
$ mkdir &quot;$j&quot; && cd &quot;$j&quot; && ... && cd ..
```
That's almost right, but there's one problem -- what happens if `$j`
contains a slash? Then `cd ..` will not return to the original
directory. That's wrong! `cd -` causes cd to return to the previous
working directory, so it's a much better choice:
``` bash
$ mkdir &quot;$j&quot; && cd &quot;$j&quot; && ... && cd -
```
(If it occurred to you that I forgot to check for success after cd -,
good job! You could do this with `{ cd - || break; }`, but I'm going to
leave that out because it's verbose and I think it's likely that we will
be able to get back to our original working directory without a
problem.)
So now we have:
``` bash
sh $ for i in *.zip; do j=$(basename &quot;$i&quot; &quot;.zip&quot;); mkdir &quot;$j&quot; && cd &quot;$j&quot; && unzip ../$i && cd -; done
```
``` bash
bash $ for i in *.zip; do j=&quot;${i%.zip}&quot;; mkdir &quot;$j&quot; && cd &quot;$j&quot; && unzip ../$i && cd -; done
```
Let's throw the `unzip` command back in the mix:
``` bash
mkdir &quot;$j&quot; && cd &quot;$j&quot; && unzip ../$i && cd -
```
Well, besides word splitting, there's nothing terribly wrong with this.
Still, did it occur to you that unzip might already be able to target a
directory? There isn't a standard for the `unzip` command, but all the
implementations I've seen can do it with the -d flag. So we can drop the
cd commands entirely:
``` bash
$ mkdir &quot;$j&quot; && unzip -d &quot;$j&quot; &quot;$i&quot;
```
``` bash
sh $ for i in *.zip; do j=$(basename &quot;$i&quot; &quot;.zip&quot;); mkdir &quot;$j&quot; && unzip -d &quot;$j&quot; &quot;$i&quot;; done
```
``` bash
bash $ for i in *.zip; do j=&quot;${i%.zip}&quot;; mkdir &quot;$j&quot; && unzip -d &quot;$j&quot; &quot;$i&quot;; done
```
There! That's as good as it gets.

386
howto/edit-ed.md Normal file
View File

@ -0,0 +1,386 @@
# Editing files via scripts with ed
![](keywords>bash shell scripting arguments file editor edit ed sed)
## Why ed?
Like `sed`, `ed` is a line editor. However, if you try to change file
contents with `sed`, and the file is open elsewhere and read by some
process, you will find out that GNU `sed` and its `-i` option will not
allow you to edit the file. There are circumstances where you may need
that, e.g. editing active and open files, the lack of GNU, or other
`sed`, with "in-place" option available.
Why `ed`?
- maybe your `sed` doesn't support in-place edit
- maybe you need to be as portable as possible
- maybe you need to really edit in-file (and not create a new file like
GNU `sed`)
- last but not least: standard `ed` has very good editing and addressing
possibilities, compared to standard `sed`
Don't get me wrong, this is **not** meant as anti-`sed` article! It's
just meant to show you another way to do the job.
## Commanding ed
Since `ed` is an interactive text editor, it reads and executes commands
that come from `stdin`. There are several ways to feed our commands to
ed:
**<u>Pipelines</u>**
echo '<ED-COMMANDS>' | ed <FILE>
To inject the needed newlines, etc. it may be easier to use the builtin
command, `printf` ("help printf"). Shown here as an example Bash
function to prefix text to file content:
# insertHead &quot;$text&quot; &quot;$file&quot;
insertHead() {
printf '%s\n' H 1i &quot;$1&quot; . w | ed -s &quot;$2&quot;
}
**<u>Here-strings</u>**
ed <FILE> <<< '<ED-COMMANDS>'
**<u>Here-documents</u>**
ed <FILE> <<EOF
<ED-COMMANDS>
EOF
Which one you prefer is your choice. I will use the here-strings, since
it looks best here IMHO.
There are other ways to provide input to `ed`. For example, process
substitution. But these should be enough for daily needs.
Since `ed` wants commands separated by newlines, I'll use a special Bash
quoting method, the C-like strings `$'TEXT'`, as it can interpret a set
of various escape sequences and special characters. I'll use the `-s`
option to make it less verbose.
## The basic interface
Check the `ed` manpage for details
Similar to `vi` or `vim`, `ed` has a "command mode" and an "interactive
mode". For non-interactive use, the command mode is the usual choice.
Commands to `ed` have a simple and regular structure: zero, one, or two
addresses followed by a single-character command, possibly followed by
parameters to that command. These addresses specify one or more lines in
the text buffer. Every command that requires addresses has default
addresses, so the addresses can often be omitted.
The line addressing is relative to the *current line*. If the edit
buffer is not empty, the initial value for the *current line* shall be
the last line in the edit buffer, otherwise zero. Generally, the
*current line* is the last line affected by a command. All addresses can
only address single lines, not blocks of lines!
Line addresses or commands using *regular expressions* interpret POSIX
Basic Regular Expressions (BRE). A null BRE is used to reference the
most recently used BRE. Since `ed` addressing is only for single lines,
no RE can ever match a newline.
## Debugging your ed scripts
By default, `ed` is not very talkative and will simply print a "?" when
an error occurs. Interactively you can use the `h` command to get a
short message explaining the last error. You can also turn on a mode
that makes `ed` automatically print this message with the `H` command.
It is a good idea to always add this command at the beginning of your ed
scripts:
bash > ed -s file <<< $'H\n,df'
?
script, line 2: Invalid command suffix
While working on your script, you might make errors and destroy your
file, you might be tempted to try your script doing something like:
# Works, but there is better
# copy my original file
cp file file.test
# try my script on the file
ed -s file.test <<< $'H\n<ed commands>\nw'
# see the results
cat file.test
There is a much better way though, you can use the ed command `p` to
print the file, now your testing would look like:
ed -s file <<< $'H\n<ed commands>\n,p'
the `,` (comma) in front of the `p` command is a shortcut for `1,$`
which defines an address range for the first to the last line, `,p` thus
means print the whole file, after it has been modified. When your script
runs sucessfully, you only have to replace the `,p` by a `w`.
Of course, even if the file is not modified by the `p` command, **it's
always a good idea to have a backup copy!**
## Editing your files
Most of these things can be done with `sed`. But there are also things
that can't be done in `sed` or can only be done with very complex code.
### Simple word substitutions
Like `sed`, `ed` also knows the common `s/FROM/TO/` command, and it can
also take line-addresses. **If no substitution is made on the addressed
lines, it's considered an error.**
#### Substitutions through the whole file
ed -s test.txt <<< $',s/Windows(R)-compatible/POSIX-conform/g\nw'
<u>Note:</u> The comma as single address operator is an alias for `1,$`
("all lines").
#### Substitutions in specific lines
On a line containing `fruits`, do the substitution:
ed -s test.txt <<< $'/fruits/s/apple/banana/g\nw'
On the 5th line after the line containing `fruits`, do the substitution:
ed -s test.txt <<< $'/fruits/+5s/apple/banana/g\nw'
### Block operations
#### Delete a block of text
The simple one is a well-known (by position) block of text:
# delete lines number 2 to 4 (2, 3, 4)
ed -s test.txt <<< $'2,5d\nw'
This deletes all lines matching a specific regular expression:
# delete all lines matching foobar
ed -s test.txt <<< $'g/foobar/d\nw'
g/regexp/ applies the command following it to all the lines matching the
regexp
#### Move a block of text
...using the `m` command: `<ADDRESS> m <TARGET-ADDRESS>`
This is definitely something that can't be done easily with sed.
# moving lines 5-9 to the end of the file
ed -s test.txt <<< $'5,9m$\nw'
# moving lines 5-9 to line 3
ed -s test.txt <<< $'5,9m3\nw'
#### Copy a block of text
...using the `t` command: `<ADDRESS> t <TARGET-ADDRESS>`
You use the `t` command just like you use the `m` (move) command.
# make a copy of lines 5-9 and place it at the end of the file
ed -s test.txt <<< $'5,9t$\nw'
# make a copy of lines 5-9 and place it at line 3
ed -s test.txt <<< $'5,9t3\nw'
#### Join all lines
...but leave the final newline intact. This is done by an extra command:
`j` (join).
ed -s file <<< $'1,$j\nw'
Compared with two other methods (using `tr` or `sed`), you don't have to
delete all newlines and manually add one at the end.
### File operations
#### Insert another file
How do you insert another file? As with `sed`, you use the `r` (read)
command. That inserts another file at the line before the last line (and
prints the result to stdout - `,p`):
ed -s FILE1 <<< $'$-1 r FILE2\n,p'
To compare, here's a possible `sed` solution which must use Bash
arithmetic and the external program `wc`:
sed &quot;$(($(wc -l < FILE1)-1))r FILE2&quot; FILE1
# UPDATE here's one which uses GNU sed's &quot;e&quot; parameter for the s-command
# it executes the commands found in pattern space. I'll take that as a
# security risk, but well, sometimes GNU > security, you know...
sed '${h;s/.*/cat FILE2/e;G}' FILE1
Another approach, in two invocations of sed, that avoids the use of
external commands completely:
sed $'${s/$/\\n-||-/;r FILE2\n}' FILE1 | sed '0,/-||-/{//!h;N;//D};$G'
## Pitfalls
### ed is not sed
ed and sed might look similar, but the same command(s) might act
differently:
**\_\_ /foo/d \_\_**
In sed /foo/d will delete all lines matching foo, in ed the commands are
not repeated on each line so this command will search the next line
matching foo and delete it. If you want to delete all lines matching
foo, or do a subsitution on all lines matching foo you have to tell ed
about it with the g (global) command:
echo $'1\n1\n3' > file
#replace all lines matching 1 by &quot;replacement&quot;
ed -s file <<< $'g/1/s/1/replacement/\n,p'
#replace the first line matching 1 by &quot;replacement&quot;
#(because it starts searching from the last line)
ed -s file <<< $'s/1/replacement/\n,p'
**\_\_ an error stops the script \_\_**
You might think that it's not a problem and that the same thing happens
with sed and you're right, with the exception that if ed does not find a
pattern it's an error, while sed just continues with the next line. For
instance, let's say that you want to change foo to bar on the first line
of the file and add something after the next line, ed will stop if it
cannot find foo on the first line, sed will continue.
#Gnu sed version
sed -e '1s/foo/bar/' -e '$a\something' file
#First ed version, does nothing if foo is not found on the first line:
ed -s file <<< $'H\n1s/foo/bar/\na\nsomething\n.\nw'
If you want the same behaviour you can use g/foo/ to trick ed. g/foo/
will apply the command on all lines matching foo, thus the substitution
will succeed and ed will not produce an error when foo is not found:
#Second version will add the line with &quot;something&quot; even if foo is not found
ed -s file <<< $'H\n1g/foo/s/foo/bar/\na\nsomething\n.\nw'
In fact, even a substitution that fails after a g/ / command does not
seem to cause an error, i.e. you can use a trick like g/./s/foo/bar/ to
attempt the substitution on all non blank lines
### here documents
**\_\_ shell parameters are expanded \_\_**
If you don't quote the delimiter, \$ has a special meaning. This sounds
obvious but it's easy to forget this fact when you use addresses like
\$-1 or commands like \$a. Either quote the \$ or the delimiter:
#fails
ed -s file << EOF
$a
last line
.
w
EOF
#ok
ed -s file << EOF
\$a
last line
.
w
EOF
#ok again
ed -s file << 'EOF'
$a
last line
.
w
EOF
**\_\_ "." is not a command \_\_**
The . used to terminate the command "a" must be the only thing on the
line. take care if you indent the commands:
#ed doesn't care about the spaces before the commands, but the . must be the only thing on the line:
ed -s file << EOF
a
my content
.
w
EOF
## Simulate other commands
Keep in mind that in all the examples below, the entire file will be
read into memory.
### A simple grep
ed -s file <<< 'g/foo/p'
# equivalent
ed -s file <<< 'g/foo/'
The name `grep` is derived from the notaion `g/RE/p` (global =\> regular
expression =\> print). ref
<http://www.catb.org/~esr/jargon/html/G/grep.html>
### wc -l
Since the default for the `ed` "print line number" command is the last
line, a simple `=` (equal sign) will print this line number and thus the
number of lines of the file:
ed -s file <<< '='
### cat
Yea, it's a joke...
ed -s file <<< $',p'
...but a similar thing to `cat` showing line-endings and escapes can be
done with the `list` command (l):
ed -s file <<< $',l'
FIXME to be continued
## Links
Reference:
- [Gnu ed](http://www.gnu.org/software/ed/manual/ed_manual.html) - if we
had to guess, you're probably using this one.
- POSIX
[ed](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ed.html#tag_20_38),
[ex](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ex.html#tag_20_40),
and
[vi](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/vi.html#tag_20_152)
- <http://sdf.lonestar.org/index.cgi?tutorials/ed> - ed cheatsheet on
sdf.org
Misc info / tutorials:
- [How can I replace a string with another string in a variable, a
stream, a file, or in all the files in a
directory?](http://mywiki.wooledge.org/BashFAQ/021) - BashFAQ

358
howto/getopts_tutorial.md Normal file
View File

@ -0,0 +1,358 @@
# Small getopts tutorial
![](keywords>bash shell scripting arguments positional parameters options getopt getopts)
## Description
**Note that** `getopts` is neither able to parse GNU-style long options
(`--myoption`) nor XF86-style long options (`-myoption`). So, when you
want to parse command line arguments in a professional ;-) way,
`getopts` may or may not work for you. Unlike its older brother `getopt`
(note the missing *s*!), it's a shell builtin command. The advantages
are:
- No need to pass the positional parameters through to an external
program.
- Being a builtin, `getopts` can set shell variables to use for parsing
(impossible for an *external* process!)
- There's no need to argue with several `getopt` implementations which
had buggy concepts in the past (whitespace, ...)
- `getopts` is defined in POSIX(r).
------------------------------------------------------------------------
Some other methods to parse positional parameters - using neither
**getopt** nor **getopts** - are described in: [How to handle positional
parameters](/scripting/posparams).
### Terminology
It's useful to know what we're talking about here, so let's see...
Consider the following command line:
mybackup -x -f /etc/mybackup.conf -r ./foo.txt ./bar.txt
These are all positional parameters, but they can be divided into
several logical groups:
- `-x` is an **option** (aka **flag** or **switch**). It consists of a
dash (`-`) followed by **one** character.
- `-f` is also an option, but this option has an associated **option
argument** (an argument to the option `-f`): `/etc/mybackup.conf`. The
option argument is usually the argument following the option itself,
but that isn't mandatory. Joining the option and option argument into
a single argument `-f/etc/mybackup.conf` is valid.
- `-r` depends on the configuration. In this example, `-r` doesn't take
arguments so it's a standalone option like `-x`.
- `./foo.txt` and `./bar.txt` are remaining arguments without any
associated options. These are often used as **mass-arguments**. For
example, the filenames specified for `cp(1)`, or arguments that don't
need an option to be recognized because of the intended behavior of
the program. POSIX(r) calls them **operands**.
To give you an idea about why `getopts` is useful, The above command
line is equivalent to:
mybackup -xrf /etc/mybackup.conf ./foo.txt ./bar.txt
which is complex to parse without the help of `getopts`.
The option flags can be **upper- and lowercase** characters, or
**digits**. It may recognize other characters, but that's not
recommended (usability and maybe problems with special characters).
### How it works
In general you need to call `getopts` several times. Each time it will
use the next positional parameter and a possible argument, if parsable,
and provide it to you. `getopts` will not change the set of positional
parameters. If you want to shift them, it must be done manually:
shift $((OPTIND-1))
# now do something with $@
Since `getopts` sets an exit status of *FALSE* when there's nothing left
to parse, it's easy to use in a while-loop:
while getopts ...; do
...
done
`getopts` will parse options and their possible arguments. It will stop
parsing on the first non-option argument (a string that doesn't begin
with a hyphen (`-`) that isn't an argument for any option in front of
it). It will also stop parsing when it sees the `--` (double-hyphen),
which means [end of options](/dict/terms/end_of_options).
### Used variables
| variable | description |
|------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [OPTIND](/syntax/shellvars#OPTIND) | Holds the index to the next argument to be processed. This is how `getopts` "remembers" its own status between invocations. Also useful to shift the positional parameters after processing with `getopts`. `OPTIND` is initially set to 1, and **needs to be re-set to 1 if you want to parse anything again with getopts** |
| [OPTARG](/syntax/shellvars#OPTARG) | This variable is set to any argument for an option found by `getopts`. It also contains the option flag of an unknown option. |
| [OPTERR](/syntax/shellvars#OPTERR) | (Values 0 or 1) Indicates if Bash should display error messages generated by the `getopts` builtin. The value is initialized to **1** on every shell startup - so be sure to always set it to **0** if you don't want to see annoying messages! **`OPTERR` is not specified by POSIX for the `getopts` builtin utility --- only for the C `getopt()` function in `unistd.h` (`opterr`).** `OPTERR` is bash-specific and not supported by shells such as ksh93, mksh, zsh, or dash. |
`getopts` also uses these variables for error reporting (they're set to
value-combinations which arent possible in normal operation).
### Specify what you want
The base-syntax for `getopts` is:
getopts OPTSTRING VARNAME [ARGS...]
where:
| `OPTSTRING` | tells `getopts` which options to expect and where to expect arguments (see below) |
|-------------|------------------------------------------------------------------------------------|
| `VARNAME` | tells `getopts` which shell-variable to use for option reporting |
| `ARGS` | tells `getopts` to parse these optional words instead of the positional parameters |
#### The option-string
The option-string tells `getopts` which options to expect and which of
them must have an argument. The syntax is very simple --- every option
character is simply named as is, this example-string would tell
`getopts` to look for `-f`, `-A` and `-x`:
getopts fAx VARNAME
When you want `getopts` to expect an argument for an option, just place
a `:` (colon) after the proper option flag. If you want `-A` to expect
an argument (i.e. to become `-A SOMETHING`) just do:
getopts fA:x VARNAME
If the **very first character** of the option-string is a `:` (colon),
which would normally be nonsense because there's no option letter
preceding it, `getopts` switches to "**silent error reporting mode**".
In productive scripts, this is usually what you want because it allows
you to handle errors yourself without being disturbed by annoying
messages.
#### Custom arguments to parse
The `getopts` utility parses the [positional
parameters](/scripting/posparams) of the current shell or function by
default (which means it parses `"$@"`).
You can give your own set of arguments to the utility to parse. Whenever
additional arguments are given after the `VARNAME` parameter, `getopts`
doesn't try to parse the positional parameters, but these given words.
This way, you are able to parse any option set you like, here for
example from an array:
while getopts :f:h opt &quot;${MY_OWN_SET[@]}&quot;; do
...
done
A call to `getopts` **without** these additional arguments is
**equivalent** to explicitly calling it with `"$@"`:
getopts ... &quot;$@&quot;
### Error Reporting
Regarding error-reporting, there are two modes `getopts` can run in:
- verbose mode
- silent mode
For productive scripts I recommend to use the silent mode, since
everything looks more professional, when you don't see annoying standard
messages. Also it's easier to handle, since the failure cases are
indicated in an easier way.
#### Verbose Mode
| invalid option | `VARNAME` is set to `?` (question-mark) and `OPTARG` is unset |
|-----------------------------|----------------------------------------------------------------------------------------------|
| required argument not found | `VARNAME` is set to `?` (question-mark), `OPTARG` is unset and an *error message is printed* |
#### Silent Mode
| invalid option | `VARNAME` is set to `?` (question-mark) and `OPTARG` is set to the (invalid) option character |
|-----------------------------|-----------------------------------------------------------------------------------------------|
| required argument not found | `VARNAME` is set to `:` (colon) and `OPTARG` contains the option-character in question |
## Using it
### A first example
Enough said - action!
Let's play with a very simple case: only one option (`-a`) expected,
without any arguments. Also we disable the *verbose error handling* by
preceding the whole option string with a colon (`:`):
``` bash
#!/bin/bash
while getopts &quot;:a&quot; opt; do
case $opt in
a)
echo &quot;-a was triggered!&quot; >&2
;;
\?)
echo &quot;Invalid option: -$OPTARG&quot; >&2
;;
esac
done
```
I put that into a file named `go_test.sh`, which is the name you'll see
below in the examples.
Let's do some tests:
#### Calling it without any arguments
$ ./go_test.sh
$
Nothing happened? Right. `getopts` didn't see any valid or invalid
options (letters preceded by a dash), so it wasn't triggered.
#### Calling it with non-option arguments
$ ./go_test.sh /etc/passwd
$
Again --- nothing happened. The **very same** case: `getopts` didn't see
any valid or invalid options (letters preceded by a dash), so it wasn't
triggered.
The arguments given to your script are of course accessible as `$1` -
`${N}`.
#### Calling it with option-arguments
Now let's trigger `getopts`: Provide options.
First, an **invalid** one:
$ ./go_test.sh -b
Invalid option: -b
$
As expected, `getopts` didn't accept this option and acted like told
above: It placed `?` into `$opt` and the invalid option character (`b`)
into `$OPTARG`. With our `case` statement, we were able to detect this.
Now, a **valid** one (`-a`):
$ ./go_test.sh -a
-a was triggered!
$
You see, the detection works perfectly. The `a` was put into the
variable `$opt` for our case statement.
Of course it's possible to **mix valid and invalid** options when
calling:
$ ./go_test.sh -a -x -b -c
-a was triggered!
Invalid option: -x
Invalid option: -b
Invalid option: -c
$
Finally, it's of course possible, to give our option **multiple times**:
$ ./go_test.sh -a -a -a -a
-a was triggered!
-a was triggered!
-a was triggered!
-a was triggered!
$
The last examples lead us to some points you may consider:
- **invalid options don't stop the processing**: If you want to stop the
script, you have to do it yourself (`exit` in the right place)
- **multiple identical options are possible**: If you want to disallow
these, you have to check manually (e.g. by setting a variable or so)
### An option with argument
Let's extend our example from above. Just a little bit:
- `-a` now takes an argument
- on an error, the parsing exits with `exit 1`
``` bash
#!/bin/bash
while getopts &quot;:a:&quot; opt; do
case $opt in
a)
echo &quot;-a was triggered, Parameter: $OPTARG&quot; >&2
;;
\?)
echo &quot;Invalid option: -$OPTARG&quot; >&2
exit 1
;;
:)
echo &quot;Option -$OPTARG requires an argument.&quot; >&2
exit 1
;;
esac
done
```
Let's do the very same tests we did in the last example:
#### Calling it without any arguments
$ ./go_test.sh
$
As above, nothing happened. It wasn't triggered.
#### Calling it with non-option arguments
$ ./go_test.sh /etc/passwd
$
The **very same** case: It wasn't triggered.
#### Calling it with option-arguments
**Invalid** option:
$ ./go_test.sh -b
Invalid option: -b
$
As expected, as above, `getopts` didn't accept this option and acted
like programmed.
**Valid** option, but without the mandatory **argument**:
$ ./go_test.sh -a
Option -a requires an argument.
$
The option was okay, but there is an argument missing.
Let's provide **the argument**:
$ ./go_test.sh -a /etc/passwd
-a was triggered, Parameter: /etc/passwd
$
## See also
- Internal: [posparams](/scripting/posparams)
- Internal: [case](/syntax/ccmd/case)
- Internal: [while_loop](/syntax/ccmd/while_loop)
- POSIX
[getopts(1)](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/getopts.html#tag_20_54)
and
[getopt(3)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/getopt.html)
- [parse CLI
ARGV](https://stackoverflow.com/questions/192249/how-do-i-parse-command-line-arguments-in-bash)
- [handle command-line arguments (options) to a
script](http://mywiki.wooledge.org/BashFAQ/035)

202
howto/mutex.md Normal file
View File

@ -0,0 +1,202 @@
# Lock your script (against parallel execution)
![](keywords>bash shell scripting mutex locking run-control)
## Why lock?
Sometimes there's a need to ensure only one copy of a script runs, i.e
prevent two or more copies running simultaneously. Imagine an important
cronjob doing something very important, which will fail or corrupt data
if two copies of the called program were to run at the same time. To
prevent this, a form of `MUTEX` (**mutual exclusion**) lock is needed.
The basic procedure is simple: The script checks if a specific condition
(locking) is present at startup, if yes, it's locked - the scipt doesn't
start.
This article describes locking with common UNIX(r) tools. There are
other special locking tools available, But they're not standardized, or
worse yet, you can't be sure they're present when you want to run your
scripts. **A tool designed for specifically for this purpose does the
job much better than general purpose code.**
### Other, special locking tools
As told above, a special tool for locking is the preferred solution.
Race conditions are avoided, as is the need to work around specific
limits.
- `flock`: <http://www.kernel.org/pub/software/utils/script/flock/>
- `solo`: <http://timkay.com/solo/>
## Choose the locking method
The best way to set a global lock condition is the UNIX(r) filesystem.
Variables aren't enough, as each process has its own private variable
space, but the filesystem is global to all processes (yes, I know about
chroots, namespaces, ... special case). You can "set" several things in
the filesystem that can be used as locking indicator:
- create files
- update file timestamps
- create directories
To create a file or set a file timestamp, usually the command touch is
used. The following problem is implied: A locking mechanism checks for
the existance of the lockfile, if no lockfile exists, it creates one and
continues. Those are **two separate steps**! That means it's **not an
atomic operation**. There's a small amount of time between checking and
creating, where another instance of the same script could perform
locking (because when it checked, the lockfile wasn't there)! In that
case you would have 2 instances of the script running, both thinking
they are succesfully locked, and can operate without colliding. Setting
the timestamp is similar: One step to check the timespamp, a second step
to set the timestamp.
\<WRAP center round tip 60%\> <u>**Conclusion:**</u> We need an
operation that does the check and the locking in one step. \</WRAP\>
A simple way to get that is to create a **lock directory** - with the
mkdir command. It will:
* create a given directory only if it does not exist, and set a successful exit code
* it will set an unsuccesful exit code if an error occours - for example, if the directory specified already exists
With mkdir it seems, we have our two steps in one simple operation. A
(very!) simple locking code might look like this:
``` bash
if mkdir /var/lock/mylock; then
echo &quot;Locking succeeded&quot; >&2
else
echo &quot;Lock failed - exit&quot; >&2
exit 1
fi
```
In case `mkdir` reports an error, the script will exit at this point -
**the MUTEX did its job!**
*If the directory is removed after setting a successful lock, while the
script is still running, the lock is lost. Doing chmod -w for the parent
directory containing the lock directory can be done, but it is not
atomic. Maybe a while loop checking continously for the existence of the
lock in the background and sending a signal such as USR1, if the
directory is not found, can be done. The signal would need to be
trapped. I am sure there there is a better solution than this
suggestion* --- *[sn18](sunny_delhi18@yahoo.com) 2009/12/19 08:24*
**Note:** While perusing the Internet, I found some people asking if the
`mkdir` method works "on all filesystems". Well, let's say it should.
The syscall under `mkdir` is guarenteed to work atomicly in all cases,
at least on Unices. Two examples of problems are NFS filesystems and
filesystems on cluster servers. With those two scenarios, dependencies
exist related to the mount options and implementation. However, I
successfully use this simple method on an Oracle OCFS2 filesystem in a
4-node cluster environment. So let's just say "it should work under
normal conditions".
Another atomic method is setting the `noclobber` shell option
(`set -C`). That will cause redirection to fail, if the file the
redirection points to already exists (using diverse `open()` methods).
Need to write a code example here.
``` bash
if ( set -o noclobber; echo &quot;locked&quot; > &quot;$lockfile&quot;) 2> /dev/null; then
trap 'rm -f &quot;$lockfile&quot;; exit $?' INT TERM EXIT
echo &quot;Locking succeeded&quot; >&2
rm -f &quot;$lockfile&quot;
else
echo &quot;Lock failed - exit&quot; >&2
exit 1
fi
```
Another explanation of this basic pattern using `set -C` can be found
[here](http://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xcu_chap02.html#tag_23_02_07).
## An example
This code was taken from a production grade script that controls PISG to
create statistical pages from my IRC logfiles. There are some
differences compared to the very simple example above:
- the locking stores the process ID of the locked instance
- if a lock fails, the script tries to find out if the locked instance
still is active (unreliable!)
- traps are created to automatically remove the lock when the script
terminates, or is killed
Details on how the script is killed aren't given, only code relevant to
the locking process is shown:
``` bash
#!/bin/bash
# lock dirs/files
LOCKDIR=&quot;/tmp/statsgen-lock&quot;
PIDFILE=&quot;${LOCKDIR}/PID&quot;
# exit codes and text
ENO_SUCCESS=0; ETXT[0]=&quot;ENO_SUCCESS&quot;
ENO_GENERAL=1; ETXT[1]=&quot;ENO_GENERAL&quot;
ENO_LOCKFAIL=2; ETXT[2]=&quot;ENO_LOCKFAIL&quot;
ENO_RECVSIG=3; ETXT[3]=&quot;ENO_RECVSIG&quot;
###
### start locking attempt
###
trap 'ECODE=$?; echo &quot;[statsgen] Exit: ${ETXT[ECODE]}($ECODE)&quot; >&2' 0
echo -n &quot;[statsgen] Locking: &quot; >&2
if mkdir &quot;${LOCKDIR}&quot; &>/dev/null; then
# lock succeeded, install signal handlers before storing the PID just in case
# storing the PID fails
trap 'ECODE=$?;
echo &quot;[statsgen] Removing lock. Exit: ${ETXT[ECODE]}($ECODE)&quot; >&2
rm -rf &quot;${LOCKDIR}&quot;' 0
echo &quot;$$&quot; >&quot;${PIDFILE}&quot;
# the following handler will exit the script upon receiving these signals
# the trap on &quot;0&quot; (EXIT) from above will be triggered by this trap's &quot;exit&quot; command!
trap 'echo &quot;[statsgen] Killed by a signal.&quot; >&2
exit ${ENO_RECVSIG}' 1 2 3 15
echo &quot;success, installed signal handlers&quot;
else
# lock failed, check if the other PID is alive
OTHERPID=&quot;$(cat &quot;${PIDFILE}&quot;)&quot;
# if cat isn't able to read the file, another instance is probably
# about to remove the lock -- exit, we're *still* locked
# Thanks to Grzegorz Wierzowiecki for pointing out this race condition on
# http://wiki.grzegorz.wierzowiecki.pl/code:mutex-in-bash
if [ $? != 0 ]; then
echo &quot;lock failed, PID ${OTHERPID} is active&quot; >&2
exit ${ENO_LOCKFAIL}
fi
if ! kill -0 $OTHERPID &>/dev/null; then
# lock is stale, remove it and restart
echo &quot;removing stale lock of nonexistant PID ${OTHERPID}&quot; >&2
rm -rf &quot;${LOCKDIR}&quot;
echo &quot;[statsgen] restarting myself&quot; >&2
exec &quot;$0&quot; &quot;$@&quot;
else
# lock is valid and OTHERPID is active - exit, we're locked!
echo &quot;lock failed, PID ${OTHERPID} is active&quot; >&2
exit ${ENO_LOCKFAIL}
fi
fi
```
## Related links
- [BashFAQ/045](http://mywiki.wooledge.org/BashFAQ/045)
- [Implementation of a shell locking
utility](http://wiki.grzegorz.wierzowiecki.pl/code:mutex-in-bash)

347
howto/pax.md Normal file
View File

@ -0,0 +1,347 @@
# pax - the POSIX archiver
![](keywords>bash shell scripting POSIX archive tar packing zip)
pax can do a lot of fancy stuff, feel free to contribute more awesome
pax tricks!
## Introduction
The POSIX archiver, `pax`, is an attempt at a standardized archiver with
the best features of `tar` and `cpio`, able to handle all common archive
types.
However, this is **not a manpage**, it will **not** list all possible
options, it will **not** you detailed information about `pax`. It's only
an introduction.
This article is based on the debianized Berkeley implementation of
`pax`, but implementation-specific things should be tagged as such.
Unfortunately, the Debian package doesn't seem to be maintained anymore.
## Overview
### Operation modes
There are four basic operation modes to *list*, *read*, *write* and
*copy* archives. They're switched with combinations of `-r` and `-w`
command line options:
| Mode | RW-Options |
|-------|-----------------|
| List | *no RW-options* |
| Read | `-r` |
| Write | `-w` |
| Copy | `-r -w` |
#### List
In *list mode*, `pax` writes the list of archive members to standard
output (a table of contents). If a pattern match is specified on the
command line, only matching filenames are printed.
#### Read
*Read* an archive. `pax` will read archive data and extract the members
to the current directory. If a pattern match is specified on the command
line, only matching filenames are extracted.
When reading an archive, the archive type is determined from the archive
data.
#### Write
*Write* an archive, which means create a new one or append to an
existing one. All files and directories specified on the command line
are inserted into the archive. The archive is written to standard output
by default.
If no files are specified on the command line, filenames are read from
`STDIN`.
The write mode is the only mode where you need to specify the archive
type with `-x <TYPE>`, e.g. `-x ustar`.
#### Copy
*Copy* mode is similar to `cpio` passthrough mode. It provides a way to
replicate a complete or partial file hierarchy (with all the `pax`
options, e.g. rewriting groups) to another location.
### Archive data
When you don't specify anything special, `pax` will attempt to read
archive data from standard input (read/list modes) and write archive
data to standard output (write mode). This ensures `pax` can be easily
used as part of a shell pipe construct, e.g. to read a compressed
archive that's decompressed in the pipe.
The option to specify the pathname of a file to be archived is `-f` This
file will be used as input or output, depending on the operation
(read/write/list).
When pax reads an archive, it tries to guess the archive type. However,
in *write* mode, you must specify which type of archive to append using
the `-x <TYPE>` switch. If you omit this switch, a default archive will
be created (POSIX says it's implementation defined, Berkeley `pax`
creates `ustar` if no options are specified).
The following archive formats are supported (Berkeley implementation):
| | |
|:--------|----------------------------|
| ustar | POSIX TAR format (default) |
| cpio | POSIX CPIO format |
| tar | classic BSD TAR format |
| bcpio | old binary CPIO format |
| sv4cpio | SVR4 CPIO format |
| sv4crc | SVR4 CPIO format with CRC |
Berkeley `pax` supports options `-z` and `-j`, similar to GNU `tar`, to
filter archive files through GZIP/BZIP2.
### Matching archive members
In *read* and *list* modes, you can specify patterns to determine which
files to list or extract.
- the pattern notation is the one known by a POSIX-shell, i.e. the one
known by Bash without `extglob`
- if the specified pattern matches a complete directory, it affects all
files and subdirectories of the specified directory
- if you specify the `-c` option, `pax` will invert the matches, i.e. it
matches all filenames **except** those matching the specified patterns
- if no patterns are given, `pax` will "match" (list or extract) all
files from the archive
- **To avoid conflicts with shell pathname expansion, it's wise to quote
patterns!**
#### Some assorted examples of patterns
pax -r <myarchive.tar 'data/sales/*.txt' 'data/products/*.png'
pax -r <myarchive.tar 'data/sales/year_200[135].txt'
# should be equivalent to
pax -r <myarchive.tar 'data/sales/year_2001.txt' 'data/sales/year_2003.txt' 'data/sales/year_2005.txt'
## Using pax
This is a brief description of using `pax` as a normal archiver system,
like you would use `tar`.
### Creating an archive
This task is done with basic syntax
# archive contents to stdout
pax -w >archive.tar README.txt *.png data/
# equivalent, extract archive contents directly to a file
pax -w -x ustar -f archive.tar README.txt *.png data/
`pax` is in *write* mode, the given filenames are packed into an
archive:
- `README.txt` is a normal file, it will be packed
- `*.png` is a pathname glob **for your shell**, the shell will
substitute all matching filenames **before** `pax` is executed. The
result is a list of filenames that will be packed like the
`README.txt` example above
- `data/` is a directory. **Everything** in this directory will be
packed into the archive, i.e. not just an empty directory
When you specify the `-v` option, `pax` will write the pathnames of the
files inserted into the archive to `STDERR`.
When, and only when, no filename arguments are specified, `pax` attempts
to read filenames from `STDIN`, separated by newlines. This way you can
easily combine `find` with `pax`:
find . -name '*.txt' | pax -wf textfiles.tar -x ustar
### Listing archive contents
The standard output format to list archive members simply is to print
each filename to a separate line. But the output format can be
customized to include permissions, timestamps, etc. with the
`-o listopt=<FORMAT>` specification. The syntax of the format
specification is strongly derived from the `printf(3)` format
specification.
**Unfortunately** the `pax` utility delivered with Debian doesn't seem
to support these extended listing formats.
However, `pax` lists archive members in a `ls -l`-like format, when you
give the `-v` option:
pax -v <myarchive.tar
# or, of course
pax -vf myarchive.tar
### Extracting from an archive
You can extract all files, or files (not) matching specific patterns
from an archive using constructs like:
# &quot;normal&quot; extraction
pax -rf myarchive.tar '*.txt'
# with inverted pattern
pax -rf myarchive.tar -c '*.txt'
### Copying files
To copy directory contents to another directory, similar to a `cp -a`
command, use:
mkdir destdir
pax -rw dir destdir #creates a copy of dir in destdir/, i.e. destdir/dir
### Copying files via ssh
To copy directory contents to another directory on a remote system, use:
pax -w localdir | ssh user@host &quot;cd distantdest && pax -r -v&quot;
pax -w localdir | gzip | ssh user@host &quot;cd distantdir && gunzip | pax -r -v&quot; #compress the sent data
These commands create a copy of localdir in distandir (distantdir/dir)
on the remote machine.
## Advanced usage
### Backup your daily work
<u>**Note:**</u> `-T` is an extension and is not defined by POSIX.
Say you have write-access to a fileserver mounted on your filesystem
tree. In *copy* mode, you can tell `pax` to copy only files that were
modified today:
mkdir /n/mybackups/$(date +%A)/
pax -rw -T 0000 data/ /n/mybackups/$(date +%A)/
This is done using the `-T` switch, which normally allows you to specify
a time window, but in this case, only the start time which means "today
at midnight".
When you execute this "very simple backup" after your daily work, you
will have a copy of the modified files.
<u>**Note:**</u> The `%A` format from `date` expands to the name of the
current day, localized, e.g. "Friday" (en) or "Mittwoch" (de).
The same, but with an archive, can be accomplished by:
pax -w -T 0000 -f /n/mybackups/$(date +%A)
In this case, the day-name is an archive-file (you don't need a filename
extension like `.tar` but you can add one, if desired).
### Changing filenames while archiving
`pax` is able to rewrite filenames while archiving or while extracting
from an archive. This example creates a tar archive containing the
`holiday_2007/` directory, but the directory name inside the archive
will be `holiday_pics/`:
pax -x ustar -w -f holiday_pictures.tar -s '/^holiday_2007/holiday_pics/' holiday_2007/
The option responsible for the string manipulation is the
`-s <REWRITE-SPECIFICATION>`. It takes the string rewrite specification
as an argument, in the form `/OLD/NEW/[gp]`, which is an `ed(1)`-like
regular expression (BRE) for `old` and generally can be used like the
popular sed construct `s/from/to/`. Any non-null character can be used
as a delimiter, so to mangle pathnames (containing slashes), you could
use `#/old/path#/new/path#`.
The optional `g` and `p` flags are used to apply substitution
**(g)**lobally to the line or to **(p)**rint the original and rewritten
strings to `STDERR`.
Multiple `-s` options can be specified on the command line. They are
applied to the pathname strings of the files or archive members. This
happens in the order they are specified.
### Excluding files from an archive
The -s command seen above can be used to exclude a file. The
substitution must result in a null string: For example, let's say that
you want to exclude all the CVS directories to create a source code
archive. We are going to replace the names containing /CVS/ with
nothing, note the .\* they are needed because we need to match the
entire pathname.
pax -w -x ustar -f release.tar -s',.*/CVS/.*,,' myapplication
You can use several -s options, for instance, let's say you also want to
remove files ending in ~:
pax -w -x ustar -f release.tar -'s,.*/CVS/.*,,' -'s/.*~//' myapplication
This can also be done while reading an archive, for instance, suppose
you have an archive containing a "usr" and a "etc" directory but that
you want to extract only the "usr" directory:
pax -r -f archive.tar -s',^etc/.*,,' #the etc/ dir is not extracted
### Getting archive filenames from STDIN
Like `cpio`, pax can read filenames from standard input (`stdin`). This
provides great flexibility - for example, a `find(1)` command may select
files/directories in ways pax can't do itself. In **write** mode
(creating an archive) or **copy** mode, when no filenames are given, pax
expects to read filenames from standard input. For example:
# Back up config files changed less than 3 days ago
find /etc -type f -mtime -3 | pax -x ustar -w -f /backups/etc.tar
# Copy only the directories, not the files
mkdir /target
find . -type d -print | pax -r -w -d /target
# Back up anything that changed since the last backup
find . -newer /var/run/mylastbackup -print0 |
pax -0 -x ustar -w -d -f /backups/mybackup.tar
touch /var/run/mylastbackup
The `-d` option tells pax `not` to recurse into directories it reads
(`cpio`-style). Without `-d`, pax recurses into all directories
(`tar`-style).
**Note**: the `-0` option is not standard, but is present in some
implementations.
## From tar to pax
`pax` can handle the `tar` archive format, if you want to switch to the
standard tool an alias like:
alias tar='echo USE PAX, idiot. pax is the standard archiver!; # '
in your `~/.bashrc` can be useful :-D.
Here is a quick table comparing (GNU) `tar` and `pax` to help you to
make the switch:
| TAR | PAX | Notes |
|-------------------------------------|------------------------------------------|-----------------------------------------------------------------------|
| `tar xzvf file.tar.gz` | `pax -rvz -f file.tar.gz` | `-z` is an extension, POSIXly: `gunzip <file.tar.gz | pax -rv` |
| `tar czvf archive.tar.gz path ...` | `pax -wvz -f archive.tar.gz path ...` | `-z` is an extension, POSIXly: `pax -wv path | gzip > archive.tar.gz` |
| `tar xjvf file.tar.bz2` | `bunzip2 <file.tar.bz2 | pax -rv` | |
| `tar cjvf archive.tar.bz2 path ...` | `pax -wv path | bzip2 > archive.tar.bz2` | |
| `tar tzvf file.tar.gz` | `pax -vz -f file.tar.gz` | `-z` is an extension, POSIXly: `gunzip <file.tar.gz | pax -v` |
`pax` might not create ustar (`tar`) archives by default but its own pax
format, add `-x ustar` if you want to ensure pax creates tar archives!
## Implementations
- [AT&T AST toolkit](http://www2.research.att.com/sw/download/) \|
[manpage](http://www2.research.att.com/~gsf/man/man1/pax.html)
- [Heirloom toolchest](http://heirloom.sourceforge.net/index.html) \|
[manpage](http://heirloom.sourceforge.net/man/pax.1.html)
- [OpenBSD pax](http://www.openbsd.org/cgi-bin/cvsweb/src/bin/pax/) \|
[manpage](http://www.openbsd.org/cgi-bin/man.cgi?query=pax&apropos=0&sektion=0&manpath=OpenBSD+Current&arch=i386&format=html)
- [MirBSD pax](https://launchpad.net/paxmirabilis) \|
[manpage](https://www.mirbsd.org/htman/i386/man1/pax.htm) - Debian
bases their package upon this.

View File

@ -0,0 +1,686 @@
# Illustrated Redirection Tutorial
![](keywords>bash shell scripting tutorial redirection redirect file descriptor)
This tutorial is not a complete guide to redirection, it will not cover
here docs, here strings, name pipes etc... I just hope it'll help you to
understand what things like `3>&2`, `2>&1` or `1>&3-` do.
# stdin, stdout, stderr
When Bash starts, normally, 3 file descriptors are opened, `0`, `1` and
`2` also known as standard input (`stdin`), standard output (`stdout`)
and standard error (`stderr`).
For example, with Bash running in a Linux terminal emulator, you'll see:
# lsof +f g -ap $BASHPID -d 0,1,2
COMMAND PID USER FD TYPE FILE-FLAG DEVICE SIZE/OFF NODE NAME
bash 12135 root 0u CHR RW,LG 136,13 0t0 16 /dev/pts/5
bash 12135 root 1u CHR RW,LG 136,13 0t0 16 /dev/pts/5
bash 12135 root 2u CHR RW,LG 136,13 0t0 16 /dev/pts/5
This `/dev/pts/5` is a pseudo terminal used to emulate a real terminal.
Bash reads (`stdin`) from this terminal and prints via `stdout` and
`stderr` to this terminal.
--- +-----------------------+
standard input ( 0 ) ---->| /dev/pts/5 |
--- +-----------------------+
--- +-----------------------+
standard output ( 1 ) ---->| /dev/pts/5 |
--- +-----------------------+
--- +-----------------------+
standard error ( 2 ) ---->| /dev/pts/5 |
--- +-----------------------+
When a command, a compound command, a subshell etc. is executed, it
inherits these file descriptors. For instance `echo foo` will send the
text `foo` to the file descriptor `1` inherited from the shell, which is
connected to `/dev/pts/5`.
# Simple Redirections
## Output Redirection "n\> file"
`>` is probably the simplest redirection.
`echo foo > file`
the `> file` after the command alters the file descriptors belonging to
the command `echo`. It changes the file descriptor `1` (`> file` is the
same as `1>file`) so that it points to the file `file`. They will look
like:
--- +-----------------------+
standard input ( 0 ) ---->| /dev/pts/5 |
--- +-----------------------+
--- +-----------------------+
standard output ( 1 ) ---->| file |
--- +-----------------------+
--- +-----------------------+
standard error ( 2 ) ---->| /dev/pts/5 |
--- +-----------------------+
Now characters written by our command, `echo`, that are sent to the
standard output, i.e., the file descriptor `1`, end up in the file named
`file`.
In the same way, command `2> file` will change the standard error and
will make it point to `file`. Standard error is used by applications to
print errors.
What will `command 3> file` do? It will open a new file descriptor
pointing to `file`. The command will then start with:
--- +-----------------------+
standard input ( 0 ) ---->| /dev/pts/5 |
--- +-----------------------+
--- +-----------------------+
standard output ( 1 ) ---->| /dev/pts/5 |
--- +-----------------------+
--- +-----------------------+
standard error ( 2 ) ---->| /dev/pts/5 |
--- +-----------------------+
--- +-----------------------+
new descriptor ( 3 ) ---->| file |
--- +-----------------------+
What will the command do with this descriptor? It depends. Often
nothing. We will see later why we might want other file descriptors.
## Input Redirection "n\< file"
When you run a commandusing `command < file`, it changes the file
descriptor `0` so that it looks like:
--- +-----------------------+
standard input ( 0 ) <----| file |
--- +-----------------------+
--- +-----------------------+
standard output ( 1 ) ---->| /dev/pts/5 |
--- +-----------------------+
--- +-----------------------+
standard error ( 2 ) ---->| /dev/pts/5 |
--- +-----------------------+
If the command reads from `stdin`, it now will read from `file` and not
from the console.
As with `>`, `<` can be used to open a new file descriptor for reading,
`command 3<file`. Later we will see how this can be useful.
## Pipes \|
What does this `|` do? Among other things, it connects the standard
output of the command on the left to the standard input of the command
on the right. That is, it creates a special file, a pipe, which is
opened as a write destinaton for the left command, and as a read source
for the right command.
echo foo | cat
--- +--------------+ --- +--------------+
( 0 ) ---->| /dev/pts/5 | ------> ( 0 ) ---->|pipe (read) |
--- +--------------+ / --- +--------------+
/
--- +--------------+ / --- +--------------+
( 1 ) ---->| pipe (write) | / ( 1 ) ---->| /dev/pts |
--- +--------------+ --- +--------------+
--- +--------------+ --- +--------------+
( 2 ) ---->| /dev/pts/5 | ( 2 ) ---->| /dev/pts/ |
--- +--------------+ --- +--------------+
This is possible because the redirections are set up by the shell
**before** the commands are executed, and the commands inherit the file
descriptors.
# More On File Descriptors
## Duplicating File Descriptor 2\>&1
We have seen how to open (or redirect) file descriptors. Let us see how
to duplicate them, starting with the classic `2>&1`. What does this
mean? That something written on the file descriptor `2` will go where
file descriptor `1` goes. In a shell `command 2>&1` is not a very
interesting example so we will use `ls /tmp/ doesnotexist 2>&1 | less`
ls /tmp/ doesnotexist 2>&1 | less
--- +--------------+ --- +--------------+
( 0 ) ---->| /dev/pts/5 | ------> ( 0 ) ---->|from the pipe |
--- +--------------+ / ---> --- +--------------+
/ /
--- +--------------+ / / --- +--------------+
( 1 ) ---->| to the pipe | / / ( 1 ) ---->| /dev/pts |
--- +--------------+ / --- +--------------+
/
--- +--------------+ / --- +--------------+
( 2 ) ---->| to the pipe | / ( 2 ) ---->| /dev/pts/ |
--- +--------------+ --- +--------------+
Why is it called *duplicating*? Because after `2>&1`, we have 2 file
descriptors pointing to the same file. Take care not to call this "File
Descriptor Aliasing"; if we redirect `stdout` after `2>&1` to a file
`B`, file descriptor `2` will still be opened on the file `A` where it
was. This is often misunderstood by people wanting to redirect both
standard input and standard output to the file. Continue reading for
more on this.
So if you have two file descriptors `s` and `t` like:
--- +-----------------------+
a descriptor ( s ) ---->| /some/file |
--- +-----------------------+
--- +-----------------------+
a descriptor ( t ) ---->| /another/file |
--- +-----------------------+
Using a `t>&s` (where `t` and `s` are numbers) it means:
> Copy whatever file descriptor `s` contains into file descriptor `t`
So you got a copy of this descriptor:
--- +-----------------------+
a descriptor ( s ) ---->| /some/file |
--- +-----------------------+
--- +-----------------------+
a descriptor ( t ) ---->| /some/file |
--- +-----------------------+
Internally each of these is represented by a file descriptor opened by
the operating system's `fopen` calls, and is likely just a pointer to
the file which has been opened for reading (`stdin` or file descriptor
`0`) or writing (`stdout` /`stderr`).
Note that the file reading or writing positions are also duplicated. If
you have already read a line of `s`, then after `t>&s` if you read a
line from `t`, you will get the second line of the file.
Similarly for output file descriptors, writing a line to file descriptor
`s` will append a line to a file as will writing a line to file
descriptor `t`.
\<note tip\>The syntax is somewhat confusing in that you would think
that the arrow would point in the direction of the copy, but it's
reversed. So it's `target>&source` effectively.\</note\>
So, as a simple example (albeit slightly contrived), is the following:
exec 3>&1 # Copy 1 into 3
exec 1> logfile # Make 1 opened to write to logfile
lotsa_stdout # Outputs to fd 1, which writes to logfile
exec 1>&3 # Copy 3 back into 1
echo Done # Output to original stdout
## Order Of Redirection, i.e., "\> file 2\>&1" vs. "2\>&1 \>file"
While it doesn't matter where the redirections appears on the command
line, their order does matter. They are set up from left to right.
- `2>&1 >file`
A common error, is to do `command 2>&1 > file` to redirect both `stderr`
and `stdout` to `file`. Let's see what's going on. First we type the
command in our terminal, the descriptors look like this:
--- +-----------------------+
standard input ( 0 ) ---->| /dev/pts/5 |
--- +-----------------------+
--- +-----------------------+
standard output ( 1 ) ---->| /dev/pts/5 |
--- +-----------------------+
--- +-----------------------+
standard error ( 2 ) ---->| /dev/pts/5 |
--- +-----------------------+
Then our shell, Bash sees `2>&1` so it duplicates 1, and the file
descriptor look like this:
--- +-----------------------+
standard input ( 0 ) ---->| /dev/pts/5 |
--- +-----------------------+
--- +-----------------------+
standard output ( 1 ) ---->| /dev/pts/5 |
--- +-----------------------+
--- +-----------------------+
standard error ( 2 ) ---->| /dev/pts/5 |
--- +-----------------------+
That's right, nothing has changed, 2 was already pointing to the same
place as 1. Now Bash sees `> file` and thus changes `stdout`:
--- +-----------------------+
standard input ( 0 ) ---->| /dev/pts/5 |
--- +-----------------------+
--- +-----------------------+
standard output ( 1 ) ---->| file |
--- +-----------------------+
--- +-----------------------+
standard error ( 2 ) ---->| /dev/pts/5 |
--- +-----------------------+
And that's not what we want.
- `>file 2>&1`
Now let's look at the correct `command >file 2>&1`. We start as in the
previous example, and Bash sees `> file`:
--- +-----------------------+
standard input ( 0 ) ---->| /dev/pts/5 |
--- +-----------------------+
--- +-----------------------+
standard output ( 1 ) ---->| file |
--- +-----------------------+
--- +-----------------------+
standard error ( 2 ) ---->| /dev/pts/5 |
--- +-----------------------+
Then it sees our duplication `2>&1`:
--- +-----------------------+
standard input ( 0 ) ---->| /dev/pts/5 |
--- +-----------------------+
--- +-----------------------+
standard output ( 1 ) ---->| file |
--- +-----------------------+
--- +-----------------------+
standard error ( 2 ) ---->| file |
--- +-----------------------+
And voila, both `1` and `2` are redirected to file.
## Why sed 's/foo/bar/' file \>file Doesn't Work
This is a common error, we want to modify a file using something that
reads from a file and writes the result to `stdout`. To do this, we
redirect stdout to the file we want to modify. The problem here is that,
as we have seen, the redirections are setup before the command is
actually executed.
So **BEFORE** sed starts, standard output has already been redirected,
with the additional side effect that, because we used \>, "file" gets
truncated. When `sed` starts to read the file, it contains nothing.
## exec
In Bash the `exec` built-in replaces the shell with the specified
program. So what does this have to do with redirection? `exec` also
allow us to manipulate the file descriptors. If you don't specify a
program, the redirection after `exec` modifies the file descriptors of
the current shell.
For example, all the commands after `exec 2>file` will have file
descriptors like:
--- +-----------------------+
standard input ( 0 ) ---->| /dev/pts/5 |
--- +-----------------------+
--- +-----------------------+
standard output ( 1 ) ---->| /dev/pts/5 |
--- +-----------------------+
--- +-----------------------+
standard error ( 2 ) ---->| file |
--- +-----------------------+
All the the errors sent to `stderr` by the commands after the
`exec 2>file` will go to the file, just as if you had the command in a
script and ran `myscript 2>file`.
`exec` can be used, if, for instance, you want to log the errors the
commands in your script produce, just add `exec 2>myscript.errors` at
the beginning of your script.
Let's see another use case. We want to read a file line by line, this is
easy, we just do:
while read -r line;do echo &quot;$line&quot;;done < file
Now, we want, after printing each line, to do a pause, waiting for the
user to press a key:
while read -r line;do echo &quot;$line&quot;; read -p &quot;Press any key&quot; -n 1;done < file
And, surprise this doesn't work. Why? because the shell descriptor of
the while loop looks like:
--- +-----------------------+
standard input ( 0 ) ---->| file |
--- +-----------------------+
--- +-----------------------+
standard output ( 1 ) ---->| /dev/pts/5 |
--- +-----------------------+
--- +-----------------------+
standard error ( 2 ) ---->| /dev/pts/5 |
--- +-----------------------+
and our read inherits these descriptors, and our command
(`read -p "Press any key" -n 1`) inherits them, and thus reads from file
and not from our terminal.
A quick look at `help read` tells us that we can specify a file
descriptor from which `read` should read. Cool. Now let's use `exec` to
get another descriptor:
exec 3<file
while read -u 3 line;do echo &quot;$line&quot;; read -p &quot;Press any key&quot; -n 1;done
Now the file descriptors look like:
--- +-----------------------+
standard input ( 0 ) ---->| /dev/pts/5 |
--- +-----------------------+
--- +-----------------------+
standard output ( 1 ) ---->| /dev/pts/5 |
--- +-----------------------+
--- +-----------------------+
standard error ( 2 ) ---->| /dev/pts/5 |
--- +-----------------------+
--- +-----------------------+
new descriptor ( 3 ) ---->| file |
--- +-----------------------+
and it works.
## Closing The File Descriptors
Closing a file through a file descriptor is easy, just make it a
duplicate of -. For instance, let's close `stdin <&-` and `stderr 2>&-`:
bash -c '{ lsof -a -p $$ -d0,1,2 ;} <&- 2>&-'
COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
bash 10668 pgas 1u CHR 136,2 4 /dev/pts/2
we see that inside the `{}` that only `1` is still here.
Though the OS will probably clean up the mess, it is perhaps a good idea
to close the file descriptors you open. For instance, if you open a file
descriptor with `exec 3>file`, all the commands afterwards will inherit
it. It's probably better to do something like:
exec 3>file
.....
#commands that uses 3
.....
exec 3>&-
#we don't need 3 any more
I've seen some people using this as a way to discard, say stderr, using
something like: command 2\>&-. Though it might work, I'm not sure if you
can expect all applications to behave correctly with a closed stderr.
When in doubt, I use 2\>/dev/null.
# An Example
This example comes from [this post
(ffe4c2e382034ed9)](http://groups.google.com/group/comp.unix.shell/browse_thread/thread/64206d154894a4ef/ffe4c2e382034ed9#ffe4c2e382034ed9)
on the comp.unix.shell group:
{
{
cmd1 3>&- |
cmd2 2>&3 3>&-
} 2>&1 >&4 4>&- |
cmd3 3>&- 4>&-
} 3>&2 4>&1
The redirections are processed from left to right, but as the file
descriptors are inherited we will also have to work from the outer to
the inner contexts. We will assume that we run this command in a
terminal. Let's start with the outer `{ } 3>&2 4>&1`.
--- +-------------+ --- +-------------+
( 0 ) ---->| /dev/pts/5 | ( 3 ) ---->| /dev/pts/5 |
--- +-------------+ --- +-------------+
--- +-------------+ --- +-------------+
( 1 ) ---->| /dev/pts/5 | ( 4 ) ---->| /dev/pts/5 |
--- +-------------+ --- +-------------+
--- +-------------+
( 2 ) ---->| /dev/pts/5 |
--- +-------------+
We only made 2 copies of `stderr` and `stdout`. `3>&1 4>&1` would have
produced the same result here because we ran the command in a terminal
and thus `1` and `2` go to the terminal. As an exercise, you can start
with `1` pointing to `file.stdout` and 2 pointing to `file.stderr`, you
will see why these redirections are very nice.
Let's continue with the right part of the second pipe:
`| cmd3 3>&- 4>&-`
--- +-------------+
( 0 ) ---->| 2nd pipe |
--- +-------------+
--- +-------------+
( 1 ) ---->| /dev/pts/5 |
--- +-------------+
--- +-------------+
( 2 ) ---->| /dev/pts/5 |
--- +-------------+
It inherits the previous file descriptors, closes 3 and 4 and sets up a
pipe for reading. Now for the left part of the second pipe
`{...} 2>&1 >&4 4>&- |`
--- +-------------+ --- +-------------+
( 0 ) ---->| /dev/pts/5 | ( 3 ) ---->| /dev/pts/5 |
--- +-------------+ --- +-------------+
--- +-------------+
( 1 ) ---->| /dev/pts/5 |
--- +-------------+
--- +-------------+
( 2 ) ---->| 2nd pipe |
--- +-------------+
First, The file descriptor `1` is connected to the pipe (`|`), then `2`
is made a copy of `1` and thus is made an fd to the pipe (`2>&1`), then
`1` is made a copy of `4` (`>&4`), then `4` is closed. These are the
file descriptors of the inner `{}`. Lcet's go inside and have a look at
the right part of the first pipe: `| cmd2 2>&3 3>&-`
--- +-------------+
( 0 ) ---->| 1st pipe |
--- +-------------+
--- +-------------+
( 1 ) ---->| /dev/pts/5 |
--- +-------------+
--- +-------------+
( 2 ) ---->| /dev/pts/5 |
--- +-------------+
It inherits the previous file descriptors, connects 0 to the 1st pipe,
the file descriptor 2 is made a copy of 3, and 3 is closed. Finally, for
the left part of the pipe:
--- +-------------+
( 0 ) ---->| /dev/pts/5 |
--- +-------------+
--- +-------------+
( 1 ) ---->| 1st pipe |
--- +-------------+
--- +-------------+
( 2 ) ---->| 2nd pipe |
--- +-------------+
It also inherits the file descriptor of the left part of the 2nd pipe,
file descriptor `1` is connected to the first pipe, `3` is closed.
The purpose of all this becomes clear if we take only the commands:
cmd2
--- +-------------+
-->( 0 ) ---->| 1st pipe |
/ --- +-------------+
/
/ --- +-------------+
cmd 1 / ( 1 ) ---->| /dev/pts/5 |
/ --- +-------------+
/
--- +-------------+ / --- +-------------+
( 0 ) ---->| /dev/pts/5 | / ( 2 ) ---->| /dev/pts/5 |
--- +-------------+ / --- +-------------+
/
--- +-------------+ / cmd3
( 1 ) ---->| 1st pipe | /
--- +-------------+ --- +-------------+
------------>( 0 ) ---->| 2nd pipe |
--- +-------------+ / --- +-------------+
( 2 ) ---->| 2nd pipe |/
--- +-------------+ --- +-------------+
( 1 ) ---->| /dev/pts/5 |
--- +-------------+
--- +-------------+
( 2 ) ---->| /dev/pts/5 |
--- +-------------+
As said previously, as an exercise, you can start with `1` open on a
file and `2` open on another file to see how the `stdin` from `cmd2` and
`cmd3` goes to the original `stdin` and how the `stderr` goes to the
original `stderr`.
# Syntax
I used to have trouble choosing between `0&<3` `3&>1` `3>&1` `->2`
`-<&0` `&-<0` `0<&-` etc... (I think probably because the syntax is more
representative of the result, i.e., the redirection, than what is done,
i.e., opening, closing, or duplicating file descriptors).
If this fits your situation, then maybe the following "rules" will help
you, a redirection is always like the following:
lhs op rhs
- `lhs` is always a file description, i.e., a number:
- Either we want to open, duplicate, move or we want to close. If the
op is `<` then there is an implicit 0, if it's `>` or `>>`, there is
an implicit 1.
<!-- -->
- `op` is `<`, `>`, `>>`, `>|`, or `<>`:
- `<` if the file decriptor in `lhs` will be read, `>` if it will be
written, `>>` if data is to be appended to the file, `>|` to
overwrite an existing file or `<>` if it will be both read and
written.
<!-- -->
- `rhs` is the thing that the file descriptor will describe:
- It can be the name of a file, the place where another descriptor
goes (`&1`), or, `&-`, which will close the file descriptor.
You might not like this description, and find it a bit incomplete or
inexact, but I think it really helps to easily find that, say `&->0` is
incorrect.
### A note on style
The shell is pretty loose about what it considers a valid redirect.
While opinions probably differ, this author has some (strong)
recommendations:
- **Always** keep redirections "tightly grouped" -- that is, **do not**
include whitespace anywhere within the redirection syntax except
within quotes if required on the RHS (e.g. a filename that contains a
space). Since shells fundamentally use whitespace to delimit fields in
general, it is visually much clearer for each redirection to be
separated by whitespace, but grouped in chunks that contain no
unnecessary whitespace.
<!-- -->
- **Do** always put a space between each redirection, and between the
argument list and the first redirect.
<!-- -->
- **Always** place redirections together at the very end of a command
after all arguments. Never precede a command with a redirect. Never
put a redirect in the middle of the arguments.
<!-- -->
- **Never** use the Csh `&>foo` and `>&foo` shorthand redirects. Use the
long form `>foo 2>&1`. (see: [obsolete](obsolete))
<!-- -->
# Good! This is clearly a simple commmand with two arguments and 4 redirections
cmd arg1 arg2 <myFile 3<&1 2>/dev/null >&2
# Good!
{ cmd1 <<<'my input'; cmd2; } >someFile
# Bad. Is the &quot;1&quot; a file descriptor or an argument to cmd? (answer: it's the FD). Is the space after the herestring part of the input data? (answer: No).
# The redirects are also not delimited in any obvious way.
cmd 2>& 1 <<< stuff
# Hideously Bad. It's difficult to tell where the redirects are and whether they're even valid redirects.
# This is in fact one command with one argument, an assignment, and three redirects.
foo=bar<baz bork<<< blarg>bleh
# Conclusion
I hope this tutorial worked for you.
I lied, I did not explain `1>&3-`, go check the manual ;-)
Thanks to Stéphane Chazelas from whom I stole both the intro and the
example....
The intro is inspired by this introduction, you'll find a nice exercise
there too:
- [A Detailed Introduction to I/O and I/O
Redirection](http://tldp.org/LDP/abs/html/ioredirintro.html)
The last example comes from this post:
- [comp.unix.shell: piping stdout and stderr to different
processes](http://groups.google.com/group/comp.unix.shell/browse_thread/thread/64206d154894a4ef/ffe4c2e382034ed9#ffe4c2e382034ed9)
# See also

1
howto/start.md Normal file
View File

@ -0,0 +1 @@
# HOWTO

View File

@ -0,0 +1,94 @@
The one of the simplest way to check your bash/sh scripts is run it and
check it output or run it and check the result. This tutorial shows
how-to use [bashtest](https://github.com/pahaz/bashtest) tool for
testing your scripts.
### Write simple util
We have a simple **stat.sh** script:
#!/usr/bin/env bash
if [ -z &quot;$1&quot; ]
then
DIR=./
else
DIR=$1
fi
echo &quot;Evaluate *.py statistics&quot;
FILES=$(find $DIR -name '*.py' | wc -l)
LINES=$((find $DIR -name '*.py' -print0 | xargs -0 cat) | wc -l)
echo &quot;PYTHON FILES: $FILES&quot;
echo &quot;PYTHON LINES: $LINES&quot;
This script evaluate the number of python files and the number of python
code lines in the files. We can use it like **./stat.sh \<dir\>**
### Create testsuit
Then make test suits for **stat.sh**. We make a directory **testsuit**
which contain test python files.
**testsuit/main.py**
import foo
print(foo)
**testsuit/foo.py**
BAR = 1
BUZ = BAR + 2
Ok! Our test suit is ready! We have 2 python files which contains 4
lines of code.
### Write bashtests
Lets write tests. We just write a shell command for testing our work.
Create file **tests.bashtest**:
$ ./stat.sh testsuit/
Evaluate *.py statistics
PYTHON FILES: 2
PYTHON LINES: 4
This is our test! This is simple. Try to run it.
# install bashtest if required!
$ pip install bashtest
# run tests
$ bashtest *.bashtest
1 items passed all tests:
1 tests in tests.bashtest
1 tests in 1 items.
1 passed and 0 failed.
Test passed.
Thats all. We wrote one test. You can write more tests if you want.
$ ls testsuit/
foo.py main.py
$ ./stat.sh testsuit/
Evaluate *.py statistics
PYTHON FILES: 2
PYTHON LINES: 4
And run tests again:
$ bashtest *.bashtest
1 items passed all tests:
2 tests in tests.bashtest
2 tests in 1 items.
2 passed and 0 failed.
Test passed.
You can find more **.bashtest** examples in the [bashtest github
repo](https://github.com/pahaz/bashtest). You can also write your
question or report a bug
[here](https://github.com/pahaz/bashtest/issues).
Happy testing!

509
internals/shell_options.md Normal file
View File

@ -0,0 +1,509 @@
# List of shell options
![](keywords>bash shell scripting options runtime variable behaviour)
This information was taken from a Bash version "`4.1`", every now and
then new options are added, so likely, this list isn't complete.
The shell-options can be set with the [shopt builtin
command](/commands/builtin/shopt).
## Shell options
### autocd
| Option: | `autocd` | Since: | 4.0-alpha |
|:------------|:-----------------|:---------|:----------|
| Shell mode: | interactive only | Default: | off |
If set, a command name that is the name of a directory is executed as if
it were the argument to the cd command.
### assoc_expand_once
| Option: | `assoc_expand_once` | Since: | 5.0-alpha |
|:------------|:--------------------|:---------|:----------|
| Shell mode: | all | Default: | off |
If set, Bash attempts to expand associative array options only once.
### cdable_vars
| Option: | `cdable_vars` | Since: | unknown |
|:------------|:--------------|:---------|:--------|
| Shell mode: | all | Default: | off |
Treat every **non-directory argument** to the `cd`-command as variable
name containing a directory to `cd` into.
### cdspell
| Option: | `cdspell` | Since: | unknown |
|:------------|:-----------------|:---------|:--------|
| Shell mode: | interactive only | Default: | off |
If set, minor errors in the spelling of a directory component in a cd
command will be corrected. The errors checked for are transposed
characters, a missing character, and one character too many. If a
correction is found, the corrected file name is printed, and the command
proceeds.
### checkhash
| Option: | `checkhash` | Since: | unknown |
|:------------|:------------|:---------|:--------|
| Shell mode: | all | Default: | off |
If set, Bash checks that a command found in the hash table exists before
trying to execute it. If a hashed command no longer exists, a normal
path search is performed.
### checkjobs
| Option: | `checkjobs` | Since: | 4.0-alpha |
|:------------|:-----------------|:---------|:----------|
| Shell mode: | interactive only | Default: | off |
If set, Bash lists the status of any stopped and running jobs before
exiting an interactive shell. If any jobs are running, this causes the
exit to be deferred until a second exit is attempted without an
intervening command. The shell always postpones exiting if any jobs are
stopped.
### checkwinsize
| Option: | `checkwinsize` | Since: | unknown |
|:------------|:---------------|:---------|:--------|
| Shell mode: | all | Default: | on |
If set, Bash checks the window size after each command and, if
necessary, updates the values of the variables
[LINES](/syntax/shellvars#LINES) and
[COLUMNS](/syntax/shellvars#COLUMNS).
### cmdhist
| Option: | `cmdhist` | Since: | unknown |
|:------------|:----------|:---------|:--------|
| Shell mode: | all | Default: | off |
If set, Bash attempts to save all lines of a multiple-line command in
the same history entry. This allows easy re-editing of multi-line
commands.
### compat31
| Option: | `compat31` | Since: | 3.2 |
|:------------|:-----------|:---------|:----|
| Shell mode: | all | Default: | off |
Compatiblity mode for Bash 3.1
### compat32
| Option: | `compat32` | Since: | 4.0 |
|:------------|:-----------|:---------|:----|
| Shell mode: | all | Default: | off |
Compatiblity mode for Bash 3.2
### compat40
| Option: | `compat40` | Since: | 4.1-beta |
|:------------|:-----------|:---------|:---------|
| Shell mode: | all | Default: | off |
Compatiblity mode for Bash 4.0
### compat41
| Option: | `compat41` | Since: | 4.2-alpha |
|:------------|:-----------|:---------|:----------|
| Shell mode: | all | Default: | off |
Compatiblity mode for Bash 4.1
### compat42
| Option: | `compat42` | Since: | 4.3-alpha |
|:------------|:-----------|:---------|:----------|
| Shell mode: | all | Default: | off |
Compatiblity mode for Bash 4.2
### compat43
| Option: | `compat43` | Since: | 4.4-alpha |
|:------------|:-----------|:---------|:----------|
| Shell mode: | all | Default: | off |
Compatiblity mode for Bash 4.3
### compat44
| Option: | `compat44` | Since: | 5.0-alpha |
|:------------|:-----------|:---------|:----------|
| Shell mode: | all | Default: | off |
Compatiblity mode for Bash 4.4
### direxpand
| Option: | `direxpand` | Since: | 4.3-alpha |
|:------------|:------------|:---------|:-----------------------------------------------------------------------|
| Shell mode: | all | Default: | off (unless changed on compile-time with `--enable-direxpand-default`) |
If set, bash replaces directory names with the results of word expansion
when performing filename completion. This changes the contents of the
readline editing buffer. If not set, bash attempts to preserve what the
user typed.
### dirspell
| Option: | `dirspell` | Since: | 4.0-alpha |
|:------------|:-----------|:---------|:----------|
| Shell mode: | all | Default: | off |
If set, Bash will perform spelling corrections on directory names to
match a glob.
### dotglob
| Option: | `dotglob` | Since: | unknown |
|:------------|:----------|:---------|:--------|
| Shell mode: | all | Default: | off |
If set, Bash includes filenames beginning with a `.` (dot) in the
results of [pathname expansion](/syntax/expansion/globs).
### execfail
| Option: | `execfail` | Since: | unknown |
|:------------|:----------------|:---------|:--------|
| Shell mode: | non-interactive | Default: | off |
If set, a non-interactive shell will not exit if it cannot execute the
file specified as an argument to the `exec`-builtin command. An
interactive shell does not exit if `exec` fails.
### expand_aliases
| Option: | `expand_aliases` | Since: | unknown |
|:------------|:-----------------|:---------|:----------------------------------------|
| Shell mode: | all | Default: | on (interactive), off (non-interactive) |
If set, aliases are expanded. This option is enabled by default for
interactive shells.
### extdebug
| Option: | `extdebug` | Since: | 3.0-alpha |
|:------------|:-----------|:---------|:----------|
| Shell mode: | all | Default: | off |
If set, behavior intended for use by debuggers is enabled.
### extglob
| Option: | `extglob` | Since: | 2.02-alpha1 |
|:------------|:----------|:---------|:------------|
| Shell mode: | all | Default: | off |
If set, the extended [pattern matching](/syntax/pattern) features are
enabled. See the important note below under [Parser
configurations](#parser_configurations).
### extquote
| Option: | `extquote` | Since: | 3.0-alpha (?) |
|:------------|:-----------|:---------|:--------------|
| Shell mode: | all | Default: | on |
If set, `$'string'` and `$"string"` quoting is performed within
[parameter expansions](/syntax/pe) enclosed in double quotes. See the
important note below under [Parser
configurations](#parser_configurations).
### failglob
| Option: | `failglob` | Since: | 3.0-alpha |
|:------------|:-----------|:---------|:----------|
| Shell mode: | all | Default: | off |
If set, patterns which fail to match filenames during pathname expansion
result in an error message.
### force_fignore
| Option: | `force_fignore` | Since: | 3.0-alpha |
|:------------|:----------------|:---------|:----------|
| Shell mode: | interactive | Default: | on |
If set, the suffixes specified by the
[FIGNORE](/syntax/shellvars#FIGNORE) shell variable cause words to be
ignored when performing word completion even if the ignored words are
the only possible completions. This option is enabled by default.
### globasciiranges
| Option: | `globasciiranges` | Since: | 4.3-alpha |
|:------------|:------------------|:---------|:----------------------------------|
| Shell mode: | all | Default: | on (configurable at compile time) |
If set, range expressions used in pattern matching behave as if in the
traditional C locale when performing comparisons. That is, the current
locale's collating sequence is not taken into account, so b will not
collate between A and B, and upper-case and lower-case ASCII characters
will collate together.
### globstar
| Option: | `globstar` | Since: | 4.0-alpha |
|:------------|:-----------|:---------|:----------|
| Shell mode: | all | Default: | off |
If set, recursive globbing with `**` is enabled.
### gnu_errfmt
| Option: | `gnu_errfmt` | Since: | 3.0-alpha |
|:------------|:-------------|:---------|:----------|
| Shell mode: | all | Default: | off |
If set, shell error messages are written in the "standard GNU error
message format".
### histappend
| Option: | `histappend` | Since: | unknown |
|:------------|:----------------|:---------|:--------|
| Shell mode: | interactive (?) | Default: | off |
If set, the history list is appended to the file named by the value of
the [HISTFILE](/syntax/shellvars#HISTFILE) variable when the shell
exits, rather than overwriting the file.
### histreedit
| Option: | `histreedit` | Since: | unknown |
|:------------|:----------------|:---------|:--------|
| Shell mode: | interactive (?) | Default: | off |
If set, and readline is being used, a user is given the opportunity to
re-edit a failed history substitution.
### histverify
| Option: | `histverify` | Since: | unknown |
|:------------|:----------------|:---------|:--------|
| Shell mode: | interactive (?) | Default: | off |
Allow to review a history substitution result by loading the resulting
line into the editing buffer, rather than directly executing it.
### hostcomplete
| Option: | `hostcomplete` | Since: | 2.0-alpha3 |
|:------------|:----------------|:---------|:-----------|
| Shell mode: | interactive (?) | Default: | on |
If set, Bash completion also completes hostnames. On by default.
### huponexit
| Option: | `huponexit` | Since: | 2.02-alpha1 |
|:------------|:------------------|:---------|:------------|
| Shell mode: | interactive login | Default: | off |
If set, Bash will send the `SIGHUP` signal to all jobs when an
interactive login shell exits.
### interactive_comments
| Option: | `interactive_comments` | Since: | unknown |
|:------------|:-----------------------|:---------|:--------|
| Shell mode: | interactive | Default: | on |
Allow [commenting](/scripting/basics#comments) in interactive shells, on
by default.
### lastpipe
| Option: | `lastpipe` | Since: | 4.2-alpha |
|:------------|:-----------|:---------|:----------|
| Shell mode: | all | Default: | off |
If set, **and job control is not active**, the shell runs the last
command of a pipeline not executed in the background in the current
shell environment.
### lithist
| Option: | `lithist` | Since: | unknown |
|:------------|:------------|:---------|:--------|
| Shell mode: | interactive | Default: | off |
If set, and the [\#cmdhist](#cmdhist) option is enabled, multi-line
commands are saved to the history with embedded newlines rather than
using semicolon separators where possible.
### localvar_inherit
| Option: | `localvar_inherit` | Since: | 5.0-alpha |
|:------------|:-------------------|:---------|:----------|
| Shell mode: | all | Default: | off |
If this option is set, a local variable inherits the value of a variable
with the same name at the nearest preceding scope.
### login_shell
| Option: | `login_shell` | Since: | 2.05a-alpha1 |
|:------------|:--------------|:---------|:-------------|
| Shell mode: | all | Default: | n/a |
The option is set when Bash is a login shell. This is a readonly option.
### mailwarn
| Option: | `mailwarn` | Since: | unknown |
|:------------|:----------------|:---------|:--------|
| Shell mode: | interactive (?) | Default: | off |
If set, and a file that Bash is checking for mail has been accessed
since the last time it was checked, the message "The mail in mailfile
has been read" is displayed.
### no_empty_cmd_completion
| Option: | `mailwarn` | Since: | unknown |
|:------------|:----------------|:---------|:--------|
| Shell mode: | interactive (?) | Default: | off |
If set, and readline is being used, Bash will not attempt to search the
PATH for possible completions when completion is attempted on an empty
line.
### nocaseglob
| Option: | `nocaseglob` | Since: | 2.02-alpha1 |
|:------------|:-------------|:---------|:------------|
| Shell mode: | all | Default: | off |
If set, Bash matches filenames in a case-insensitive fashion when
performing pathname expansion.
### nocasematch
| Option: | `nocasematch` | Since: | 3.1-alpha1 |
|:------------|:--------------|:---------|:-----------|
| Shell mode: | all | Default: | off |
If set, Bash matches patterns in a case-insensitive fashion when
performing matching while executing `case` or `[[` conditional commands.
### nullglob
| Option: | `nullglob` | Since: | unknown |
|:------------|:-----------|:---------|:--------|
| Shell mode: | all | Default: | off |
If set, Bash allows patterns which match no files to expand to a null
string, rather than themselves.
### progcomp
| Option: | `progcomp` | Since: | 2.04-alpha1 |
|:------------|:----------------|:---------|:------------|
| Shell mode: | interactive (?) | Default: | on |
If set, the programmable completion facilities are enabled. This option
is enabled by default.
### promptvars
| Option: | `promptvars` | Since: | unknown |
|:------------|:-------------|:---------|:--------|
| Shell mode: | interactive | Default: | on |
If set, prompt strings undergo parameter expansion, command
substitution, arithmetic expansion, and quote removal after being
expanded using the prompt special sequences. This option is enabled by
default.
### restricted_shell
| Option: | `restricted_shell` | Since: | 2.03-alpha |
|:------------|:-------------------|:---------|:-----------|
| Shell mode: | interactive (?) | Default: | off |
The option is set when Bash is a restricted shell. This is a readonly
option.
### shift_verbose
| Option: | `shift_verbose` | Since: | unknown |
|:------------|:----------------|:---------|:----------------------|
| Shell mode: | all | Default: | off, on in POSIX mode |
If set, the shift builtin prints an error message when the shift count
exceeds the number of positional parameters.
### sourcepath
| Option: | `sourcepath` | Since: | unknown |
|:------------|:-------------|:---------|:--------|
| Shell mode: | all | Default: | on |
If set, the source builtin command uses the value of PATH to find the
directory containing the file supplied as an argument. This option is
enabled by default.
### syslog_history
| Option: | `syslog_history` | Since: | 5.0-alpha |
|:------------|:-----------------|:---------|:----------|
| Shell mode: | unknown | Default: | off |
If set, the shell history is sent to syslog.
This option is undocumented and available only if the shell supports
syslog.
### xpg_echo
| Option: | `xpg_echo` | Since: | 2.04-beta1 |
|:------------|:-----------|:---------|:-----------|
| Shell mode: | all | Default: | off |
If set, the `echo`-builtin command expands backslash-escape sequences by
default (POSIX, SUS, XPG).
## Parser configurations
Parser configurations change the way the Bash parser recognizes the
syntax when parsing a line. This, of course, is impossible for a line
that already was parsed.
There are two options that influence the parsing this way:
- `extglob`
- `extquote`
Consequence: You **can't** use the new syntax (e.g. the extended
globbing syntax) and the command to enable it **in the same line**.
$ shopt -s extglob; echo !(*.txt) # this is the WRONG way!
-bash: syntax error near unexpected token `('
You have to configure the parser **before** a line with new syntax is
parsed:
$ shopt -s extglob # standalone - CORRECT way!
$ echo !(*.txt)
...
## See also
- Internal: [shopt builtin command](/commands/builtin/shopt)
- Internal: [set builtin command](/commands/builtin/set)

52
misc/bashphorisms.md Normal file
View File

@ -0,0 +1,52 @@
# The Bashphorisms
Bashphorisms are aphorisms for the IRC channel `#bash` on Freenode. Keep
in mind that this version is a snapshot, the bashphorisms are changed
here and there. Also, [another
snapshot](http://mywiki.wooledge.org/BashFAQ/064).
I think `greycat` was the first one who had the idea, but I'm not sure.
Our bashphorisms can be queried from `greybot` using `!bN`, where `N` is
the bashphorism number.
And yes, these bashphorisms reflect the daily reality in `#bash`.
| Number | Bashphorism |
|:-------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 0 | The questioner will never tell you what they are really doing the first time they ask. |
| 1 | The questioner's first description of the problem/question will be misleading. |
| 2 | The questioner will keep changing the question until it drives the helpers in the channel insane. |
| 3 | Offtopicness will continue until someone asks a bash question that falls under bashphorisms 1 and/or 2, and `greycat` gets pissed off. |
| 4 | The questioner will not read and apply the answers he is given but will instead continue to practice bashphorism \#1 and bashphorism \#2. |
| 5 | The ignorant will continually mis-educate the other noobies. |
| 6 | When given a choice of solutions, the newbie will always choose the wrong one. |
| 7 | The newbie will always find a reason to say, "It doesn't work." |
| 8 | If you don't know to whom the bashphorism's referring, it's you. |
| 9 | All examples given by the questioner will be broken, misleading, wrong, and not representative of the actual question. |
| 10 | See B1 |
| 11 | Please apply `(( % 10 ))` to the bashphorism value. |
| 12 | All logic is deniable; however, some logic will \*plonk\* you if you deny it. |
| 13 | Everyone ignores greycat when he is right. When he is wrong, it is !b1 |
| 14 | The newbie doesn't actually know what he's asking. If he did, he wouldn't need to ask. |
| 15 | The more advanced you are, the more likely you are to be overcomplicating it. |
| 16 | The more beginner you are, the more likely you are to be overcomplicating it. |
| 17 | A newbie comes to \#bash to get his script confirmed. He leaves disappointed. |
| 18 | The newbie will not accept the answer you give, no matter how right it is. |
| 19 | The newbie is a bloody loon. |
| 20 | The newbie will always have some excuse for doing it wrong. |
| 21 | When the newbie's question is ambiguous, the proper interpretation will be whichever one makes the problem the hardest to solve. |
| 22 | The newcomer will abuse the bot's factoid triggers for their own entertainment until someone gets annoyed enough to ask them to message it privately instead. |
| 23 | Everyone is a newcomer. |
| 24 | The newcomer will address greybot as if it were human. |
| 25 | The newbie won't accept any answer that uses practical or standard tools. |
| 26 | The newbie will not TELL you about this restriction until you have wasted half an hour. |
| 27 | The newbie will lie. |
| 28 | When the full horror of the newbie's true goal is revealed, the newbie will try to restate the goal to trick you into answering. Newbies are stupid. |
| 29 | It's always git. Or python virtualenv. Or docker. One of those pieces of shit. ALWAYS. |
| 30 | They won't show you the homework assignment. That would make it too easy. |
| 31 | Your teacher is a f\*\*king idiot. |
| 32 | The more horrifyingly wrong a proposed solution is, the more likely it will be used. |
| 33 | The newbie cannot explain what he is doing, or why. He will show you incomprehensible, nonworking code instead. What? You can't read his mind?! |
Please feel free to correct or extend this page whenever needed.

68
misc/readthesourceluke.md Normal file
View File

@ -0,0 +1,68 @@
Comments extracted from the bash source and therefore Copyright (C)
1987-2004 Free Software Foundation, Inc. under the terms of the GNU
General Public License etc..
from `mailcheck.c`:
``` C
/* check_mail () is useful for more than just checking mail. Since it has
the paranoids dream ability of telling you when someone has read your
mail, it can just as easily be used to tell you when someones .profile
file has been read, thus letting one know when someone else has logged
in. Pretty good, huh? */
```
From `builtins/read.def`:
``` C
/* If there are no variables, save the text of the line read to the
variable $REPLY. ksh93 strips leading and trailing IFS whitespace,
so that `read x ; echo &quot;$x&quot;' and `read ; echo &quot;$REPLY&quot;' behave the
same way, but I believe that the difference in behaviors is useful
enough to not do it. Without the bash behavior, there is no way
to read a line completely without interpretation or modification
unless you mess with $IFS (e.g., setting it to the empty string).
If you disagree, change the occurrences of `#if 0' to `#if 1' below. */
```
from `variables.c`:
``` C
/*
* 24 October 2001
*
* I'm tired of the arguing and bug reports. Bash now leaves SSH_CLIENT
* and SSH2_CLIENT alone. I'm going to rely on the shell_level check in
* isnetconn() to avoid running the startup files more often than wanted.
* That will, of course, only work if the user's login shell is bash, so
* I've made that behavior conditional on SSH_SOURCE_BASHRC being defined
* in config-top.h.
*/
```
From `shell.h`:
``` C
/* Values that can be returned by execute_command (). */
#define EXECUTION_FAILURE 1
#define EXECUTION_SUCCESS 0
/* Usage messages by builtins result in a return status of 2. */
#define EX_BADUSAGE 2
/* Special exit statuses used by the shell, internally and externally. */
#define EX_RETRYFAIL 124
#define EX_WEXPCOMSUB 125
#define EX_BINARY_FILE 126
#define EX_NOEXEC 126
#define EX_NOINPUT 126
#define EX_NOTFOUND 127
#define EX_SHERRBASE 256 /* all special error values are > this. */
#define EX_BADSYNTAX 257 /* shell syntax error */
#define EX_USAGE 258 /* syntax error in usage */
#define EX_REDIRFAIL 259 /* redirection failed */
#define EX_BADASSIGN 260 /* variable assignment error */
#define EX_EXPFAIL 261 /* word expansion failed */
```

35
misc/shell_humor.md Normal file
View File

@ -0,0 +1,35 @@
# Shell Humor
Nothing special, just my private collection of some more or less funny
shell stuff I saw during the years.
Usually Bash and/or Linux (GNU Toolset) specific.
$ %blow
-bash: fg: %blow: no such job
$ ar m god
ar: creating god
$ touch /pussy
touch: cannot touch `/pussy': Permission denied
$ mount; fsck; fsck; fsck; umount; sleep
# the lover variant
$ unzip; strip; touch; finger; grep; mount; fsck; more; yes; fsck; fsck; umount; sleep
# it's not directly funny, only because it's not obvious that this is an sed command
# for the <<<, it works only in Bash
$ sed streetlight <<< reeter
lighter
# see above for comments
$ sed statement <<< cat
cement
$ \(-
bash: (-: command not found
$ echo '[q]sa[ln0=aln256%Pln256/snlbx]sb3135071790101768542287578439snlbxq'|dc
GET A LIFE!

360
scripting/bashbehaviour.md Normal file
View File

@ -0,0 +1,360 @@
# Bash's behaviour
![](keywords>bash shell scripting startup files dotfiles modes POSIX)
FIXME incomplete
## Bash startup modes
### Login shell
As a "login shell", Bash reads and sets (executes) the user's profile
from `/etc/profile` and one of `~/.bash_profile`, `~/.bash_login`, or
`~/.profile` (in that order, using the first one that's readable!).
When a login shell exits, Bash reads and executes commands from the file
`~/.bash_logout`, if it exists.
Why an extra login shell mode? There are many actions and variable sets
that only make sense for the initial user login. That's why all UNIX(r)
shells have (should have) a "login" mode.
<u>**Methods to start Bash as a login shell:**</u>
- the first character of `argv[0]` is `-` (a hyphen): traditional
UNIX(r) shells start from the `login` binary
- Bash is started with the `-l` option
- Bash is started with the `--login` option
<u>**Methods to test for login shell mode:**</u>
- the shell option `login_shell` is set
<u>**Related switches:**</u>
- `--noprofile` disables reading of all profile files
### Interactive shell
When Bash starts as an interactive non-login shell, it reads and
executes commands from `~/.bashrc`. This file should contain, for
example, aliases, since they need to be defined in every shell as
they're not inherited from the parent shell.
The feature to have a system-wide `/etc/bash.bashrc` or a similar
system-wide rc-file is specific to vendors and distributors that ship
*their own, patched variant of Bash*. The classic way to have a
system-wide rc file is to `source /etc/bashrc` from every user's
`~/.bashrc`.
<u>**Methods to test for interactive-shell mode:**</u>
- the special parameter `$-` contains the letter `i` (lowercase I)
<u>**Related switches:**</u>
- `-i` forces the interactive mode
- `--norc` disables reading of the startup files (e.g.
`/etc/bash.bashrc` if supported) and `~/.bashrc`
- `--rcfile` defines another startup file (instead of `/etc/bash.bashrc`
and `~/.bashrc`)
### SH mode
When Bash starts in SH compatiblity mode, it tries to mimic the startup
behaviour of historical versions of `sh` as closely as possible, while
conforming to the POSIX(r) standard as well. The profile files read are
`/etc/profile` and `~/.profile`, if it's a login shell.
If it's not a login shell, the environment variable
[ENV](/syntax/shellvars#ENV) is evaluated and the resulting filename is
used as the name of the startup file.
After the startup files are read, Bash enters the [POSIX(r) compatiblity
mode (for running, not for starting!)](#posix_run_mode).
<u>**Bash starts in `sh` compatiblity mode when:**</u>
- the base filename in `argv[0]` is `sh` (:!: NB: `/bin/sh` may be
linked to `/bin/bash`, but that doesn't mean it acts like `/bin/bash`
:!:)
### POSIX mode
When Bash is started in POSIX(r) mode, it follows the POSIX(r) standard
for startup files. In this mode, **interactive shells** expand the
[ENV](/syntax/shellvars#ENV) variable and commands are read and executed
from the file whose name is the expanded value.
No other startup files are read. Hence, a non-interactive shell doesn't
read any startup files in POSIX(r) mode.
<u>**Bash starts in POSIX(r) mode when:**</u>
- the commandline option `--posix` is specified
- the environment variable
[POSIXLY_CORRECT](/syntax/shellvars#POSIXLY_CORRECT) is set
### Quick startup file reference
- Eventual system-wide rc-files are usually read when `~/.bashrc` would
be read (at least Debian GNU/Linux behaves like that)
- Regardless of the system-wide files in `/etc` which are always read,
Bash usually reads the first file found, when multiple choices are
given (for user files in `~/`)
| Mode | `/etc/profile` | `~/.bash_profile` | `~/.bash_login` | `~/.profile` | `~/.bashrc` | `${ENV}` |
|-----------------------|----------------|-------------------|-----------------|--------------|-------------|----------|
| Login shell | X | X | X | X | \- | \- |
| Interactive shell | \- | \- | \- | \- | X | \- |
| SH compatible login | X | \- | \- | X | \- | \- |
| SH compatible | \- | \- | \- | \- | \- | X |
| POSIX(r) compatiblity | \- | \- | \- | \- | \- | X |
## Bash run modes
### Normal Bash
### POSIX run mode
In POSIX(r) mode, Bash follows the POSIX(r) standard regarding behaviour
and parsing (excerpt from a Bash maintainer's document):
Starting Bash with the `--posix' command-line option or executing `set
-o posix' while Bash is running will cause Bash to conform more closely
to the POSIX standard by changing the behavior to match that specified
by POSIX in areas where the Bash default differs.
When invoked as `sh', Bash enters POSIX mode after reading the startup
files.
The following lists what's changed when Bash is in `POSIX mode':
1. When a command in the hash table no longer exists, Bash will
re-search `$PATH' to find the new location. This is also
available with `shopt -s checkhash'.
2. The message printed by the job control code and builtins when a job
exits with a non-zero status is `Done(status)'.
3. The message printed by the job control code and builtins when a job
is stopped is `Stopped(SIGNAME)', where SIGNAME is, for example,
`SIGTSTP'.
4. The `bg' builtin uses the required format to describe each job
placed in the background, which does not include an indication of
whether the job is the current or previous job.
5. Reserved words appearing in a context where reserved words are
recognized do not undergo alias expansion.
6. The POSIX `PS1' and `PS2' expansions of `!' to the history number
and `!!' to `!' are enabled, and parameter expansion is performed
on the values of `PS1' and `PS2' regardless of the setting of the
`promptvars' option.
7. The POSIX startup files are executed (`$ENV') rather than the
normal Bash files.
8. Tilde expansion is only performed on assignments preceding a
command name, rather than on all assignment statements on the line.
9. The default history file is `~/.sh_history' (this is the default
value of `$HISTFILE').
10. The output of `kill -l' prints all the signal names on a single
line, separated by spaces, without the `SIG' prefix.
11. The `kill' builtin does not accept signal names with a `SIG'
prefix.
12. Non-interactive shells exit if FILENAME in `.' FILENAME is not
found.
13. Non-interactive shells exit if a syntax error in an arithmetic
expansion results in an invalid expression.
14. Redirection operators do not perform filename expansion on the word
in the redirection unless the shell is interactive.
15. Redirection operators do not perform word splitting on the word in
the redirection.
16. Function names must be valid shell names. That is, they may not
contain characters other than letters, digits, and underscores, and
may not start with a digit. Declaring a function with an invalid
name causes a fatal syntax error in non-interactive shells.
17. POSIX special builtins are found before shell functions during
command lookup.
18. If a POSIX special builtin returns an error status, a
non-interactive shell exits. The fatal errors are those listed in
the POSIX standard, and include things like passing incorrect
options, redirection errors, variable assignment errors for
assignments preceding the command name, etc.
19. If `CDPATH' is set, the `cd' builtin will not implicitly append
the current directory to it. This means that `cd' will fail if no
valid directory name can be constructed from any of the entries in
`$CDPATH', even if the a directory with the same name as the name
given as an argument to `cd' exists in the current directory.
20. A non-interactive shell exits with an error status if a variable
assignment error occurs when no command name follows the assignment
statements. A variable assignment error occurs, for example, when
trying to assign a value to a readonly variable.
21. A non-interactive shell exits with an error status if the iteration
variable in a `for' statement or the selection variable in a
`select' statement is a readonly variable.
22. Process substitution is not available.
23. Assignment statements preceding POSIX special builtins persist in
the shell environment after the builtin completes.
24. Assignment statements preceding shell function calls persist in the
shell environment after the function returns, as if a POSIX
special builtin command had been executed.
25. The `export' and `readonly' builtin commands display their output
in the format required by POSIX.
26. The `trap' builtin displays signal names without the leading `SIG'.
27. The `trap' builtin doesn't check the first argument for a possible
signal specification and revert the signal handling to the original
disposition if it is, unless that argument consists solely of
digits and is a valid signal number. If users want to reset the
handler for a given signal to the original disposition, they
should use `-' as the first argument.
28. The `.' and `source' builtins do not search the current directory
for the filename argument if it is not found by searching `PATH'.
29. Subshells spawned to execute command substitutions inherit the
value of the `-e' option from the parent shell. When not in POSIX
mode, Bash clears the `-e' option in such subshells.
30. Alias expansion is always enabled, even in non-interactive shells.
31. When the `alias' builtin displays alias definitions, it does not
display them with a leading `alias ' unless the `-p' option is
supplied.
32. When the `set' builtin is invoked without options, it does not
display shell function names and definitions.
33. When the `set' builtin is invoked without options, it displays
variable values without quotes, unless they contain shell
metacharacters, even if the result contains nonprinting characters.
34. When the `cd' builtin is invoked in LOGICAL mode, and the pathname
constructed from `$PWD' and the directory name supplied as an
argument does not refer to an existing directory, `cd' will fail
instead of falling back to PHYSICAL mode.
35. When the `pwd' builtin is supplied the `-P' option, it resets
`$PWD' to a pathname containing no symlinks.
36. The `pwd' builtin verifies that the value it prints is the same as
the current directory, even if it is not asked to check the file
system with the `-P' option.
37. When listing the history, the `fc' builtin does not include an
indication of whether or not a history entry has been modified.
38. The default editor used by `fc' is `ed'.
39. The `type' and `command' builtins will not report a non-executable
file as having been found, though the shell will attempt to
execute such a file if it is the only so-named file found in
`$PATH'.
40. The `vi' editing mode will invoke the `vi' editor directly when
the `v' command is run, instead of checking `$FCEDIT' and
`$EDITOR'.
41. When the `xpg_echo' option is enabled, Bash does not attempt to
interpret any arguments to `echo' as options. Each argument is
displayed, after escape characters are converted.
There is other POSIX behavior that Bash does not implement by default
even when in POSIX mode. Specifically:
1. The `fc' builtin checks `$EDITOR' as a program to edit history
entries if `FCEDIT' is unset, rather than defaulting directly to
`ed'. `fc' uses `ed' if `EDITOR' is unset.
2. As noted above, Bash requires the `xpg_echo' option to be enabled
for the `echo' builtin to be fully conformant.
Bash can be configured to be POSIX-conformant by default, by specifying
the `--enable-strict-posix-default' to `configure' when building.
FIXME help me to find out what breaks in POSIX(r) mode!
<u>**The POSIX(r) mode can be switched on by:**</u>
- Bash starting as `sh` (the basename of `argv[0]` is `sh`)
- starting Bash with the commandline option `--posix`
- on startup, the environment variable
[POSIXLY_CORRECT](/syntax/shellvars#POSIXLY_CORRECT) is set
- the command `set -o posix`
<u>**Tests for the POSIX(r) mode:**</u>
- the variable [SHELLOPTS](/syntax/shellvars#SHELLOPTS) contains `posix`
in its list
### Restricted shell
In restricted mode, Bash sets up (and runs) a shell environment that's
far more controlled and limited than the standard shell mode. It acts
like normal Bash with the following restrictions:
- the `cd` command can't be used to change directories
- the variables [SHELL](/syntax/shellvars#SHELL),
[PATH](/syntax/shellvars#PATH), [ENV](/syntax/shellvars#ENV) and
[BASH_ENV](/syntax/shellvars#BASH_ENV) can't be set or unset
- command names that contain a `/` (slash) can't be called (hence you're
limited to `PATH`)
- filenames containing a `/` (slash) can't be specified as argument to
the `source` or `.` builtin command
- filenames containing a `/` (slash) can't be specified as argument to
the `-p` option of the `hash` builtin command
- function definitions are not inherited from the environment at shell
startup
- the environment variable [SHELLOPTS](/syntax/shellvars#SHELLOPTS) is
ignored at startup
- redirecting output using the `>`, `>|`, `<>`, `>&`, `&>`, and `>>`
redirection operators isn't allowed
- the `exec` builtin command can't replace the shell with another
process
- adding or deleting builtin commands with the `-f` and `-d` options to
the enable builtin command is forbidden
- using the `enable` builtin command to enable disabled shell builtins
doesn't work
- the `-p` option to the `command` builtin command doesn't work
- turning off restricted mode with `set +r` or `set +o restricted` is
(of course) forbidden
The "-r" restrictions are turned on **after** Bash has read its startup
files.
When the command that is run is a shell script, then the restrictions
are **turned off** for the (sub-)shell that runs that shell script.
<u>**The restricted shell can be switched on by:**</u>
- calling Bash as `rbash` (the basename of `argv[0]` is `rbash`)
- calling Bash with the `-r` option
- calling Bash with the `--restricted` option
<u>**Tests for restricted mode:**</u>
- the special parameter `$-` contains the letter `r` (lowercase R)
- the shell option `restricted_shell` is set and can be checked by the
`shopt` builtin command

333
scripting/bashchanges.md Normal file
View File

@ -0,0 +1,333 @@
# Bash changes
This article is an **incomplete overview** of changes to Bash over time.
Not all changes are listed, just the ones most likely to be useful for
normal scripting. The overviews are categorized by topic and ordered by
version.
A useful starting point is the [NEWS
file](https://github.com/bminor/bash/blob/master/NEWS) in bash sources.
If you have more detailed information, or historical information about
Bash versions earlier than V2, feel free to mail me, or use the
discussion below.
Status: 5.1 (alpha)
## Shell options
Note that the `shopt` builtin command first appeared in Bash 2.0.
For this topic, see also
- [shell_options](/internals/shell_options)
- [set](/commands/builtin/set)
| Feature or change description | Appeared in Bash version | See also/remarks |
|:--------------------------------|:-------------------------|:--------------------------------------------------------------------------------|
| `posix` (for `set -o`) | 1.14.0 | |
| `hostcomplete` | 2.0-alpha3 | |
| `expand_aliases` | 2.0 | |
| `huponexit` | 2.02-alpha1 | |
| `nocaseglob` | 2.02-alpha1 | |
| `extglob` | 2.02-alpha1 | together with extended globbing, KSH88 |
| `restricted_shell` | 2.03-alpha | |
| `xpg_echo` | 2.04-beta1 | |
| `progcomp` | 2.04-alpha1 | |
| `no_empty_command_completion` | 2.04 | |
| `login_shell` | 2.05a-alpha1 | |
| `nolog` (for `set -o`) | 2.05a | |
| `gnu_errfmt` | 3.0-alpha | |
| `force_fignore` | 3.0-alpha | |
| `failglob` | 3.0-alpha | |
| `extquote` | 3.0-alpha | unsure -- verify! |
| `extdebug` | 3.0-alpha | |
| `pipefail` (for `set -o`) | 3.0 | |
| `functrace` (for `set -o`) | 3.0 | |
| `errtrace` (for `set -o`) | 3.0 | |
| `nocasematch` | 3.1-alpha1 | |
| `dirspell` | 4.0-alpha | |
| `globstar` | 4.0-alpha | |
| `checkjobs` | 4.0-alpha | |
| `autocd` | 4.0-alpha | |
| `set -e` effects more intuitive | 4.0 | not directly specified by POSIX, but in consensus with POSIX WG |
| `compat40` | 4.1-beta | |
| `lastpipe` | 4.2-alpha | only works with job control disabled |
| `compat41` | 4.2-alpha | |
| `globasciiranges` | 4.3-alpha | enable "character range globbing" to always act as if in `C` locale |
| `compat42` | 4.3-alpha | |
| `compat43` | 4.4-alpha | |
| `compat44` | 5.0-alpha | |
| `localvar_inherit` | 5.0-alpha | local variables inherit preceeding scope values if they have the same name |
| `syslog_history` | 5.0-alpha | send history lines to syslog (undocumented, default off) if syslog is supported |
| `assoc_expand_once` | 5.0-alpha | expand associative array subscripts only one |
| `globasciiranges` | 5.0-beta | New default: on (default may be configured at compile time) |
| `localvar_inherit` | 5.0-beta | guard code against inheriting from an incompatible data type |
| `checkwinsize` | 5.0-beta2 | New default: on |
| `shift_verbose` | 5.0-beta2 | Default on when in POSIX mode |
### General (all/many builtins)
| Feature or change description | Appeared in Bash version | See also/remarks |
|:----------------------------------------------------------------------|:-------------------------|:----------------------------------------------------------------|
| generally return 2 on usage error | 2.0 | |
| generally accept `--` (end of options) | 2.0 | |
| (where applicable) implement a `-p` option to produce reusable output | 2.0 | `shopt` and `umask` builtins were fixed to support that in 2.02 |
### printf
For this topic, see also
- [printf](/commands/builtin/printf)
| Feature or change description | Appeared in Bash version | See also/remarks |
|:------------------------------------------------------------|:-------------------------|:-------------------------------------------------------|
| new `printf` command | 2.02-alpha1 | |
| respects `0..` and `0x..` prefixed numbers | 2.04-beta1 | consistency with arithmetic |
| POSIX(r) length specifiers `j`, `t` and `z` | 2.05a-alpha1 | ISO C99 |
| POSIX(r) flag `'` | 2.05a-alpha1 | |
| conversion `a` and `A` | 2.05a-rc1 | if provided by the underlying printf(3) |
| conversion `F` | 2.05a-rc1 | |
| conversion `n` | 2.05a-rc1 | |
| new option `-v` | 3.1-alpha1 | |
| escape sequences `\"` and `\?` | 3.0-beta1 | |
| modified option `-v` to assign to individual array elements | 4.1-alpha | |
| conversion `(...)T` | 4.2-alpha | support stftime(3) date/time format; uses current time |
| `\uNNNN` and `\UNNNNNNNN` escape sequences | 4.2-alpha | for: `printf`, `echo -e`, `$'...'` |
### Conditional expressions and test command
For this topic, see also
- [conditional_expression](/syntax/ccmd/conditional_expression)
- [classictest](/commands/classictest)
| Feature or change description | Appeared in Bash version | See also/remarks |
|:-------------------------------------------------------------------|:-------------------------|:-------------------------------------------------------------------------------------|
| `test`: `-o`, `==`, `<` and `>` | 2.0 | |
| `test`: `-N` | 2.02 | |
| `[[...]]`: new | 2.02-alpha1 | KSH93 |
| `[[...]]`: regex support (`=~`) | 3.0-alpha | |
| `[[...]]`: quotable right-hand-side of `=~` forces string matching | 3.2-alpha | for consistency with pattern matching |
| `[[...]]`: `<` and `>` operators respect locale | 4.1-alpha | for consistency, since 4.1-beta: ensure you have set compatiblity to \>4.0 (default) |
| `test`/`[`/`[[`: `-v` | 4.2-alpha | check if a variable is set |
| `test`/`[`/`[[`: `-v` | 4.2-alpha | support array syntax to check for elements |
| `test`/`[`/`[[`: `-N` accepts nanoseconds | 5.1-alpha | |
| `test`/`[`/`[[`: `-v` accepts positional parameters | 5.1-alpha | |
### Other builtins and keywords
| Builtin | Feature or change description | Appeared in Bash version | See also/remarks |
|:----------------------|:-------------------------------------------------------------------------------------------------------------------------------------------|:-------------------------|:----------------------------------------------------------------------------------------------|
| `bashbug` | new | 1.14.0 | |
| `select` | new | 1.14.0 | |
| `disown` | new | 2.0 | |
| `shopt` | new | 2.0 | [shopt](/commands/builtin/shopt) |
| `declare` | new options `-a` and `-F` | 2.0 | |
| `enable` | builtin has basic plugin support (dlopen) | 2.0 | |
| `exec` | options `-l`, `-c` and `-a` | 2.0 | |
| `read` | options `-p`, `-e` and `-a` | 2.0 | [read](/commands/builtin/read) |
| `readonly` | option `-a` | 2.0 | [arrays](/syntax/arrays) |
| `time` | new keyword | 2.0 | |
| `shopt` | `-p` (reusable output) | 2.02 | |
| `umask` | `-p` (reusable output) | 2.02 | |
| `complete` | new | 2.04-devel | for and together with support for programmable completion |
| `compgen` | new | 2.04-devel | for and together with support for programmable completion |
| `read` | options `-t`, `-n`, `-d`, `-s` | 2.04-devel | [read](/commands/builtin/read) |
| `for ((...;...;...))` | new | 2.04-devel | KSH93 |
| `set` | print shell functions in a format reusable as input | 2.05-beta1 | |
| `for` | allow an empty word list | 2.05a-alpha1 | |
| `read` | new option `-u` | 2.05b-alpha1 | [read](/commands/builtin/read) |
| `caller` | new | 3.0 | [caller](/commands/builtin/caller) |
| `coproc` | new | 4.0-alpha | |
| `declare` | new options `-l` and `-u` | 4.0-alpha | together with case-changing expansion forms |
| `case` | new action list terminators '';;& and '';& | 4.0-alpha | ksh93: only `;&`. zsh and mksh: `;|`. mksh: all 4, (`;;&` is undocumented Bash compatibility) |
| `read` | changed `-t` (fractional seconds) | 4.0-alpha | |
| `mapfile` | new | 4.0-alpha | |
| `read` | new option `-i` | 4.0-alpha | |
| `compopt` | new | 4.0-alpha | |
| `read` | modified option `-t` to test for data | 4.0-beta | |
| `read` | new option `-N` | 4.1-alpha | |
| `mapfile` | changed behaviour regarding history spamming | 4.1-alpha | |
| `declare` | new option `-g` | 4.2-alpha | |
| `mapfile` | calls the callback with an additional argument: The line (data) | 4.2-alpha | |
| `cd` | new option `-e` | 4.2-alpha | |
| `echo` | `\uNNNN` and `\UNNNNNNNN` escape sequences | 4.2-alpha | for: `printf`, `echo -e`, `$'...'` |
| `exec` | option `-a` to give a `argv[0]` string | 4.2-alpha | |
| `time` | allowed as a command by itself to display timing values of the shell and its children | 4.2-alpha | POSIX change |
| `help` | `help` now searches exact topic-strings (i.e. `help read` won't find `readonly` anymore) | 4.3-alpha | |
| `return` | accept negative values as return value (e.g. `return -1` will show as (8 bit) 255 in the caller) | 4.3-alpha | |
| `exit` | accept negative values as return value (e.g. `return -1` will show as (8 bit) 255 in the caller) | 4.3-alpha | |
| `read` | `read` skips `NUL` (ASCII Code 0) in input | 4.3-alpha | |
| `declare` | new option `-n`/`+n` to support nameref variable type | 4.3-alpha | |
| `wait` | new option `-n` to wait for the next background job to finish, returning its exit status. | 4.3-alpha | |
| `read` | `read` checks first variable argument for validity before trying to read inout | 4.3-beta | |
| `help` | attempts substring matching (as it did through bash-4.2) if exact string matching fails | 4.3-beta2 | |
| `fc` | interprets option `-0` (zero) as the current command line | 4.3-beta2 | |
| `cd` | new option `-@` to browse a file's extended attributes (on systems that support `O_XATTR`) | 4.3-rc1 | |
| `kill` | new option `-L` (upper case ell) to list signals like the normal lowercase option `-l` (compatiblity with some standalone `kill` commands) | 4.4-beta | |
| `mapfile` | new option `-d` | 4.4-alpha | |
| `wait` | new option `-f` | 5.0-alpha | |
| `history` | option `-d` allows negative numbers to index from the end of the history list | 5.0-alpha | |
| `umask` | allows modes greater than octal 777 | 5.0-alpha | |
| `times` | honors current locale settings when printing decimal points | 5.0-alpha | |
| `kill` | New options `-n SIGNUMBER` and `-s SIGNAME` | 5.0-beta2 | [kill](/commands/builtin/kill) |
| `select` | Support for an empty wordlist following `in` | 5.0-beta2 | |
| `read` | Option `-e` (use ReadLine to obtain input) now works with arbitrary file descriptors (given by `-u` option) | 5.1-alpha | |
| `trap` | `-p` option prints signals with SIG_DFL/SIG_IGN on shell start (POSIX mode) | 5.1-alpha | |
| `unset` | automatically tries to unset a function if the given name is an invalid variable name | 5.1-aplha | |
| `wait` | option `-n` now accepts a list of jobs | 5.1-alpha | |
| `wait` | new option `-p NAME` to store PID/JobID (useful when waiting for a list of jobs) | 5.1-alpha | |
| `local` | new option `-p` to print local variables in the current scope | 5.1-alpha | |
| `ulimit` | new option `-R` to get/set `RLIMIT_RTTIME` resource | 5.1-alpha | |
## Builtin variables
| Feature or change description | Appeared in Bash version | See also |
|:--------------------------------------------------|:-------------------------|:---------------------------------------------------------------------------------|
| `HISTCMD` | 1.14.0 | interactive usage |
| `PS1`, `PS2`, `PATH`, and `IFS` are unsettable | 2.0 | |
| `DIRSTACK` array variable | 2.0 | |
| `PIPESTATUS` array variable | 2.0 | |
| `BASH_VERSINFO` array variable | 2.0 | |
| `HOSTNAME` | 2.0 | |
| `SHELLOPTS` | 2.0 | |
| `MACHTYPE` | 2.0 | |
| `GLOBIGNORE` | 2.0 | |
| `HISTIGNORE` | 2.0 | |
| respect `LC_ALL` | 2.0 | |
| respect `LC_MESSAGES` | 2.0 | |
| respect `LC_CTYPE` | 2.0 | |
| respect `LC_COLLATE` | 2.0 | |
| respect `LANG` | 2.0 | |
| `GROUPS` array variable | 2.01 | |
| `GROUPS` unsettable/takes (discarded) assignments | 2.04 | |
| `FUNCNAME` | 2.04 | |
| respect `LC_NUMERIC` | 2.04 | |
| `TMOUT` | 2.05b | |
| `BASH_REMATCH` | 3.0 | together with regex support in `[[...]]` |
| `BASH_ARGC` | 3.0 | debugger support |
| `BASH_ARGV` | 3.0 | debugger support |
| `BASH_SOURCE` | 3.0 | debugger support |
| `BASH_LINENO` | 3.0 | debugger support |
| `BASH_SUBSHELL` | 3.0 | debugger support |
| `BASH_EXECUTION_STRING` | 3.0 | debugger support |
| `BASH_COMMAND` | 3.0 | debugger support |
| `HISTTIMEFORMAT` | 3.0 | |
| `COMP_WORDBREAKS` | 3.0 | |
| respect `LC_TIME` | 3.1 | |
| `BASHPID` | 4.0-alpha | Added to mksh R41. |
| `PROMPT_DIRTRIM` | 4.0 | |
| `BASH_XTRACEFD` | 4.1-alpha | |
| `BASHOPTS` | 4.1-alpha | |
| `FUNCNEST` | 4.2-alpha | |
| `HISTSIZE` | 4.3-alpha | can be set to negative values for unlimited history length |
| `HISTFILESIZE` | 4.3-alpha | can be set to negative values for unlimit history file size |
| `CHILD_MAX` | 4.3-alpha | max. number of exit status of children the shell remembers |
| `BASH_COMPAT` | 4.3-alpha | set shell compatiblity levels |
| `EPOCHSECONDS` | 5.0-alpha | expands to the time in seconds since Unix epoch |
| `EPOCHREALTIME` | 5.0-alpha | expands to the time in seconds since Unix epoch with microsecond granularity |
| `BASH_ARGV0` | 5.0-alpha | get/set `$0` |
| `PATH` | 5.0-alpha | Possibility to set a static path for use in a restricted shell (at compile time) |
| `HISTSIZE` | 5.0-beta | Default can now be set at runtime |
| `SRANDOM` | 5.1-alpha | New random generator for 32bit numbers (using various methods in the backend) |
| `ARGV0` | 5.1-alpha | Respected when set in initial shell environment, then initially used to set `$0` |
| `BASH_REMATCH` | 5.1-alpha | Not readonly anymore |
| `PROMPT_COMMANDS` | 5.1-alpha | New array variable. List of commands to be executed like `PROMPT_COMMAND` |
| `SECONDS` | 5.1-alpha | Assignment using arithmetic expressions (is nominally an integer variabnle) |
| `RANDOM` | 5.1-alpha | Assignment using arithmetic expressions (is nominally an integer variabnle) |
| `LINENO` | 5.1-alpha | Not an integer variabe |
## Quoting, expansions, substitutions and related
For this topic, see also
- [pe](/syntax/pe).
| Feature or change description | Appeared in Bash version | Remarks |
|:-----------------------------------------------------------------------------------------------------|:-------------------------|:--------------------------------------------------------------------------------------------------------------|
| Support for integer-indexed arrays | 2.0 | relevant builtins also got array support |
| `${PARAMETER//PATTERN/REPLACEMENT}` | 2.0 | |
| `${PARAMETER:OFFSET:LENGTH}` | 2.0 | |
| `${!PARAMETER}` (indirection) | 2.0 | |
| `$&quot;...&quot;` (localized strings) | 2.0 | |
| `$'...'` (ANSI-C-like strings) | 2.0 | |
| `\xNNN` in `$'...'` (and `echo -e`) | 2.02-alpha1 | |
| `$(< FILENAME)` (file content) | 2.02-alpha1 | |
| globbing (`fnmatch()`) capable of POSIX(r) character classes etc. | 2.02-alpha1 | |
| extended globbing | 2.02-alpha1 | KSH88 |
| globbing inside array mass-assignment: `ARRAY=(*.txt)` | 2.03-alpha | |
| `$'...\'...'` escaped single quote inside ANSI-C-like strings | 2.04-devel | KSH93 |
| `${!PREFIX*}` (parameter name expansion) | 2.04 | KSH93 |
| `$'...'` expands `\cx` (Control-x) | 2.05b | |
| `[:class:]` syntax for pattern matching | 2.05b | KSH93 |
| `${!ARRAY[@]}` (array index expansion) | 3.0-alpha | KSH93 |
| `{x..y}` (range brace expansion) | 3.0-alpha | |
| `$'...'` expands `\xNNN` (Hexdigits) | 3.0 | |
| `+=` operator for arrays and strings | 3.1-alpha1 | |
| `${PARAMETER//PATTERN/REPLACEMENT}` behaviour changed | 3.2-alpha | anchoring for global substitution is no longer allowed, changes the way old syntax may work |
| `${@:0:x}` includes `$0` | 4.0-alpha | |
| Support for associative arrays | 4.0-alpha | relevant builtins also got associative array support |
| case modification operators for expansions | 4.0-alpha | |
| `{0x..0y}` (zeropadding brace expansion) | 4.0-alpha | |
| numerically indexed arrays can be accessed (on expansion time) from the end using negative indexes | 4.1-alpha | |
| `\uNNNN` and `\uNNNNNNNN` in `$'...'` | 4.2-alpha | for: `printf`, `echo -e`, `$'...'` |
| `${PARAMETER:OFFSET:LENGTH}`: Negative `LENGTH` values are used as offset from the end of the string | 4.2-alpha | Substrings only for Bash and ksh93. Works also for argument expansions in zsh. ksh93 can use `${arr[n..-m]}`. |
| Word expansions like `${foo##bar}` understand indirect variable references | 4.3-beta | |
| Transformations | 4.4 | |
| Process substitution now works in POSIX mode | 5.1-alpha | |
| New transformations: `U`, `u`, `L` | 5.1-alpha | Case-transformation |
| New transformation: `K` | 5.1-alpha | Display associative arrays as key/value pairs |
## Arithmetic
For this topic, see also
- [arith_expr](/syntax/arith_expr)
- [arith](/syntax/expansion/arith)
| Feature or change description | Appeared in Bash version | Remarks |
|:-------------------------------------------|:-------------------------|:------------------------------------------|
| `((...))` | 2.0-beta2 | KSH93 |
| ternary operator | 2.0 | |
| base 64 integer constants | 2.0 | the max. base before is unknown. Anybody? |
| deprecated `$[...]` in favor of `$((...))` | 2.0 | |
| exponentiaition operator (`**`) | 2.02-alpha1 | |
| comma operator `EXPR,EXPR` | 2.04-devel | |
| pre- and postfix operators | 2.04-devel | |
## Redirection and related
For this topic, see also
- [redirection](/syntax/redirection)
| Feature or change description | Appeared in Bash version | Remarks |
|:--------------------------------------------------------------------------------------|:-------------------------|:--------|
| socket redirection (`/dev/tcp/`, `/dev/udp/`) | 2.04-devel | |
| OS/filesystem-independent support for `/dev/std(in|out|err)` and `/dev/fd/*` | 2.04 | |
| socket redirection accepts service names | 2.05 | |
| `[n]<&word-` and `[n]>&word-` FD-duplicate/closing | 2.05b-alpha1 | KSH93 |
| Here strings: `<<< WORD` | 2.05b-alpha1 | |
| `|&` (synonym for `2>&1 |`) | 4.0-alpha | |
| `&>>` (equiv. to `>>FILE 2>&1`) | 4.0-alpha | |
| `{varname}` style automatic file descriptor allocation | 4.1-alpha | ksh93 |
| `{varname[idx]}` fd allocation accepts array subscripts and special-meaning variables | 4.3-alpha | ksh93 |
## Misc
| Feature or change description | Appeared in Bash version | See also/remarks |
|:------------------------------------------------------------------------------------------------|:-------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------|
| `DEBUG` trap | 2.0 | |
| `ERR` trap | 2.05a | KSH93 |
| Support for multibyte characters: Unicode / UTF8 | 2.05b | |
| `RETURN` trap | 3.0 | ksh93 `EXIT` trap evaluates in caller scope (for `function name {`). Bash `RETURN` in same scope. |
| `command_not_found_handle` handler function | 4.0-alpha | |
| official introduction of switchable "compatiblity levels" | 4.0-alpha | `compat31` was introduced in a 3.2 version, mainly because of the incompatibilities that were introduced by the changed `=~` operator |
| `[[...]]` and `((...))` conditional commands are subject to the `ERR` trap and `set -e` feature | 4.1-alpha | |
| ACL support for file status checks | 4.1-alpha | |
| Assignment to negative array indices | 4.3-alpha | ksh93, zsh |
| `declare`/`typeset -n` | 4.3-alpha | Support for nameref variable type, a variable referencing another one by name |
| shells started to run process substitutions now run any trap set on `EXIT` | 4.3-beta | |
| process substitution does not inherit the `v` flag | 5.0-alpha | |
| `ERR` trap | 5.0-alpha | Reports more reliable line numbers |
| Variable assignment | 5.0-beta | Assignments preceeding a special builtin that chages variable attributes are not propagated back unless compatiblity mode is 44 or lower |

351
scripting/basics.md Normal file
View File

@ -0,0 +1,351 @@
# The basics of shell scripting
![](keywords>bash shell scripting basics learning tutorial)
## Script files
A shell script usually resides inside a file. The file can be
executable, but you can call a Bash script with that filename as a
parameter:
bash ./myfile
There is **no need to add a boring filename extension** like `.bash` or
`.sh`. That is a holdover from UNIX(r), where executables are not tagged
by the extension, but by **permissions** (filemode). The file name can
be any combination of legal filename characters. Adding a proper
filename extension is a convention, nothing else.
chmod +x ./myfile
If the file is executable, and you want to use it by calling only the
script name, the shebang must be included in the file.
## The Shebang
The in-file specification of the interpreter of that file, for example:
``` bash
#!/bin/bash
echo &quot;Hello world...&quot;
```
This is interpreted by the kernel [^1] of your system. In general, if a
file is executable, but not an executable (binary) program, and such a
line is present, the program specified after `#!` is started with the
scriptname and all its arguments. These two characters `#` and `!` must
be **the first two bytes** in the file!
You can follow the process by using `echo` as a fake interpreter:
#!/bin/echo
We don't need a script body here, as the file will never be interpreted
and executed by "`echo`". You can see what the Operating System does, it
calls "`/bin/echo`" with the name of the executable file and following
arguments.
$ /home/bash/bin/test testword hello
/home/bash/bin/test testword hello
The same way, with `#!/bin/bash` the shell "`/bin/bash`" is called with
the script filename as an argument. It's the same as executing
"`/bin/bash /home/bash/bin/test testword hello`"
If the interpreter can be specified with arguments and how long it can
be is system-specific (see
[\#!-magic](http://www.in-ulm.de/~mascheck/various/shebang/)). When Bash
executes a file with a \#!/bin/bash shebang, the shebang itself is
ignored, since the first character is a hashmark "#", which indicates a
comment. The shebang is for the operating system, not for the shell.
Programs that don't ignore such lines, may not work as shebang driven
interpreters.
\<WRAP center round important 60%\> <u>**Attention:**</u>When the
specified interpreter is unavailable or not executable (permissions),
you usually get a "`bad interpreter`" error message., If you get nothing
and it fails, check the shebang. Older Bash versions will respond with a
"`no such file or directory`" error for a nonexistant interpreter
specified by the shebang. \</WRAP\>
**Additional note:** When you specify `#!/bin/sh` as shebang and that's
a link to a Bash, then Bash will run in POSIX(r) mode! See:
- [Bash behaviour](/scripting/bashbehaviour).
A common method is to specify a shebang like
#!/usr/bin/env bash
...which just moves the location of the potential problem to
- the `env` utility must be located in /usr/bin/
- the needed `bash` binary must be located in `PATH`
Which one you need, or whether you think which one is good, or bad, is
up to you. There is no bulletproof portable way to specify an
interpreter. **It's a common misconception that it solves all problems.
Period.**
## The standard filedescriptors
Once Initialized, every normal UNIX(r)-program has *at least 3 open
files*:
- **stdin**: standard input
- **stdout**: standard output
- **stderr**: standard error output
Usually, they're all connected to your terminal, stdin as input file
(keyboard), stdout and stderr as output files (screen). When calling
such a program, the invoking shell can change these filedescriptor
connections away from the terminal to any other file (see redirection).
Why two different output filedescriptors? It's convention to send error
messages and warnings to stderr and only program output to stdout. This
enables the user to decide if they want to see nothing, only the data,
only the errors, or both - and where they want to see them.
When you write a script:
- always read user-input from `stdin`
- always write diagnostic/error/warning messages to `stderr`
To learn more about the standard filedescriptors, especially about
redirection and piping, see:
- [An illustrated redirection tutorial](/howto/redirection_tutorial)
## Variable names
It's good practice to use lowercase names for your variables, as shell
and system-variable names are usually all in UPPERCASE. However, you
should avoid naming your variables any of the following (incomplete
list!):
| | | | | | |
|----------------|-------------|----------------|---------------|------------------|-----------------|
| `BASH` | `BASH_ARGC` | `BASH_ARGV` | `BASH_LINENO` | `BASH_SOURCE` | `BASH_VERSINFO` |
| `BASH_VERSION` | `COLUMNS` | `DIRSTACK` | `DISPLAY` | `EDITOR` | `EUID` |
| `GROUPS` | `HISTFILE` | `HISTFILESIZE` | `HISTSIZE` | `HOME` | `HOSTNAME` |
| `IFS` | `LANG` | `LANGUAGE` | `LC_ALL` | `LINES` | `LOGNAME` |
| `LS_COLORS` | `MACHTYPE` | `MAILCHECK` | `OLDPWD` | `OPTERR` | `OPTIND` |
| `OSTYPE` | `PATH` | `PIPESTATUS` | `PPID` | `PROMPT_COMMAND` | `PS1` |
| `PS2` | `PS4` | `PS3` | `PWD` | `SHELL` | `SHELLOPTS` |
| `SHLVL` | `TERM` | `UID` | `USER` | `USERNAME` | `XAUTHORITY` |
This list is incomplete. **The safest way is to use all-lowercase
variable names.**
## Exit codes
Every program you start terminates with an exit code and reports it to
the operating system. This exit code can be utilized by Bash. You can
show it, you can act on it, you can control script flow with it. The
code is a number between 0 and 255. Values from 126 to 255 are reserved
for use by the shell directly, or for special purposes, like reporting a
termination by a signal:
- **126**: the requested command (file) was found, but can't be executed
- **127**: command (file) not found
- **128**: according to ABS it's used to report an invalid argument to
the exit builtin, but I wasn't able to verify that in the source code
of Bash (see code 255)
- **128 + N**: the shell was terminated by the signal N
- **255**: wrong argument to the exit builtin (see code 128)
The lower codes 0 to 125 are not reserved and may be used for whatever
the program likes to report. A value of 0 means **successful**
termination, a value not 0 means **unsuccessful** termination. This
behavior (== 0, != 0) is also what Bash reacts to in some flow control
statements.
An example of using the exit code of the program `grep` to check if a
specific user is present in /etc/passwd:
``` bash
if grep ^root /etc/passwd; then
echo &quot;The user root was found&quot;
else
echo &quot;The user root was not found&quot;
fi
```
A common decision making command is "`test`" or its equivalent "`[`".
But note that, when calling test with the name "`[`", the square
brackets are not part of the shell syntax, the left bracket **is** the
test command!
``` bash
if [ &quot;$mystring&quot; = &quot;Hello world&quot; ]; then
echo &quot;Yeah dude, you entered the right words...&quot;
else
echo &quot;Eeeek - go away...&quot;
fi
```
Read more about [the test command](/commands/classictest)
A common exit code check method uses the "`||`" or "`&&`" operators.
This lets you execute a command based on whether or not the previous
command completed successfully:
``` bash
grep ^root: /etc/passwd >/dev/null || echo &quot;root was not found - check the pub at the corner.&quot;
which vi && echo &quot;Your favourite editor is installed.&quot;
```
Please, when your script exits on errors, provide a "FALSE" exit code,
so others can check the script execution.
## Comments
In a larger, or complex script, it's wise to comment the code. Comments
can help with debugging or tests. Comments start with the \# character
(hashmark) and continue to the end of the line:
``` bash
#!/bin/bash
# This is a small script to say something.
echo &quot;Be liberal in what you accept, and conservative in what you send&quot; # say something
```
The first thing was already explained, it's the so-called shebang, for
the shell, **only a comment**. The second one is a comment from the
beginning of the line, the third comment starts after a valid command.
All three syntactically correct.
### Block commenting
To temporarily disable complete blocks of code you would normally have
to prefix every line of that block with a \# (hashmark) to make it a
comment. There's a little trick, using the pseudo command `:` (colon)
and input redirection. The `:` does nothing, it's a pseudo command, so
it does not care about standard input. In the following code example,
you want to test mail and logging, but not dump the database, or execute
a shutdown:
``` bash
#!/bin/bash
# Write info mails, do some tasks and bring down the system in a safe way
echo &quot;System halt requested&quot; | mail -s &quot;System halt&quot; netadmin@example.com
logger -t SYSHALT &quot;System halt requested&quot;
##### The following &quot;code block&quot; is effectively ignored
: <<&quot;SOMEWORD&quot;
/etc/init.d/mydatabase clean_stop
mydatabase_dump /var/db/db1 /mnt/fsrv0/backups/db1
logger -t SYSHALT &quot;System halt: pre-shutdown actions done, now shutting down the system&quot;
shutdown -h NOW
SOMEWORD
##### The ignored codeblock ends here
```
What happened? The `:` pseudo command was given some input by
redirection (a here-document) - the pseudo command didn't care about it,
effectively, the entire block was ignored.
The here-document-tag was quoted here **to avoid substitutions** in the
"commented" text! Check [redirection with
here-documents](/syntax/redirection#tag_heredoc) for more
## Variable scope
In Bash, the scope of user variables is generally *global*. That means,
it does **not** matter whether a variable is set in the "main program"
or in a "function", the variable is defined everywhere.
Compare the following *equivalent* code snippets:
``` bash
myvariable=test
echo $myvariable
```
``` bash
myfunction() {
myvariable=test
}
myfunction
echo $myvariable
```
In both cases, the variable `myvariable` is set and accessible from
everywhere in that script, both in functions and in the "main program".
**<u>Attention:</u>** When you set variables in a child process, for
example a *subshell*, they will be set there, but you will **never**
have access to them outside of that subshell. One way to create a
subshell is the pipe. It's all mentioned in a small article about [Bash
in the processtree](/scripting/processtree)!
### Local variables
Bash provides ways to make a variable's scope *local* to a function:
- Using the `local` keyword, or
- Using `declare` (which will *detect* when it was called from within a
function and make the variable(s) local).
``` bash
myfunc() {
local var=VALUE
# alternative, only when used INSIDE a function
declare var=VALUE
...
}
```
The *local* keyword (or declaring a variable using the `declare`
command) tags a variable to be treated *completely local and separate*
inside the function where it was declared:
``` bash
foo=external
printvalue() {
local foo=internal
echo $foo
}
# this will print &quot;external&quot;
echo $foo
# this will print &quot;internal&quot;
printvalue
# this will print - again - &quot;external&quot;
echo $foo
```
### Environment variables
The environment space is not directly related to the topic about scope,
but it's worth mentioning.
Every UNIX(r) process has a so-called *environment*. Other items, in
addition to variables, are saved there, the so-called *environment
variables*. When a child process is created (in Bash e.g. by simply
executing another program, say `ls` to list files), the whole
environment *including the environment variables* is copied to the new
process. Reading that from the other side means: **Only variables that
are part of the environment are available in the child process.**
A variable can be tagged to be part of the environment using the
`export` command:
``` bash
# create a new variable and set it:
# -> This is a normal shell variable, not an environment variable!
myvariable=&quot;Hello world.&quot;
# make the variable visible to all child processes:
# -> Make it an environment variable: &quot;export&quot; it
export myvariable
```
[^1]: under specific circumstances, also by the shell itself

371
scripting/debuggingtips.md Normal file
View File

@ -0,0 +1,371 @@
# Debugging a script
![](keywords>bash shell scripting bug debug debugging)
These few lines are not intended as a full-fledged debugging tutorial,
but as hints and comments about debugging a Bash script.
## Use a unique name for your script
Do **not** name your script `test`, for example! *Why?* `test` is the
name of a UNIX(r)-command, and <u>most likely built into your shell</u>
(it's a built-in in Bash) - so you won't be able to run a script with
the name `test` in a normal way.
**Don't laugh!** This is a classic mistake :-)
## Read the error messages
Many people come into IRC and ask something like *"Why does my script
fail? I get an error!"*. And when you ask them what the error message
is, they don't even know. Beautiful.
Reading and interpreting error messages is 50% of your job as debugger!
Error messages actually **mean** something. At the very least, they can
give you hints as to where to start debugging. **READ YOUR ERROR
MESSAGES!**
You may ask yourself why is this mentioned as debugging tip? Well,
<u>you would be surprised how many shell users ignore the text of error
messages!</u> When I find some time, I'll paste 2 or 3 IRC log-snips
here, just to show you that annoying fact.
## Use a good editor
Your choice of editor is a matter of personal preference, but one with
**Bash syntax highlighting** is highly recommended! Syntax highlighting
helps you see (you guessed it) syntax errors, such as unclosed quotes
and braces, typos, etc.
From my personal experience, I can suggest `vim` or `GNU emacs`.
## Write logfiles
For more complex scripts, it's useful to write to a log file, or to the
system log. Nobody can debug your script without knowing what actually
happened and what went wrong.
An available syslog interface is `logger` ([online
manpage](http://unixhelp.ed.ac.uk/CGI/man-cgi?logger+1)).
## Inject debugging code
Insert **echos** everywhere you can, and print to `stderr`:
echo &quot;DEBUG: current i=$i&quot; >&2
If you read input from **anywhere**, such as a file or [command
substitution](/syntax/expansion/cmdsubst), print the debug output with
literal quotes, to see leading and trailing spaces!
pid=$(< fooservice.pid)
echo &quot;DEBUG: read from file: pid=\&quot;$pid\&quot;&quot; >&2
Bash's [printf](/commands/builtin/printf) command has the `%q` format,
which is handy for verifying whether strings are what they appear to be.
foo=$(< inputfile)
printf &quot;DEBUG: foo is |%q|\n&quot; &quot;$foo&quot; >&2
# exposes whitespace (such as CRs, see below) and non-printing characters
## Use shell debug output
There are two useful debug outputs for that task (both are written to
`stderr`):
- `set -v` mode (`set -o verbose`)
- print commands to be executed to `stderr` as if they were read from
input (script file or keyboard)
- print everything **before** any ([substitution and
expansion](/syntax/expansion/intro), ...) is applied
- `set -x` mode (`set -o xtrace`)
- print everything as if it were executed, after [substitution and
expansion](/syntax/expansion/intro) is applied
- indicate the depth-level of the subshell (by default by prefixing a
`+` (plus) sign to the displayed command)
- indicate the recognized words after [word
splitting](/syntax/expansion/wordsplit) by marking them like `'x y'`
- in shell version 4.1, this debug output can be printed to a
configurable file descriptor, rather than sdtout by setting the
[BASH_XTRACEFD](/syntax/shellvars#BASH_XTRACEFD) variable.
**<u>Hint:</u>** These modes can be entered when calling Bash:
- from commandline: `bash -vx ./myscript`
- from shebang (OS dependant): `#!/bin/bash -vx`
### Simple example of how to interpret xtrace output
Here's a simple command (a string comparison using the [classic test
command](/commands/classictest)) executed while in `set -x` mode:
set -x
foo=&quot;bar baz&quot;
[ $foo = test ]
That fails. Why? Let's see the `xtrace` output:
+ '[' bar baz = test ']'
And now you see that it's ("bar" and "baz") recognized as two separate
words (which you would have realized if you READ THE ERROR MESSAGES ;)
). Let's check it...
# next try
[ &quot;$foo&quot; = test ]
`xtrace` now gives
+ '[' 'bar baz' = test ']'
^ ^
word markers!
### Making xtrace more useful
(by AnMaster)
`xtrace` output would be more useful if it contained source file and
line number. Add this assignment [PS4](/syntax/shellvars#PS4) at the
beginning of your script to enable the inclusion of that information:
export PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'
**Be sure to use single quotes here!**
The output would look like this when you trace code *outside a
function*:
+(somefile.bash:412): echo 'Hello world'
...and like this when you trace code *inside a function*:
+(somefile.bash:412): myfunc(): echo 'Hello world'
That helps a lot when the script is long, or when the main script
sources many other files.
#### Set flag variables with descriptive words
If you test variables that flag the state of options, such as with
`if [[ -n $option ]];`, consider using descriptive words rather than
short codes, such as 0, 1, Y, N, because xtrace will show
`[[ -n word ]]` rather than `[[ -n 1 ]]` when the option is set.
## Debugging commands depending on a set variable
For general debugging purposes you can also define a function and a
variable to use:
debugme() {
[[ $script_debug = 1 ]] && &quot;$@&quot; || :
# be sure to append || : or || true here or use return 0, since the return code
# of this function should always be 0 to not influence anything else with an unwanted
# &quot;false&quot; return code (for example the script's exit code if this function is used
# as the very last command in the script)
}
This function does nothing when `script_debug` is unset or empty, but it
executes the given parameters as commands when `script_debug` is set.
Use it like this:
script_debug=1
# to turn it off, set script_debug=0
debugme logger &quot;Sorting the database&quot;
database_sort
debugme logger &quot;Finished sorting the database, exit code $?&quot;
Of course this can be used to execute something other than echo during
debugging:
debugme set -x
# ... some code ...
debugme set +x
## Dry-run STDIN driven commands
Imagine you have a script that runs FTP commands using the standard FTP
client:
``` bash
ftp user@host <<FTP
cd /data
get current.log
dele current.log
FTP
```
A method to dry-run this with debug output is:
``` bash
if [[ $DRY_RUN = yes ]]; then
sed 's/^/DRY_RUN FTP: /'
else
ftp user@host
fi <<FTP
cd /data
get current.log
dele current.log
FTP
```
This can be wrapped in a shell function for more readable code.
## Common error messages
### Unexpected end of file
script.sh: line 100: syntax error: unexpected end of file
Usually indicates exactly what it says: An unexpected end of file. It's
unexpected because Bash waits for the closing of a [compound
command](/syntax/ccmd/intro):
- did you close your `do` with a `done`?
- did you close your `if` with a `fi`?
- did you close your `case` with a `esac`?
- did you close your `{` with a `}`?
- did you close your `(` with a `)`?
**<u>Note:</u>** It seems that here-documents (tested on versions
`1.14.7`, `2.05b`, `3.1.17` and `4.0`) are correctly terminated when
there is an EOF before the end-of-here-document tag (see
[redirection](/syntax/redirection)). The reason is unknown, but it seems
to be deliberate. Bash 4.0 added an extra message for this:
`` warning: here-document at line <N> delimited by end-of-file (wanted `<MARKER>') ``
### Unexpected end of file while looking for matching ...
script.sh: line 50: unexpected EOF while looking for matching `&quot;'
script.sh: line 100: syntax error: unexpected end of file
This one indicates the double-quote opened in line 50 does not have a
matching closing quote.
These *unmatched errors* occur with:
- double-quote pairs
- single-quote pairs (also `$'string'`!)
- missing a closing `}` with [parameter expansion syntax](/syntax/pe)
### Too many arguments
bash: test: too many arguments
You most likely forgot to quote a variable expansion somewhere. See the
example for `xtrace` output from above. External commands may display
such an error message though in our example, it was the **internal**
test-command that yielded the error.
### !": event not found
$ echo &quot;Hello world!&quot;
bash: !&quot;: event not found
This is not an error per se. It happens in interactive shells, when the
C-Shell-styled history expansion ("`!searchword`") is enabled. This is
the default. Disable it like this:
set +H
# or
set +o histexpand
### syntax error near unexpected token \`('
When this happens during a script **function definition** or on the
commandline, e.g.
$ foo () { echo &quot;Hello world&quot;; }
bash: syntax error near unexpected token `('
you most likely have an alias defined with the same name as the function
(here: `foo`). Alias expansion happens before the real language
interpretion, thus the alias is expanded and makes your function
definition invalid.
## The CRLF issue
### What is the CRLF issue?
There's a big difference in the way that UNIX(r) and Microsoft(r) (and
possibly others) handle the **line endings** of plain text files. The
difference lies in the use of the CR (Carriage Return) and LF (Line
Feed) characters.
- MSDOS uses: `\r\n` (ASCII `CR` \#13 `^M`, ASCII LF \#10)
- UNIX(r) uses: `\n` (ASCII `LF` \#10)
Keep in mind your script is a **plain text file**, and the `CR`
character means nothing special to UNIX(r) - it is treated like any
other character. If it's printed to your terminal, a carriage return
will effectively place the cursor at the beginning of the *current*
line. This can cause much confusion and many headaches, since lines
containing CRs are not what they appear to be when printed. In summary,
CRs are a pain.
### How did a CR end up in my file?
Some possible sources of CRs:
- a DOS/Windows text editor
- a UNIX(r) text editor that is "too smart" when determining the file
content type (and thinks "*it's a DOS text file*")
- a direct copy and paste from certain webpages (some pastebins are
known for this)
### Why do CRs hurt?
CRs can be a nuisance in various ways. They are especially bad when
present in the shebang/interpreter specified with `#!` in the very first
line of a script. Consider the following script, written with a
Windows(r) text editor (`^M` is a symbolic representation of the `CR`
carriage return character!):
#!/bin/bash^M
^M
echo &quot;Hello world&quot;^M
...
Here's what happens because of the `#!/bin/bash^M` in our shebang:
- the file `/bin/bash^M` doesn't exist (hopefully)
- So Bash prints an error message which (depending on the terminal, the
Bash version, or custom patches!) may or may not expose the problem.
- the script can't be executed
The error message can vary. If you're lucky, you'll get:
bash: ./testing.sh: /bin/bash^M: bad interpreter: No such file or directory
which alerts you to the CR. But you may also get the following:
: bad interpreter: No such file or directory
Why? Because when printed literally, the `^M` makes the cursor go back
to the beginning of the line. The whole error message is *printed*, but
you *see* only part of it!
\<note warning\> It's easy to imagine the `^M` is bad in other places
too. If you get weird and illogical messages from your script, rule out
the possibility that`^M` is involved. Find and eliminate it! \</note\>
### How can I find and eliminate them?
**To display** CRs (these are only a few examples)
- in VI/VIM: `:set list`
- with `cat(1)`: `cat -v FILE`
**To eliminate** them (only a few examples)
- blindly with `tr(1)`: `tr -d '\r' <FILE >FILE.new`
- controlled with `recode(1)`: `recode MSDOS..latin1 FILE`
- controlled with `dos2unix(1)`: `dos2unix FILE`
## See also
- [the set builtin command](/commands/builtin/set) (for `-v` and `-x`)
FIXME
- DEBUG trap

278
scripting/newbie_traps.md Normal file
View File

@ -0,0 +1,278 @@
# Beginner Mistakes
![](keywords>bash shell scripting pitfalls traps beginners)
Here are some typical traps:
## Script execution
### Your perfect Bash script executes with syntax errors
If you write Bash scripts with Bash specific syntax and features, run
them with <u>Bash</u>, and run them with Bash in <u>native mode</u>.
**Wrong**:
- no shebang
- the interpreter used depends on the OS implementation and current
shell
- **can** be run by calling bash with the script name as an argument,
e.g. `bash myscript`
- `#!/bin/sh` shebang
- depends on what `/bin/sh` actually is, for a Bash it means
compatiblity mode, **not** native mode
See also:
- [Bash startup mode: SH mode](/scripting/bashbehaviour#sh_mode)
- [Bash run mode: POSIX mode](/scripting/bashbehaviour#posix_run_mode)
### Your script named "test" doesn't execute
Give it another name. The executable `test` already exists.
In Bash it's a builtin. With other shells, it might be an executable
file. Either way, it's bad name choice!
Workaround: You can call it using the pathname:
/home/user/bin/test
## Globbing
### Brace expansion is not globbing
The following command line is not related to globbing (filename
expansion):
# YOU EXPECT
# -i1.vob -i2.vob -i3.vob ....
echo -i{*.vob,}
# YOU GET
# -i*.vob -i
**Why?** The brace expansion is simple text substitution. All possible
text formed by the prefix, the postfix and the braces themselves are
generated. In the example, these are only two: `-i*.vob` and `-i`. The
filename expansion happens **after** that, so there is a chance that
`-i*.vob` is expanded to a filename - if you have files like
`-ihello.vob`. But it definitely doesn't do what you expected.
Please see:
- [brace](/syntax/expansion/brace)
## Test-command
- `if [ $foo ] ...`
- `if [-d $dir] ...`
- ...
Please see:
- [The classic test command -
pitfalls](/commands/classictest#pitfalls_summarized)
## Variables
### Setting variables
#### The Dollar-Sign
There is no `$` (dollar-sign) when you reference the **name** of a
variable! Bash is not PHP!
# THIS IS WRONG!
$myvar=&quot;Hello world!&quot;
A variable name preceeded with a dollar-sign always means that the
variable gets **expanded**. In the example above, it might expand to
nothing (because it wasn't set), effectively resulting in...
=&quot;Hello world!&quot;
...which **definitely is wrong**!
When you need the **name** of a variable, you write **only the name**,
for example
- (as shown above) to set variables: `picture=/usr/share/images/foo.png`
- to name variables to be used by the `read` builtin command:
`read picture`
- to name variables to be unset: `unset picture`
When you need the **content** of a variable, you prefix its name with
**a dollar-sign**, like
- echo "The used picture is: \$picture"
#### Whitespace
Putting spaces on either or both sides of the equal-sign (`=`) when
assigning a value to a variable **will** fail.
# INCORRECT 1
example = Hello
# INCORRECT 2
example= Hello
# INCORRECT 3
example =Hello
The only valid form is **no spaces between the variable name and
assigned value**:
# CORRECT 1
example=Hello
# CORRECT 2
example=&quot; Hello&quot;
### Expanding (using) variables
A typical beginner's trap is quoting.
As noted above, when you want to **expand** a variable i.e. "get the
content", the variable name needs to be prefixed with a dollar-sign.
But, since Bash knows various ways to quote and does word-splitting, the
result isn't always the same.
Let's define an example variable containing text with spaces:
example=&quot;Hello world&quot;
| Used form | result | number of words |
|--------------|---------------|-----------------|
| `$example` | `Hello world` | 2 |
| `"$example"` | `Hello world` | 1 |
| `\$example` | `$example` | 1 |
| `'$example'` | `$example` | 1 |
If you use parameter expansion, you **must** use the **name** (`PATH`)
of the referenced variables/parameters. i.e. **not** (`$PATH`):
# WRONG!
echo &quot;The first character of PATH is ${$PATH:0:1}&quot;
# CORRECT
echo &quot;The first character of PATH is ${PATH:0:1}&quot;
Note that if you are using variables in [arithmetic
expressions](/syntax/arith_expr), then the bare **name** is allowed:
((a=$a+7)) # Add 7 to a
((a = a + 7)) # Add 7 to a. Identical to the previous command.
((a += 7)) # Add 7 to a. Identical to the previous command.
a=$((a+7)) # POSIX-compatible version of previous code.
Please see:
- [words](/syntax/words)
- [quoting](/syntax/quoting)
- [wordsplit](/syntax/expansion/wordsplit)
- [pe](/syntax/pe)
### Exporting
Exporting a variable means giving **newly created** (child-)processes a
copy of that variable. It does **not** copy a variable created in a
child process back to the parent process. The following example does
**not** work, since the variable `hello` is set in a child process (the
process you execute to start that script `./script.sh`):
$ cat script.sh
export hello=world
$ ./script.sh
$ echo $hello
$
Exporting is one-way. The direction is from parent process to child
process, not the reverse. The above example **will** work, when you
don't execute the script, but include ("source") it:
$ source ./script.sh
$ echo $hello
world
$
In this case, the export command is of no use.
Please see:
- [processtree](/scripting/processtree)
## Exit codes
### Reacting to exit codes
If you just want to react to an exit code, regardless of its specific
value, you **don't need** to use `$?` in a test command like this:
``` bash
grep ^root: /etc/passwd >/dev/null 2>&1
if [ $? -ne 0 ]; then
echo &quot;root was not found - check the pub at the corner&quot;
fi
```
This can be simplified to:
``` bash
if ! grep ^root: /etc/passwd >/dev/null 2>&1; then
echo &quot;root was not found - check the pub at the corner&quot;
fi
```
Or, simpler yet:
``` bash
grep ^root: /etc/passwd >/dev/null 2>&1 || echo &quot;root was not found - check the pub at the corner&quot;
```
If you need the specific value of `$?`, there's no other choice. But if
you need only a "true/false" exit indication, there's no need for `$?`.
See also:
- [Exit codes](/scripting/basics#exit_codes)
### Output vs. Return Value
It's important to remember the different ways to run a child command,
and whether you want the output, the return value, or neither.
When you want to run a command (or a pipeline) and save (or print) the
**output**, whether as a string or an array, you use Bash's `$(command)`
syntax:
$(ls -l /tmp)
newvariable=$(printf &quot;foo&quot;)
When you want to use the **return value** of a command, just use the
command, or add ( ) to run a command or pipeline in a subshell:
if grep someuser /etc/passwd ; then
# do something
fi
if ( w | grep someuser | grep sqlplus ) ; then
# someuser is logged in and running sqlplus
fi
Make sure you're using the form you intended:
# WRONG!
if $(grep ERROR /var/log/messages) ; then
# send alerts
fi
Please see:
- [intro](/syntax/ccmd/intro)
- [cmdsubst](/syntax/expansion/cmdsubst)

265
scripting/nonportable.md Normal file
View File

@ -0,0 +1,265 @@
# Portability talk
![](keywords>bash shell scripting portability POSIX portable)
The script programming language of BASH is based on the Bourne Shell
syntax, with some extensions and derivations.
If scripts need to be portable, some of the BASH-specific syntax
elements should be avoided. Others should be avoided for all scripts,
e.g. if there is a corresponding POSIX(r)-compatible syntax (see
[obsolete](/scripting/obsolete)).
Some syntax elements have a BASH-specific, and a portable[^1]) pendant.
In these cases the portable syntax should be preferred.
<table>
<thead>
<tr class="header">
<th style="text-align: center;">construct</th>
<th style="text-align: center;">portable equivalent</th>
<th style="text-align: center;">Description</th>
<th style="text-align: center;">Portability</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: center;"><code>source\ FILE</code></td>
<td style="text-align: center;"><code>. FILE</code></td>
<td style="text-align: center;">include a script file</td>
<td style="text-align: center;">Bourne shell (bash, ksh, POSIX(r), zsh,
...)</td>
</tr>
<tr class="even">
<td style="text-align: center;"><code>declare</code>\ keyword</td>
<td style="text-align: center;"><code>typeset</code> keyword</td>
<td style="text-align: center;">define local variables (or variables
with special attributes)</td>
<td style="text-align: center;">ksh, zsh, ..., <strong>not
POSIX!</strong></td>
</tr>
<tr class="odd">
<td
style="text-align: center;"><code>command\ &lt;&lt;&lt;\ WORD</code></td>
<td
style="text-align: center;"><code>command &lt;&lt;MARKER WORD MARKER</code></td>
<td style="text-align: center;">a here-string, a special form of the
here-document, avoid it in portable scripts!</td>
<td style="text-align: center;">POSIX(r)</td>
</tr>
<tr class="even">
<td style="text-align: center;"><code>export VAR=VALUE</code></td>
<td style="text-align: center;"><code>VAR=VALUE export VAR</code></td>
<td style="text-align: center;">Though POSIX(r) allows it, some shells
don't want the assignment and the exporting in one command</td>
<td style="text-align: center;">POSIX(r), zsh, ksh, ...</td>
</tr>
<tr class="odd">
<td style="text-align: center;"><code>(( MATH ))</code></td>
<td style="text-align: center;"><code>: $(( MATH ))</code></td>
<td style="text-align: center;">POSIX(r) does't define an arithmetic
compund command, many shells don't know it. Using the pseudo-command
<code>:</code> and the arithmetic expansion <code>$(( ))</code> is a
kind of workaround here. <strong>Attention:</strong> Not all shell
support assignment like <code>$(( a = 1 + 1 ))</code>! Also see below
for a probably more portable solution.</td>
<td style="text-align: center;">all POSIX(r) compatible shells</td>
</tr>
<tr class="even">
<td style="text-align: center;"><code>[[\ EXPRESSION\ ]]</code></td>
<td style="text-align: center;"><code>[ EXPRESSION ]</code><br />
or<br />
<code>test EXPRESSION</code></td>
<td style="text-align: center;">The Bashish test keyword is reserved by
POSIX(r), but not defined. Use the old fashioned way with the
<code>test</code> command. See <a href="/commands/classictest">the
classic test command</a></td>
<td style="text-align: center;">POSIX(r) and others</td>
</tr>
<tr class="odd">
<td
style="text-align: center;"><code>COMMAND\ &lt;\ &lt;(\ ...INPUTCOMMANDS...\ )</code></td>
<td
style="text-align: center;"><code>INPUTCOMMANDS\ &gt;\ TEMPFILE COMMAND\ &lt;\ TEMPFILE</code></td>
<td style="text-align: center;">Process substitution (here used with
redirection); use the old fashioned way (tempfiles)</td>
<td style="text-align: center;">POSIX(r) and others</td>
</tr>
<tr class="even">
<td style="text-align: center;"><code>((echo X);(echo Y))</code></td>
<td style="text-align: center;"><code>( (echo X); (echo Y) )</code></td>
<td style="text-align: center;">Nested subshells (separate the inner
<code>()</code> from the outer <code>()</code> by spaces, to not confuse
the shell regarding arithmetic control operators)</td>
<td style="text-align: center;">POSIX(r) and others</td>
</tr>
</tbody>
</table>
## Portability rationale
Here is some assorted portability information. Take it as a small guide
to make your scripts a bit more portable. It's not complete (it never
will be!) and it's not very detailed (e.g. you won't find information
about how which shell technically forks off which subshell). It's just
an assorted small set of portability guidelines. *-Thebonsai*
FIXME UNIX shell gurus out there, please be patient with a newbie like
me and give comments and hints instead of flames.
### Environment (exported) variables
When a new value is assigned to an **existing environment variable**,
there are two possibilities:
The *new value* is seen by subsequent programs
- without any special action (e.g. Bash)
- only after an explicit export with `export VARIABLE` (e.g. Sun's
`/bin/sh`)
Since an extra `export` doesn't hurt, the safest and most portable way
is to always (re-)export a changed variable if you want it to be seen by
subsequent processes.
### Arithmetics
Bash has a special compound command to do arithmetic without expansion.
However, POSIX has no such command. In the table at the top, there's the
`: $((MATH))` construct mentioned as possible alternative. Regarding the
exit code, a 100% equivalent construct would be:
# Bash (or others) compound command
if ((MATH)); then
...
# portable equivalent command
if [ &quot;$((MATH))&quot; -ne 0 ]; then
...
Quotes around the arithmetic expansion `$((MATH))` should not be
necessary as per POSIX, but Bash and AT&T-KSH perform word-splitting on
aritrhmetic expansions, so the most portable is *with quotes*.
### echo command
The overall problem with `echo` is, that there are 2 (maybe more)
mainstream flavours around. The only case where you may safely use an
`echo` on all systems is: Echoing non-variable arguments that don't
start with a `-` (dash) and don't contain a `\` (backslash).
Why? (list of known behaviours)
- may or may not automatically interpret backslash escpape codes in the
strings
- may or may not automatically interpret switches (like `-n`)
- may or may not ignore "end of options" tag (`--`)
- `echo -n` and `echo -e` are neither portable nor standard (**even
within the same shell**, depending on the version or environment
variables or the build options, especially KSH93 and Bash)
For these, and possibly other, reasons, POSIX (SUS) standardized the
existance of [the `printf` command](/commands/builtin/printf).
### Parameter expansions
- `${var:x:x}` is KSH93/Bash specific
- `${var/../..}` and `${var//../..}` are KSH93/Bash specific
- `var=$*` and `var=$@` are not handled the same in all shells if the
first char of IFS is not " " (space). `var="$*"` should work (except
the Bourne shell always joins the expansions with space)
### Special variables
#### PWD
[PWD](/syntax/shellvars#PWD) is POSIX but not Bourne. Most shells are
*not POSIX* in that they don't ignore the value of the `PWD` environment
variable. Workaround to fix the value of `PWD` at the start of your
script:
pwd -P > dev/null
#### RANDOM
[RANDOM](/syntax/shellvars#RANDOM) is Bash/KSH/ZSH specific variable
that will give you a random number up to 32767 (2^15-1). Among many
other available external options, you can use awk to generate a random
number. There are multiple implementations of awk and which version your
system uses will depend. Most modern systems will call 'gawk' (i.e. GNU
awk) or 'nawk'. 'oawk' (i.e. Original/Old awk) does not have the rand()
or srand() functions, so is best avoided.
# 'gawk' can produce random numbers using srand(). In this example, 10 integers between 1 and 500:
randpm=$(gawk -v min=1 -v max=500 -v nNum=10 'BEGIN { srand(systime() + PROCINFO[&quot;pid&quot;]); for (i = 0; i < nNum; ++i) {print int(min + rand() * (max - min)} }')
# 'nawk' and 'mawk' does the same, but needs a seed to be provided for its rand() function. In this example we use $(date)
randpm=$(mawk -v min=1 -v max=500 -v nNum=10 -v seed=&quot;$(date +%Y%M%d%H%M%S)&quot; 'BEGIN { srand(seed); for (i = 0; i < nNum; ++i) {print int(min + rand() * (max - min)} }')
*Yes, I'm not an `awk` expert, so please correct it, rather than
complaining about possible stupid code!*
# Well, seeing how this //is// BASH-hackers.org I kinda missed the bash way of doing the above ;-)
# print a number between 0 and 500 :-)
printf $(( 500 * RANDOM / 32767 ))
# Or print 30 random numbers between 0 and 10 ;)
X=0; while (( X++ < 30 )); do echo $(( 10 * RANDOM / 32767 )); done
#### SECONDS
[SECONDS](/syntax/shellvars#SECONDS) is KSH/ZSH/Bash specific. Avoid it.
Find another method.
### Check for a command in PATH
The [PATH](/syntax/shellvars#PATH) variable is a colon-delimited list of
directory names, so it's basically possible to run a loop and check
every `PATH` component for the command you're looking for and for
executability.
However, this method doesn't look nice. There are other ways of doing
this, using commands that are *not directly* related to this task.
#### hash
The `hash` command is used to make the shell store the full pathname of
a command in a lookup-table (to avoid re-scanning the `PATH` on every
command execution attempt). Since it has to do a `PATH` search, it can
be used for this check.
For example, to check if the command `ls` is available in a location
accessible by `PATH`:
if hash ls >/dev/null 2>&1; then
echo &quot;ls is available&quot;
fi
Somewhat of a mass-check:
for name in ls grep sed awk; do
if ! hash &quot;$name&quot; >/dev/null 2>&1; then
echo &quot;FAIL: Missing command '$name'&quot;
exit 1
fi
done
Here (bash 3), `hash` also respects builtin commands. I don't know if
this works everywhere, but it seems logical.
#### command
The `command` command is used to explicitly call an external command,
rather than a builtin with the same name. For exactly this reason, it
has to do a `PATH` search, and can be used for this check.
For example, to check if the command `sed` is available in a location
accessible by `PATH`:
if command -v sed >/dev/null 2>&1; then
echo &quot;sed is available&quot;
fi
[^1]: "portable" doesn't necessarily mean it's POSIX, it can also mean
it's "widely used and accepted", and thus maybe more portable than
POSIX(r

0
scripting/obsolete.md Normal file
View File

396
scripting/posparams.md Normal file
View File

@ -0,0 +1,396 @@
# Handling positional parameters
![](keywords>bash shell scripting arguments positional parameters options)
## Intro
The day will come when you want to give arguments to your scripts. These
arguments are known as **positional parameters**. Some relevant special
parameters are described below:
| Parameter(s) | Description |
|------------------|---------------------------------------------------------------------------------------------------------------------------------|
| `$0` | the first positional parameter, equivalent to `argv[0]` in C, see [the first argument](/scripting/posparams#the_first_argument) |
| `$FUNCNAME` | the function name (<u>**attention**</u>: inside a function, `$0` is still the `$0` of the shell, **not** the function name) |
| `$1 ... $9` | the argument list elements from 1 to 9 |
| `${10} ... ${N}` | the argument list elements beyond 9 (note the [parameter expansion](/syntax/pe) syntax!) |
| `$*` | all positional parameters except `$0`, see [mass usage](/scripting/posparams#mass_usage) |
| `$@` | all positional parameters except `$0`, see [mass usage](/scripting/posparams#mass_usage) |
| `$#` | the number of arguments, not counting `$0` |
These positional parameters reflect exactly what was given to the script
when it was called.
Option-switch parsing (e.g. `-h` for displaying help) is not performed
at this point.
See also [the dictionary entry for "parameter"](/dict/terms/parameter).
## The first argument
The very first argument you can access is referenced as `$0`. It is
usually set to the script's name exactly as called, and it's set on
shell initialization:
<u>Testscript</u> - it just echos `$0`:
#!/bin/bash
echo &quot;$0&quot;
You see, `$0` is always set to the name the script is called with (`>`
is the prompt...):
> ./testscript
./testscript
> /usr/bin/testscript
/usr/bin/testscript
However, this isn't true for login shells:
> echo &quot;$0&quot;
-bash
In other terms, `$0` is not a positional parameter, it's a special
parameter independent from the positional parameter list. It can be set
to anything. In the **ideal** case it's the pathname of the script, but
since this gets set on invocation, the invoking program can easily
influence it (the `login` program does that for login shells, by
prefixing a dash, for example).
Inside a function, `$0` still behaves as described above. To get the
function name, use `$FUNCNAME`.
## Shifting
The builtin command `shift` is used to change the positional parameter
values:
- `$1` will be discarded
- `$2` will become `$1`
- `$3` will become `$2`
- ...
- in general: `$N` will become `$N-1`
The command can take a number as argument: Number of positions to shift.
e.g. `shift 4` shifts `$5` to `$1`.
## Using them
Enough theory, you want to access your script-arguments. Well, here we
go.
### One by one
One way is to access specific parameters:
#!/bin/bash
echo &quot;Total number of arguments: $#&quot;
echo &quot;Argument 1: $1&quot;
echo &quot;Argument 2: $2&quot;
echo &quot;Argument 3: $3&quot;
echo &quot;Argument 4: $4&quot;
echo &quot;Argument 5: $5&quot;
While useful in another situation, this way is lacks flexibility. The
maximum number of arguments is a fixedvalue - which is a bad idea if you
write a script that takes many filenames as arguments.
=\> forget that one
### Loops
There are several ways to loop through the positional parameters.
------------------------------------------------------------------------
You can code a [C-style for-loop](/syntax/ccmd/c_for) using `$#` as the
end value. On every iteration, the `shift`-command is used to shift the
argument list:
numargs=$#
for ((i=1 ; i <= numargs ; i++))
do
echo &quot;$1&quot;
shift
done
Not very stylish, but usable. The `numargs` variable is used to store
the initial value of `$#` because the shift command will change it as
the script runs.
------------------------------------------------------------------------
Another way to iterate one argument at a time is the `for` loop without
a given wordlist. The loop uses the positional parameters as a wordlist:
for arg
do
echo &quot;$arg&quot;
done
<u>Advantage:</u> The positional parameters will be preserved
------------------------------------------------------------------------
The next method is similar to the first example (the `for` loop), but it
doesn't test for reaching `$#`. It shifts and checks if `$1` still
expands to something, using the [test command](/commands/classictest):
while [ &quot;$1&quot; ]
do
echo &quot;$1&quot;
shift
done
Looks nice, but has the disadvantage of stopping when `$1` is empty
(null-string). Let's modify it to run as long as `$1` is defined (but
may be null), using [parameter expansion for an alternate
value](/syntax/pe#use_an_alternate_value):
while [ &quot;${1+defined}&quot; ]; do
echo &quot;$1&quot;
shift
done
### Getopts
There is a [small tutorial dedicated to
`getopts`](/howto/getopts_tutorial) (*under construction*).
## Mass usage
### All Positional Parameters
Sometimes it's necessary to just "relay" or "pass" given arguments to
another program. It's very inefficient to do that in one of these loops,
as you will destroy integrity, most likely (spaces!).
The shell developers created `$*` and `$@` for this purpose.
As overview:
| Syntax | Effective result |
|:-------|:----------------------------|
| `$*` | `$1 $2 $3 ... ${N}` |
| `$@` | `$1 $2 $3 ... ${N}` |
| `"$*"` | `"$1c$2c$3c...c${N}"` |
| `"$@"` | `"$1" "$2" "$3" ... "${N}"` |
Without being quoted (double quotes), both have the same effect: All
positional parameters from `$1` to the last one used are expanded
without any special handling.
When the `$*` special parameter is double quoted, it expands to the
equivalent of: `"$1c$2c$3c$4c........$N"`, where 'c' is the first
character of `IFS`.
But when the `$@` special parameter is used inside double quotes, it
expands to the equivanent of...
`"$1" "$2" "$3" "$4" ..... "$N"`
...which **reflects all positional parameters as they were set
initially** and passed to the script or function. If you want to re-use
your positional parameters to **call another program** (for example in a
wrapper-script), then this is the choice for you, use double quoted
`"$@"`.
Well, let's just say: **You almost always want a quoted `"$@"`!**
### Range Of Positional Parameters
Another way to mass expand the positional parameters is similar to what
is possible for a range of characters using [substring
expansion](/syntax/pe#substring_expansion) on normal parameters and the
mass expansion range of [arrays](/syntax/arrays).
`${@:START:COUNT}`
`${*:START:COUNT}`
`"${@:START:COUNT}"`
`"${*:START:COUNT}"`
The rules for using `@` or `*` and quoting are the same as above. This
will expand `COUNT` number of positional parameters beginning at
`START`. `COUNT` can be omitted (`${@:START}`), in which case, all
positional parameters beginning at `START` are expanded.
If `START` is negative, the positional parameters are numbered in
reverse starting with the last one.
`COUNT` may not be negative, i.e. the element count may not be
decremented.
<u>**Example:**</u> START at the last positional parameter:
echo &quot;${@: -1}&quot;
<u>**Attention**</u>: As of Bash 4, a `START` of `0` includes the
special parameter `$0`, i.e. the shell name or whatever \$0 is set to,
when the positional parameters are in use. A `START` of `1` begins at
`$1`. In Bash 3 and older, both `0` and `1` began at `$1`.
## Setting Positional Parameters
Setting positional parameters with command line arguments, is not the
only way to set them. The [builtin command, set](/commands/builtin/set)
may be used to "artificially" change the positional parameters from
inside the script or function:
set &quot;This is&quot; my new &quot;set of&quot; positional parameters
# RESULTS IN
# $1: This is
# $2: my
# $3: new
# $4: set of
# $5: positional
# $6: parameters
It's wise to signal "end of options" when setting positional parameters
this way. If not, the dashes might be interpreted as an option switch by
`set` itself:
# both ways work, but behave differently. See the article about the set command!
set -- ...
set - ...
Alternately this will also preserve any verbose (-v) or tracing (-x)
flags, which may otherwise be reset by `set`
set -$- ...
FIXME continue
## Production examples
### Using a while loop
To make your program accept options as standard command syntax:
`COMMAND [options] <params>` \# Like 'cat -A file.txt'
See simple option parsing code below. It's not that flexible. It doesn't
auto-interpret combined options (-fu USER) but it works and is a good
rudimentary way to parse your arguments.
#!/bin/sh
# Keeping options in alphabetical order makes it easy to add more.
while :
do
case &quot;$1&quot; in
-f | --file)
file=&quot;$2&quot; # You may want to check validity of $2
shift 2
;;
-h | --help)
display_help # Call your function
# no shifting needed here, we're done.
exit 0
;;
-u | --user)
username=&quot;$2&quot; # You may want to check validity of $2
shift 2
;;
-v | --verbose)
# It's better to assign a string, than a number like &quot;verbose=1&quot;
# because if you're debugging the script with &quot;bash -x&quot; code like this:
#
# if [ &quot;$verbose&quot; ] ...
#
# You will see:
#
# if [ &quot;verbose&quot; ] ...
#
# Instead of cryptic
#
# if [ &quot;1&quot; ] ...
#
verbose=&quot;verbose&quot;
shift
;;
--) # End of all options
shift
break;
-*)
echo &quot;Error: Unknown option: $1&quot; >&2
exit 1
;;
*) # No more options
break
;;
esac
done
# End of file
### Filter unwanted options with a wrapper script
This simple wrapper enables filtering unwanted options (here: `-a` and
`--all` for `ls`) out of the command line. It reads the positional
parameters and builds a filtered array consisting of them, then calls
`ls` with the new option set. It also respects the `--` as "end of
options" for `ls` and doesn't change anything after it:
#!/bin/bash
# simple ls(1) wrapper that doesn't allow the -a option
options=() # the buffer array for the parameters
eoo=0 # end of options reached
while [[ $1 ]]
do
if ! ((eoo)); then
case &quot;$1&quot; in
-a)
shift
;;
--all)
shift
;;
-[^-]*a*|-a?*)
options+=(&quot;${1//a}&quot;)
shift
;;
--)
eoo=1
options+=(&quot;$1&quot;)
shift
;;
*)
options+=(&quot;$1&quot;)
shift
;;
esac
else
options+=(&quot;$1&quot;)
# Another (worse) way of doing the same thing:
# options=(&quot;${options[@]}&quot; &quot;$1&quot;)
shift
fi
done
/bin/ls &quot;${options[@]}&quot;
### Using getopts
There is a [small tutorial dedicated to
`getopts`](/howto/getopts_tutorial) (*under construction*).
## See also
- Internal: [getopts_tutorial](/howto/getopts_tutorial)
- Internal: [while_loop](/syntax/ccmd/while_loop)
- Internal: [c_for](/syntax/ccmd/c_for)
- Internal: [arrays](/syntax/arrays) (for equivalent syntax for
mass-expansion)
- Internal: [Substring expansion on a
parameter](/syntax/pe#substring_expansion) (for equivalent syntax for
mass-expansion)
- Dictionary, internal: [parameter](/dict/terms/parameter)

175
scripting/processtree.md Normal file
View File

@ -0,0 +1,175 @@
# Bash and the process tree
![](keywords>bash shell scripting processes pipes variables environment)
## The process tree
The processes in UNIX(r) are - unlike other systems - **organized as a
tree**. Every process has a parent process that started, or is
responsible, for it. Every process has its own **context memory** (Not
the memory where the process stores its data, rather, the memory where
data is stored that doesn't directly belong to the process, but is
needed to run the process) i.e. <u>**The environment**</u>.
Every process has its **own** environment space.
The environment stores, among other things, data that's useful to us,
the **environment variables**. These are strings in common `NAME=VALUE`
form, but they are not related to shell variables. A variable named
`LANG`, for example, is used by every program that looks it up in its
environment to determinate the current locale.
**<u>Attention:</u>** A variable that is set, like with `MYVAR=Hello`,
is **not** automatically part of the environment. You need to put it
into the environment with the bash builtin command `export`:
export MYVAR
Common system variables like [PATH](/syntax/shellvars#PATH) or
[HOME](/syntax/shellvars#HOME) are usually part of the environment (as
set by login scripts or programs).
## Executing programs
All the diagrams of the process tree use names like "`xterm`" or
"`bash`", but that's just to make it easier to understand what's going
on, it doesn't mean those processes are actually executed.
Let's take a short look at what happens when you "execute a program"
from the Bash prompt, a program like "ls":
$ ls
Bash will now perform **two steps**:
- It will make a copy of itself
- The copy will replace itself with the "ls" program
The copy of Bash will inherit the environment from the "main Bash"
process: All environment variables will also be copied to the new
process. This step is called **forking**.
For a short moment, you have a process tree that might look like this...
xterm ----- bash ----- bash(copy)
...and after the "second Bash" (the copy) replaces itself with the `ls`
program (the copy execs it), it might look like
xterm ----- bash ----- ls
If everything was okay, the two steps resulted in one program being run.
The copy of the environment from the first step (forking) becomes the
environment for the final running program (in this case, `ls`).
<u>**What is so important about it?**</u> In our example, what the
program `ls` does inside its own environment, it can't affect the
environment of its parent process (in this case, `bash`). The
environment was copied when ls was executed. Nothing is "copied back" to
the parent environment when `ls` terminates.
## Bash playing with pipes
Pipes are a very powerful tool. You can connect the output of one
process to the input of another process. We won't delve into piping at
this point, we just want to see how it looks in the process tree. Again,
we execute some commands, this time, we'll run `ls` and `grep`:
$ ls | grep myfile
It results in a tree like this:
+-- ls
xterm ----- bash --|
+-- grep
Note once again, `ls` can't influence the `grep` environment, `grep`
can't influence the `ls` environment, and neither `grep` nor `ls` can
influence the `bash` environment.
<u>**How is that related to shell programming?!?**</u>
Well, imagine some Bash code that reads data from a pipe. For example,
the internal command `read`, which reads data from *stdin* and puts it
into a variable. We run it in a loop here to count input lines:
counter=0
cat /etc/passwd | while read; do ((counter++)); done
echo &quot;Lines: $counter&quot;
What? It's 0? Yes! The number of lines might not be 0, but the variable
`$counter` still is 0. Why? Remember the diagram from above? Rewriting
it a bit, we have:
+-- cat /etc/passwd
xterm ----- bash --|
+-- bash (while read; do ((counter++)); done)
See the relationship? The forked Bash process will count the lines like
a charm. It will also set the variable `counter` as directed. But if
everything ends, this extra process will be terminated - **your
"counter" variable is gone.** You see a 0 because in the main shell it
was 0, and wasn't changed by the child process!
<u>**So, how do we count the lines?**</u> Easy: **Avoid the subshell.**
The details don't matter, the important thing is the shell that sets the
counter must be the "main shell". For example:
counter=0
while read; do ((counter++)); done </etc/passwd
echo &quot;Lines: $counter&quot;
It's nearly self-explanatory. The `while` loop runs in the **current
shell**, the counter is incremented in the **current shell**, everything
vital happens in the **current shell**, also the `read` command sets the
variable `REPLY` (the default if nothing is given), though we don't use
it here.
## Actions that create a subshell
Bash creates **subshells** or **subprocesses** on various actions it
performs:
### Executing commands
As shown above, Bash will create subprocesses everytime it executes
commands. That's nothing new.
But if your command is a subprocess that sets variables you want to use
in your main script, that won't work.
For exactly this purpose, there's the `source` command (also: the *dot*
`.` command). Source doesn't execute the script, it imports the other
script's code into the current shell:
source ./myvariables.sh
# equivalent to:
. ./myvariables.sh
### Pipes
The last big section was about pipes, so no example here.
### Explicit subshell
If you group commands by enclosing them in parentheses, these commands
are run inside a subshell:
(echo PASSWD follows; cat /etc/passwd; echo GROUP follows; cat /etc/group) >output.txt
### Command substitution
With [command substitution](/syntax/expansion/cmdsubst) you re-use the
output of another command as text in your command line, for example to
set a variable. The other command is run in a subshell:
number_of_users=$(cat /etc/passwd | wc -l)
Note that, in this example, a second subshell was created by using a
pipe in the command substitution:
+-- cat /etc/passwd
xterm ----- bash ----- bash (cmd. subst.) --|
+-- wc -l

404
scripting/style.md Normal file
View File

@ -0,0 +1,404 @@
# Scripting with style
FIXME continue
These are some coding guidelines that helped me to read and understand
my own code over the years. They also will help to produce code that
will be a bit more robust than "if something breaks, I know how to fix
it".
This is not a bible, of course. But I have seen so much ugly and
terrible code (not only in shell) during all the years, that I'm 100%
convinced there needs to be *some* code layout and style. No matter
which one you use, use it throughout your code (at least don't change it
within the same shellscript file); don't change your code layout with
your mood.
Some good code layout helps you to read your own code after a while. And
of course it helps others to read the code.
## Indentation guidelines
Indentation is nothing that technically influences a script, it's only
for us humans.
I'm used to seeing/using indentation of *two space characters* (though
many may prefer 4 spaces, see below in the discussion section):
- it's easy and fast to type
- it's not a hard-tab that's displayed differently in different
environments
- it's wide enough to give a visual break and small enough to not waste
too much space on the line
Speaking of hard-tabs: Avoid them if possible. They only make trouble. I
can imagine one case where they're useful: Indenting
[here-documents](/syntax/redirection#here_documents).
### Breaking up lines
Whenever you need to break lines of long code, you should follow one of
these two rules:
<u>**Indention using command width:**</u>
activate some_very_long_option \
some_other_option
<u>**Indention using two spaces:**</u>
activate some_very_long_option \
some_other_option
Personally, with some exceptions, I prefer the first form because it
supports the visual impression of "these belong together".
### Breaking compound commands
[Compound commands](/syntax/ccmd/intro) form the structures that make a
shell script different from a stupid enumeration of commands. Usually
they contain a kind of "head" and a "body" that contains command lists.
This type of compound command is relatively easy to indent.
I'm used to (not all points apply to all compound commands, just pick
the basic idea):
- put the introducing keyword and the initial command list or parameters
on one line ("head")
- put the "body-introducing" keyword on the same line
- the command list of the "body" on separate lines, indented by two
spaces
- put the closing keyword on a separated line, indented like the initial
introducing keyword
What?! Well, here again:
##### Symbolic
HEAD_KEYWORD parameters; BODY_BEGIN
BODY_COMMANDS
BODY_END
##### if/then/elif/else
This construct is a bit special, because it has keywords (`elif`,
`else`) "in the middle". The visually appealing way is to indent them
like this:
if ...; then
...
elif ...; then
...
else
...
fi
##### for
for f in /etc/*; do
...
done
##### while/until
while [[ $answer != [YyNn] ]]; do
...
done
##### The case construct
The `case` construct might need a bit more discussion here, since its
structure is a bit more complex.
In general, every new "layer" gets a new indentation level:
case $input in
hello)
echo &quot;You said hello&quot;
;;
bye)
echo &quot;You said bye&quot;
if foo; then
bar
fi
;;
*)
echo &quot;You said something weird...&quot;
;;
esac
Some notes:
- if not 100% needed, the optional left parenthesis on the pattern is
not used
- the patterns (`hello)`) and the corresponding action terminator (`;;`)
are indented at the same level
- the action command lists are indented one more level (and continue to
have their own indentation, if needed)
- though optional, the very last action terminator is given
## Syntax and coding guidelines
### Cryptic constructs
Cryptic constructs, we all know them, we all love them. If they are not
100% needed, avoid them, since nobody except you may be able to decipher
them.
It's - just like in C - the middle ground between smart, efficient and
readable.
If you need to use a cryptic construct, include a comment that explains
what your "monster" does.
### Variable names
Since all reserved variables are `UPPERCASE`, the safest way is to only
use `lowercase` variable names. This is true for reading user input,
loop counting variables, etc., ... (in the example: `file`)
- prefer `lowercase` variables
- if you use `UPPERCASE` names, **do not use reserved variable names**
(see
[SUS](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08)
for an incomplete list)
- if you use `UPPERCASE` names, prepend the name with a unique prefix
(`MY_` in the example below)
<!-- -->
#!/bin/bash
# the prefix 'MY_'
MY_LOG_DIRECTORY=/var/adm/
for file in &quot;$MY_LOG_DIRECTORY&quot;/*; do
echo &quot;Found Logfile: $file&quot;
done
### Variable initialization
As in C, it's always a good idea to initialize your variables, though,
the shell will initialize fresh variables itself (better: Unset
variables will generally behave like variables containing a null
string).
It's no problem to pass an **environment variable** to the script. If
you blindly assume that all variables you use for the first time are
**empty**, anybody can **inject** content into a variable by passing it
via the environment.
The solution is simple and effective: **Initialize them**
my_input=&quot;&quot;
my_array=()
my_number=0
If you do that for every variable you use, then you also have some
in-code documentation for them.
### Parameter expansion
Unless you are really sure what you're doing, **quote every parameter
expansion**.
There are some cases where this isn't needed from a technical point of
view, e.g.
- inside `[[ ... ]]` (other than the RHS of the `==`, `!=`, and `=~`
operators)
- the parameter (`WORD`) in `case $WORD in ....`
- variable assignment: `VAR=$WORD`
But quoting these is never a mistake. If you quote every parameter
expansion, you'll be safe.
If you need to parse a parameter as a list of words, you can't quote, of
course, e.g.
list=&quot;one two three&quot;
# you MUST NOT quote $list here
for word in $list; do
...
done
### Function names
Function names should be all `lowercase` and meaningful. The function
names should be human readable. A function named `f1` may be easy and
quick to write down, but for debugging and especially for other people,
it reveals nothing. Good names help document your code without using
extra comments.
**do not use command names for your functions**. e.g. naming a script or
function `test`, will collide with the UNIX `test` command.
Unless absolutely necessary, only use alphanumeric characters and the
underscore for function names. `/bin/ls` is a valid function name in
Bash, but is not a good idea.
### Command substitution
As noted in [the article about command
substitution](/syntax/expansion/cmdsubst), you should use the `$( ... )`
form.
If portability is a concern, use the backquoted form `` ` ... ` ``.
In any case, if other expansions and word splitting are not wanted, you
should quote the command substitution!
### Eval
Well, like Greg says: **"If eval is the answer, surely you are asking
the wrong question."**
Avoid it, unless absolutely neccesary:
- `eval` can be your neckshot
- there are most likely other ways to achieve what you want
- if possible, re-think the way your script works, if it seems you can't
avoid `eval` with your current method
- if you really, really, have to use it, then take care, and be sure
about what you're doing
## Basic structure
The basic structure of a script simply reads:
#!SHEBANG
CONFIGURATION_VARIABLES
FUNCTION_DEFINITIONS
MAIN_CODE
### The shebang
If possible (I know it's not always possible!), use [a
shebang](/dict/terms/shebang).
Be careful with `/bin/sh`: The argument that "on Linux `/bin/sh` is
Bash" **is a lie** (and technically irrelevant)
The shebang serves two purposes for me:
- it specifies the interpreter to be used when the script file is called
directly: If you code for Bash, specify `bash`!
- it documents the desired interpreter (so: use `bash` when you write a
Bash-script, use `sh` when you write a general Bourne/POSIX script,
...)
### Configuration variables
I call variables that are meant to be changed by the user "configuration
variables" here.
Make them easy to find (directly at the top of the script), give them
meaningful names and maybe a short comment. As noted above, use
`UPPERCASE` for them only when you're sure about what you're doing.
`lowercase` will be the safest.
### Function definitions
Unless there are reasons not to, all function definitions should be
declared before the main script code runs. This gives a far better
overview and ensures that all function names are known before they are
used.
Since a function isn't parsed before it is executed, you usually don't
have to ensure they're in a specific order.
The portable form of the function definition should be used, without the
`function` keyword (here using the [grouping compound
command](/syntax/ccmd/grouping_plain)):
getargs() {
...
}
Speaking about the command grouping in function definitions using
`{ ...; }`: If you don't have a good reason to use another compound
command directly, you should always use this one.
## Behaviour and robustness
### Fail early
**Fail early**, this sounds bad, but usually is good. Failing early
means to error out as early as possible when checks indicate an error or
unmet condition. Failing early means to error out **before** your script
begins its work in a potentially broken state.
### Availability of commands
If you use external commands that may not be present on the path, or not
installed, check for their availability, then tell the user they're
missing.
Example:
my_needed_commands=&quot;sed awk lsof who&quot;
missing_counter=0
for needed_command in $my_needed_commands; do
if ! hash &quot;$needed_command&quot; >/dev/null 2>&1; then
printf &quot;Command not found in PATH: %s\n&quot; &quot;$needed_command&quot; >&2
((missing_counter++))
fi
done
if ((missing_counter > 0)); then
printf &quot;Minimum %d commands are missing in PATH, aborting\n&quot; &quot;$missing_counter&quot; >&2
exit 1
fi
### Exit meaningfully
The [exit code](/dict/terms/exit_status) is your only way to directly
communicate with the calling process without any special provisions.
If your script exits, provide a meaningful exit code. That minimally
means:
- `exit 0` (zero) if everything is okay
- `exit 1` - in general non-zero - if there was an error
This, **and only this**, will enable the calling component to check the
operation status of your script.
You know: **"One of the main causes of the fall of the Roman Empire was
that, lacking zero, they had no way to indicate successful termination
of their C programs."** *-- Robert Firth*
## Misc
### Output and appearance
- if the script is interactive, if it works for you and if you think
this is a nice feature, you can try to [save the terminal content and
restore it](/snipplets/screen_saverestore) after execution
- output clean and understandable screen messages
- if applicable, you can use colors or specific prefixes to tag error
and warning messages
- make it easy for the user to identify those messages
- write normal output to `STDOUT`. write error, warning and diagnostic
messages to `STDERR`
- enables message filtering
- keeps the script from mixing output data with diagnostic, or error
messages
- if the script gives syntax help (`-?` or `-h` or `--help`
arguments), it should go to `STDOUT`
- if applicable, write error/diagnostic messages to a logfile
- avoids screen clutter
- messages are available for diagnostic use
### Input
- never blindly assume anything. If you want the user to input a number,
**check for numeric input, leading zeros**, etc. If you have specific
format or content needs, **always validate the input!**
### Tooling

403
scripting/terminalcodes.md Normal file
View File

@ -0,0 +1,403 @@
# Terminal codes (ANSI/VT100) introduction
![](keywords>bash shell scripting colors cursor control vt100 ansi)
Terminal (control) codes are used to issue specific commands to your
terminal. This can be related to switching colors or positioning the
cursor, i.e. anything that can't be done by the application itself.
## How it technically works
A terminal control code is a special sequence of characters that is
printed (like any other text). If the terminal understands the code, it
won't display the character-sequence, but will perform some action. You
can print the codes with a simple `echo` command.
<u>**Note:**</u> I see codes referenced as "Bash colors" sometimes
(several "Bash tutorials" etc...): That's a completely incorrect
definition.
## The tput command
Because there's a large number of different terminal control languages,
usually a system has an intermediate communication layer. The real codes
are looked up in a database **for the currently detected terminal type**
and you give standardized requests to an API or (from the shell) to a
command.
One of these commands is `tput`. Tput accepts a set of acronyms called
*capability names* and any parameters, if appropriate, then looks up the
correct escape sequences for the detected terminal in the `terminfo`
database and prints the correct codes (the terminal hopefully
understands).
## The codes
In this list I'll focus on ANSI/VT100 control codes for the most common
actions - take it as quick reference. The documentation of your terminal
or the `terminfo` database is always the preferred source when something
is unclear! Also the `tput` acronyms are usually the ones dedicated for
ANSI escapes!
I listed only the most relevant codes, of course, any ANSI terminal
understands many more! But let's keep the discussion centered on common
shell scripting ;-)
If I couldn't find a matching ANSI escape, you'll see a :?: as the code.
Feel free to mail me or fix it.
The ANSI codes always start with the ESC character. (ASCII 0x1B or octal
033) This isn't part of the list, but **you should avoid using the ANSI
codes directly - use the `tput` command!**
All codes that can be used with `tput` can be found in terminfo(5). (on
OpenBSD at least) See [OpenBSD's
terminfo(5)](http://www.openbsd.org/cgi-bin/man.cgi?query=terminfo&apropos=0&sektion=5&manpath=OpenBSD+Current&arch=i386&format=html)
under the <u>Capabilities</u> section. The *cap-name* is the code to use
with tput. A description of each code is also provided.
### General useful ASCII codes
The **Ctrl-Key** representation is simply associating the non-printable
characters from ASCII code 1 with the printable (letter) characters from
ASCII code 65 ("A"). ASCII code 1 would be `^A` (Ctrl-A), while ASCII
code 7 (BEL) would be `^G` (Ctrl-G). This is a common representation
(and input method) and historically comes from one of the VT series of
terminals.
| Name | decimal | octal | hex | C-escape | Ctrl-Key | Description |
|-------|---------|-------|------|----------|----------|--------------------------------|
| `BEL` | 7 | 007 | 0x07 | `\a` | `^G` | Terminal bell |
| `BS` | 8 | 010 | 0x08 | `\b` | `^H` | Backspace |
| `HT` | 9 | 011 | 0x09 | `\t` | `^I` | Horizontal TAB |
| `LF` | 10 | 012 | 0x0A | `\n` | `^J` | Linefeed (newline) |
| `VT` | 11 | 013 | 0x0B | `\v` | `^K` | Vertical TAB |
| `FF` | 12 | 014 | 0x0C | `\f` | `^L` | Formfeed (also: New page `NP`) |
| `CR` | 13 | 015 | 0x0D | `\r` | `^M` | Carriage return |
| `ESC` | 27 | 033 | 0x1B | `<none>` | `^[` | Escape character |
| `DEL` | 127 | 177 | 0x7F | `<none>` | `<none>` | Delete character |
### Cursor handling
<table>
<thead>
<tr class="header">
<th>ANSI</th>
<th>terminfo equivalent</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>[ &lt;X&gt; ; &lt;Y&gt; H</code><br />
<code>[ &lt;X&gt; ; &lt;Y&gt; f</code></td>
<td><code>cup &lt;X&gt; &lt;Y&gt;</code></td>
<td>Home-positioning to <code>X</code> and <code>Y</code>
coordinates<br />
:!: it seems that ANSI uses 1-1 as home while <code>tput</code> uses
0-0</td>
</tr>
<tr class="even">
<td><code>[ H</code></td>
<td><code>home</code></td>
<td>Move cursor to home position (0-0)</td>
</tr>
<tr class="odd">
<td><code>7</code></td>
<td><code>sc</code></td>
<td>Save current cursor position</td>
</tr>
<tr class="even">
<td><code>8</code></td>
<td><code>rc</code></td>
<td>Restore saved cursor position</td>
</tr>
<tr class="odd">
<td>:?: most likely a normal code like <code>\b</code></td>
<td><code>cub1</code></td>
<td>move left one space (backspace)</td>
</tr>
<tr class="even">
<td>VT100 <code>[ ? 25 l</code></td>
<td><code>civis</code></td>
<td>make cursor invisible</td>
</tr>
<tr class="odd">
<td>VT100 <code>[ ? 25 h</code></td>
<td><code>cvvis</code></td>
<td>make cursor visible</td>
</tr>
</tbody>
</table>
### Erasing text
<table>
<thead>
<tr class="header">
<th style="text-align: left;">ANSI</th>
<th style="text-align: left;">terminfo equivalent</th>
<th style="text-align: left;">Description</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: left;"><code>[ K</code><br />
<code>[ 0 K</code></td>
<td style="text-align: left;"><code>el</code></td>
<td style="text-align: left;"><strong>Clear line</strong> from current
cursor position <strong>to end</strong> of line</td>
</tr>
<tr class="even">
<td style="text-align: left;"><code>[ 1 K</code></td>
<td style="text-align: left;"><code>el1</code></td>
<td style="text-align: left;"><strong>Clear line from beginning</strong>
to current cursor position</td>
</tr>
<tr class="odd">
<td style="text-align: left;"><code>[ 2 K</code></td>
<td style="text-align: left;"><code>el2</code>:?:</td>
<td style="text-align: left;"><strong>Clear whole line</strong> (cursor
position unchanged)</td>
</tr>
</tbody>
</table>
### General text attributes
| ANSI | terminfo equivalent | Description |
|---------|-----------------------------|----------------------------------------------|
| `[ 0 m` | `sgr0` | Reset all attributes |
| `[ 1 m` | `bold` | Set "bright" attribute |
| `[ 2 m` | `dim` | Set "dim" attribute |
| `[ 3 m` | `smso` | Set "standout" attribute |
| `[ 4 m` | set `smul` unset `rmul` :?: | Set "underscore" (underlined text) attribute |
| `[ 5 m` | `blink` | Set "blink" attribute |
| `[ 7 m` | `rev` | Set "reverse" attribute |
| `[ 8 m` | `invis` | Set "hidden" attribute |
### Foreground coloring
| ANSI | terminfo equivalent | Description |
|:----------|:--------------------|:----------------------------------------------|
| `[ 3 0 m` | `setaf 0` | Set **foreground** to color \#0 - **black** |
| `[ 3 1 m` | `setaf 1` | Set **foreground** to color \#1 - **red** |
| `[ 3 2 m` | `setaf 2` | Set **foreground** to color \#2 - **green** |
| `[ 3 3 m` | `setaf 3` | Set **foreground** to color \#3 - **yellow** |
| `[ 3 4 m` | `setaf 4` | Set **foreground** to color \#4 - **blue** |
| `[ 3 5 m` | `setaf 5` | Set **foreground** to color \#5 - **magenta** |
| `[ 3 6 m` | `setaf 6` | Set **foreground** to color \#6 - **cyan** |
| `[ 3 7 m` | `setaf 7` | Set **foreground** to color \#7 - **white** |
| `[ 3 9 m` | `setaf 9` | Set **default** color as foreground color |
### Background coloring
| ANSI | terminfo equivalent | Description |
|:----------|:--------------------|:----------------------------------------------|
| `[ 4 0 m` | `setab 0` | Set **background** to color \#0 - **black** |
| `[ 4 1 m` | `setab 1` | Set **background** to color \#1 - **red** |
| `[ 4 2 m` | `setab 2` | Set **background** to color \#2 - **green** |
| `[ 4 3 m` | `setab 3` | Set **background** to color \#3 - **yellow** |
| `[ 4 4 m` | `setab 4` | Set **background** to color \#4 - **blue** |
| `[ 4 5 m` | `setab 5` | Set **background** to color \#5 - **magenta** |
| `[ 4 6 m` | `setab 6` | Set **background** to color \#6 - **cyan** |
| `[ 4 7 m` | `setab 7` | Set **background** to color \#7 - **white** |
| `[ 4 9 m` | `setab 9` | Set **default** color as background color |
### Misc codes
#### Save/restore screen
Used capabilities: `smcup`, `rmcup`
You've undoubtedly already encountered programs that restore the
terminal contents after they do their work (like `vim`). This can be
done by the following commands:
# save, clear screen
tput smcup
clear
# example &quot;application&quot; follows...
read -n1 -p &quot;Press any key to continue...&quot;
# example &quot;application&quot; ends here
# restore
tput rmcup
These features require that certain capabilities exist in your
termcap/terminfo. While `xterm` and most of its clones (`rxvt`, `urxvt`,
etc) will support the instructions, your operating system may not
include references to them in its default xterm profile. (FreeBSD, in
particular, falls into this category.) If \`tput smcup\` appears to do
nothing for you, and you don't want to modify your system
termcap/terminfo data, and you KNOW that you are using a compatible
xterm application, the following may work for you:
echo -e '\033[?47h' # save screen
echo -e '\033[?47l' # restore screen
Certain software uses these codes (via their termcap capabilities) as
well. You may have seen the screen save/restore in `less`, `vim`, `top`,
`screen` and others. Some of these applications may also provide
configuration options to \*disable\* this behaviour. For example, `less`
has a `-X` option for this, which can also be set in an environment
variable:
export LESS=X
less /path/to/file
Similarly, `vim` can be configured not to "restore" the screen by adding
the following to your `~/.vimrc`:
set t_ti= t_te=
#### Additional colors
Some terminal emulators support additional colors. The most common
extension used by xterm-compatible terminals supports 256 colors. These
can be generated by `tput` with `seta{f,b} [0-255]` when the `TERM`
value has a `-256color` suffix. [Some
terminals](https://gist.github.com/XVilka/8346728#now-supporting-truecolour)
also support full 24-bit colors, and any X11 color code can be written
directly into a special escape sequence. ([More
infos](https://gist.github.com/XVilka/8346728)) Only a few programs make
use of anything beyond 256 colors, and tput doesn't know about them.
Colors beyond 16 usually only apply to modern terminal emulators running
in graphical environments.
The Virtual Terminal implemented in the Linux kernel supports only 16
colors, and the usual default terminfo entry for `TERM=linux` defines
only 8. There is sometimes an alternate "linux-16color" that you can
switch to, to get the other 8 colors.
## Bash examples
### Hardcoded colors
printf '%b\n' 'It is \033[31mnot\033[39m intelligent to use \033[32mhardcoded ANSI\033[39m codes!'
### Colors using tput
<u>Directly inside the echo:</u>
echo &quot;TPUT is a $(tput setaf 2)nice$(tput setaf 9) and $(tput setaf 5)user friendly$(tput setaf 9) terminal capability database.&quot;
<u>With preset variables:</u>
COL_NORM=&quot;$(tput setaf 9)&quot;
COL_RED=&quot;$(tput setaf 1)&quot;
COL_GREEN=&quot;$(tput setaf 2)&quot;
echo &quot;It's ${COL_RED}red${COL_NORM} and ${COL_GREEN}green${COL_NORM} - have you seen?&quot;
### Misc
<u>HOME function</u>
home() {
# yes, actually not much shorter ;-)
tput home
}
### Silly but nice effect
#!/bin/bash
DATA[0]=&quot; _/ _/ _/ _/ &quot;
DATA[1]=&quot; _/_/_/_/_/ _/_/_/ _/_/_/ _/_/_/ _/_/_/ &quot;
DATA[2]=&quot; _/ _/ _/ _/ _/ _/ _/_/ _/ _/&quot;
DATA[3]=&quot;_/_/_/_/_/ _/ _/ _/ _/ _/_/ _/ _/ &quot;
DATA[4]=&quot; _/ _/ _/_/_/ _/_/_/ _/_/_/ _/ _/ &quot;
# virtual coordinate system is X*Y ${#DATA} * 5
REAL_OFFSET_X=0
REAL_OFFSET_Y=0
draw_char() {
V_COORD_X=$1
V_COORD_Y=$2
tput cup $((REAL_OFFSET_Y + V_COORD_Y)) $((REAL_OFFSET_X + V_COORD_X))
printf %c ${DATA[V_COORD_Y]:V_COORD_X:1}
}
trap 'exit 1' INT TERM
trap 'tput setaf 9; tput cvvis; clear' EXIT
tput civis
clear
while :; do
for ((c=1; c <= 7; c++)); do
tput setaf $c
for ((x=0; x<${#DATA[0]}; x++)); do
for ((y=0; y<=4; y++)); do
draw_char $x $y
done
done
done
done
### Mandelbrot set
This is a slightly modified version of Charles Cooke's colorful
Mandelbrot plot scripts ([original w/
screenshot](http://earth.gkhs.net/ccooke/shell.html)) -- ungolfed,
optimized a bit, and without hard-coded terminal escapes. The `colorBox`
function is [memoized](http://en.wikipedia.org/wiki/Memoization) to
collect `tput` output only when required and output a new escape only
when a color change is needed. This limits the number of `tput` calls to
at most 16, and reduces raw output by more than half. The `doBash`
function uses integer arithmetic, but is still ksh93-compatible (run as
e.g. `bash ./mandelbrot` to use it). The ksh93-only floating-point
`doKsh` is almost 10x faster than `doBash` (thus the ksh shebang by
default), but uses only features that don't make the Bash parser crash.
#!/usr/bin/env ksh
# Charles Cooke's 16-color Mandelbrot
# http://earth.gkhs.net/ccooke/shell.html
# Combined Bash/ksh93 flavors by Dan Douglas (ormaaj)
function doBash {
typeset P Q X Y a b c i v x y
for ((P=10**8,Q=P/100,X=320*Q/cols,Y=210*Q/lines,y=-105*Q,v=-220*Q,x=v;y<105*Q;x=v,y+=Y)); do
for ((;x<P;a=b=i=c=0,x+=X)); do
for ((;a**2+b**2<4*P**2&&i++<99;a=((c=a)**2-b**2)/P+x,b=2*c*b/P+y)); do :
done
colorBox $((i<99?i%16:0))
done
echo
done
}
function doKsh {
integer i
float a b c x=2.2 y=-1.05 X=3.2/cols Y=2.1/lines
while
for ((a=b=i=0;(c=a)**2+b**2<=2&&i++<99&&(a=a**2-b**2+x,b=2*c*b+y);)); do :
done
. colorBox $((i<99?i%16:0))
if ((x<1?!(x+=X):(y+=Y,x=-2.2))); then
print
((y<1.05))
fi
do :
done
}
function colorBox {
(($1==lastclr)) || printf %s &quot;${colrs[lastclr=$1]:=$(tput setaf &quot;$1&quot;)}&quot;
printf '\u2588'
}
unset -v lastclr
((cols=$(tput cols)-1, lines=$(tput lines)))
typeset -a colrs
trap 'tput sgr0; echo' EXIT
${KSH_VERSION+. doKsh} ${BASH_VERSION+doBash}

467
scripting/tutoriallist.md Normal file
View File

@ -0,0 +1,467 @@
# List of Bash online-tutorials
![](keywords>bash shell scripting review tutorial list recommendation)
Here's a list of some Bash tutorials.
The primary purpose of that list is to lead beginners to *good
tutorials* and not to the wrong ones. However, the secondary purpose is
to provide information to increase quality of the linked tutorials.
My experience shows that nobody is interested when you "just send a mail
to the author", even if he links a big "contact me" in his article(s).
This is another try of influencing the Bash world.
## List
This is a test for the data plugin. For now, please use the next
section.
---- datatable tutorial ---- cols : %pageid%, recindex headers : Short
name, Recommendation Index sort : recindex filter : type=tutorial
------------------------------------------------------------------------
## Recommendations
**Note** that these recommendations are my personal opinion. Please
**contact** me
- if you have reviews or new sites
- if you're not okay with a recommendation
- if you're the author of a mentioned site (remove link, copyright,
discussion, ...)
- etc...
The recommendation-indicator "REC" used below is a number between 1 and
10 visualized as a bar:
- \<progress=0\> Not recommended to read, at best, don't click the link.
- ...
- \<progress=50\> Use it with care!
- ...
- \<progress=100\> **The perfect godlike tutorial** (I doubt I'll ever
find it)
<table>
<thead>
<tr class="header">
<th style="text-align: left;">Name (Links to review below)j</th>
<th style="text-align: left;">Weblink\ \ \</th>
<th style="text-align: left;">REC<br />
indicator</th>
<th style="text-align: left;">Comments</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: left;">Bash\ guide on Greg's wiki</td>
<td style="text-align: left;"><a
href="http://mywiki.wooledge.org/BashGuide">click</a> (<a
href="http://guide.bash.academy">new revision draft</a>)</td>
<td style="text-align: left;">&lt;progress=90&gt;</td>
<td style="text-align: left;">This guide teaches modern stuff and good
practises. I recommend learning from it. It was written by the guys in
<code>#bash</code>IRC channel on Freenode (mainly <code>lhunath</code>),
because there are so many bad tutorials out there.</td>
</tr>
<tr class="even">
<td style="text-align: left;">Steve Parker's shell scripting guide</td>
<td style="text-align: left;"><a
href="http://steve-parker.org/sh/intro.shtml">click</a></td>
<td style="text-align: left;">&lt;progress=90&gt;</td>
<td style="text-align: left;">Very good (not only Bash) shell scripting
guide. Teaches good practices, gives background information.</td>
</tr>
<tr class="odd">
<td style="text-align: left;">Bash Guide for Beginners (<a
href="#rv_bgb">review</a>)</td>
<td style="text-align: left;"><a
href="http://tldp.org/LDP/Bash-Beginners-Guide/html/">click</a></td>
<td style="text-align: left;">&lt;progress=80&gt;</td>
<td style="text-align: left;">Good introduction that really requires no
previous knowledge, also covers the most important unix utilities</td>
</tr>
<tr class="even">
<td style="text-align: left;">Advanced\ Bash\ Scripting\ Guide (ABS) (<a
href="#rv_abs">review</a>)</td>
<td style="text-align: left;"><a
href="http://tldp.org/LDP/abs/html/">click</a></td>
<td style="text-align: left;">&lt;progress=50&gt;</td>
<td style="text-align: left;">Has a lot of information that is hard to
find, is outdated and often unsafe. To be avoided until you can filter
out the good stuff.</td>
</tr>
<tr class="odd">
<td style="text-align: left;">IBM developerWorks "Bash\ by\
example"</td>
<td style="text-align: left;"><a
href="http://www.ibm.com/developerworks/library/l-bash.html">click(1)</a><br />
<a
href="http://www.ibm.com/developerworks/library/l-bash2.html">click(2)</a><br />
<a
href="http://www.ibm.com/developerworks/library/l-bash3.html">click(3)</a></td>
<td style="text-align: left;">&lt;progress=80&gt;</td>
<td style="text-align: left;">Doesn't teach outdated stuff, doesn't tell
you wrong things. A good start, though not that detailed.</td>
</tr>
<tr class="even">
<td style="text-align: left;">Deadman's</td>
<td style="text-align: left;"><a
href="http://samrowe.com/wordpress/advancing-in-the-bash-shell/">click</a></td>
<td style="text-align: left;">&lt;progress=70&gt;</td>
<td style="text-align: left;">Focus isn't scripting per se. Focus is
interactive use and increasing the productivity on the prompt. Teaches
some nice features.</td>
</tr>
<tr class="odd">
<td style="text-align: left;">Bash Shell Programming in Linux (P.
Lutus)</td>
<td style="text-align: left;"><a
href="http://www.arachnoid.com/linux/shell_programming.html">click</a></td>
<td style="text-align: left;">&lt;progress=70&gt;</td>
<td style="text-align: left;">Good start. Though there are small
bugs.</td>
</tr>
<tr class="even">
<td style="text-align: left;">BASH Help at hypexr.org (<a
href="#rv_hypexrorg">review</a>)</td>
<td style="text-align: left;"><a
href="http://www.hypexr.org/bash_tutorial.php">click</a></td>
<td style="text-align: left;">&lt;progress=50&gt;</td>
<td style="text-align: left;">Shows you some nice stuff and links to
other ressources. Not a tutorial to learn Bash, though.</td>
</tr>
<tr class="odd">
<td style="text-align: left;">Bash Programming Introduction HowTo (TLDP)
(<a href="#rv_bprogintrohowto">review</a>)</td>
<td style="text-align: left;"><a
href="http://tldp.org/HOWTO/Bash-Prog-Intro-HOWTO.html">click</a></td>
<td style="text-align: left;">&lt;progress=10&gt;</td>
<td style="text-align: left;">Absolute crap. Many syntax errors
alone.</td>
</tr>
<tr class="even">
<td style="text-align: left;">Quick guide (<a
href="#rv_qguide">review</a>)</td>
<td style="text-align: left;"><a
href="http://www.panix.com/~elflord/unix/bash-tute.html">click</a></td>
<td style="text-align: left;">&lt;progress=50&gt;</td>
<td style="text-align: left;">Usable as a start. Doesn't teach wrong
stuff, shows you good practices.</td>
</tr>
<tr class="odd">
<td style="text-align: left;">LinuxCommand.org: Writing shell scripts.
(<a href="#rv_linuxcommand">review</a>) incomplete, thus ranking isn't
complete</td>
<td style="text-align: left;"><a
href="http://linuxcommand.org/writing_shell_scripts.php">click</a></td>
<td style="text-align: left;">&lt;progress=50&gt;</td>
<td style="text-align: left;">Practise oriented, some mistakes/flaws,
but sadly it stops in the middle</td>
</tr>
<tr class="even">
<td style="text-align: left;">Linux\ Shell\ Scripting\ Tutorial\ v2.0
(<a href="#rv_linuxscriptv2">review</a>)</td>
<td style="text-align: left;"><a
href="http://bash.cyberciti.biz/guide/Main_Page">click</a></td>
<td style="text-align: left;">&lt;progress=40&gt;</td>
<td style="text-align: left;">currently reviewing (the tutorial is also
under development)</td>
</tr>
<tr class="odd">
<td style="text-align: left;">linuxconfig.org Bash\ Scripting\ Tutorial
(<a href="#rv_linuxconfig">review</a>)</td>
<td style="text-align: left;"><a
href="http://www.linuxconfig.org/Bash_scripting_Tutorial">click</a></td>
<td style="text-align: left;">&lt;progress=0&gt;</td>
<td style="text-align: left;">Teaches many outdated, unstable,
undetailed stuff. You won't learn scripting from there.</td>
</tr>
<tr class="even">
<td style="text-align: left;">Beginner\ Linux\ Tutorial</td>
<td style="text-align: left;"><a
href="http://linuxtutorial.todolistme.net">click</a></td>
<td style="text-align: left;">&lt;progress=60&gt;</td>
<td style="text-align: left;">A comprehensive introduction to the Linux
Command Line including ample examples to make learning easy.</td>
</tr>
<tr class="odd">
<td style="text-align: left;">Beginner\ Bash\ Scripting\ Tutorial</td>
<td style="text-align: left;"><a
href="http://ryanstutorials.net/bash-scripting-tutorial/">click</a></td>
<td style="text-align: left;">&lt;progress=60&gt;</td>
<td style="text-align: left;">A beginners guide to Bash scripting under
Linux.</td>
</tr>
<tr class="even">
<td style="text-align: left;">Linuxcommand.org: The\ Linux\ Command\
Line</td>
<td style="text-align: left;"><a
href="http://linuxcommand.org/tlcl.php/">click</a></td>
<td style="text-align: left;">&lt;progress=40&gt;</td>
<td style="text-align: left;">A beginners guide to using Bash shell,
basic unix utilities, and shell scripting. Shell scripting part is not
so good. But good introduction on how to use various utilities in
Bash.</td>
</tr>
</tbody>
</table>
## Detailed reviews
\<BOOKMARK:rv_linuxconfig\>
### linuxconfig.org Bash Scripting Tutorial
Article link: <http://www.linuxconfig.org/Bash_scripting_Tutorial>
Discussion link: http://www.linuxconfig.org/Talk:Bash_scripting_Tutorial
UPDATE: Discussion page is gone.
Though the basic idea is nice, using flash terminal sessions and
screenshots, there are many bugs or bad constructs.
Some stuff I didn't like there:
- uses external, unreliable command `which` instead of builtin `type -p`
to determinate the location of a program
- lacks [sane quoting](/syntax/quoting)
- uses `function SOMENAME` keyword instead of the common
POSIX-compatible variant `SOMENAME()` to [define a
function](/syntax/basicgrammar#shell_function_definitions)
- uses backticks instead of `$( ... )` for [command
substitution](/syntax/expansion/cmdsubst)
- incorrectly uses an additional array to store [positional
parameters](/scripting/posparams), disregarding that `$@` already is
array-like
- uses `echo -e` and the historical control character `\c` instead of
modern [printf](/commands/builtin/printf)
- uses `` for a in `seq 1 10` `` instead of a [C-like counter
loop](/syntax/ccmd/c_for) `for ((a=1; a <= 10; a++))`
- the `if/else` stuff looks as if the `test` (or `[ ...]`) command is
the only thing Bash can execute and check
- a `for` loop example that **will** explode on
[word-splitting](/syntax/expansion/wordsplit)
- arithmetic tests (the while/until loop examples) using the historical
"old way" with `test`, not [modern arithmetic
components](/syntax/arith_expr)
- useless [quoting](/syntax/quoting) of one-word strings (not that it
hurts per se, but it shows that the author has no clue when to use
quotes)
- a weird construct I don't understand (example for stdout/err
redirection): `grep -r hda6 * . 1>&2 stderr.txt`
`twkm` commented some things on their
http://www.linuxconfig.org/Talk:Bash_scripting_Tutorial, I linked *this
article* there. UPDATE: Discussion page is gone.
Overall, if the author doesn't change the article, it's unusable from my
point of view. At least unusable to teach sane Bash scripting.
UPDATE: Discussion is available directly below the article. I linked
this page, but waiting for moderator approval. \<BOOKMARK:rv_qguide\>
### Quick Guide
Article link: <http://www.panix.com/~elflord/unix/bash-tute.html>
Discussion Link: *not available*
This article is usable as basic introduction into the Bash world. It
doesn't teach you wrong things, and it uses correct syntax and
explanations nearly everywhere. However, it's not usable as complete
learning tutorial - but that is not the goal.
One point (I **have** to criticize **something** ;-) ):
- the article says that there are no [C-styled
for-loops](/syntax/ccmd/c_for) in Bash, this is wrong (maybe the
article is for a very old (pre `2.05b`) Bash version)
\<BOOKMARK:rv_bprogintrohowto\>
### Bash Programming Introduction HowTo
Article link: <http://tldp.org/HOWTO/Bash-Prog-Intro-HOWTO.html>
Discussion link: *not available*; EMail: `mikkey (AT) dynamo.com.ar`
A few points:
- uses `function SOMENAME` instead of more common and most portable
`SOMENAME()` to [define
functions](/syntax/basicgrammar#shell_function_definitions)
- wrong description of the [if clause](/syntax/ccmd/if_clause) (says
"the base \[...\] is `if [expression];`", which is wrong)
- a [for-loop example](/syntax/ccmd/classic_for) will explode due to
[word splitting](/syntax/expansion/wordsplit) (`for i in $( ls )` -
evil!)
- the mentioned C-like for-loop is not the [real C-like
for-loop](/syntax/ccmd/c_for), it just calls `seq(1)`+
- Many syntax errors: The examples won't even parse correctly
I like the style this article is written in. If the points are fixed, it
could be a really usable starting point for newbies. But at the moment
it's unusable
\<BOOKMARK:rv_hypexrorg\>
### BASH Help at hypexr.org
Article link: <http://www.hypexr.org/bash_tutorial.php>
Discussion link: *not available*; EMail: scott (AT) hypexr.org
The article is usable to step into the shell world. It's not a tutorial
per se, it will tell you some nice CLI-specific things like readline or
completion.
Only one point:
- confusing description of the dotfiles
\<BOOKMARK:rv_abs\>
### Advanced Bash Scripting guide (ABS)
Article link: <http://tldp.org/LDP/abs/html/>
Discussion link: *not available*; EMail: thegrendel (AT) theriver.com
The only big problem I see with the ABS is, that the name doesn't match
the reality. It doesn't teach *advanced* techniques. That **doesn't
mean** the guide isn't good! It's one of the biggest, most complete and
interesting Bash guides I've seen.
I don't want to write every point here that disturbs me. In general it's
not that important since the mistakes or wrong assumptions it makes are
minimal. Also I noticed that mistakes in example scripts vanish with
time, the author polishes his work. Thanks Mr. Cooper.
The ABS is definitely worth reading to step deeper into the Bash
commandline world of Linux (since many Linux-specific examples are
there, they're unusable for Unices).
\<BOOKMARK:rv_bgb\>
### Bash Guide for Beginners
Article link: <http://tldp.org/LDP/Bash-Beginners-Guide/html/>
Discussion link: *not available*
Good introduction to bash and shell scripting, the guide is fairly
complete and requires almost no previous knowledge other than be able to
type some commands in a shell.
Some advice is a bit strange or outdated "Real Programmers - Most
programmers will prefer to use the test built-in command" "Wherever
possible, Bash users should try to use the syntax with angular
brackets:(\$\[ \])" but all in all a nice tutorial to get a good
overview of shell programming starting from 0.
\<BOOKMARK:rv_linuxscriptv2\>
### Linux Shell Scripting Tutorial v2.0
Article link: <http://bash.cyberciti.biz/guide/Main_Page>
Discussion link: *use the individual MediaWiki discussion pages*
Additional problem: The author rates his shell skills as "9 of 10" in
his CV. After reading this tutorial personally I'd rate him 3/10
This guide has some big problems. It seems to cover a lot of material
but has some pretty nasty issues, too. Examples:
- ~~When showing how to echo variables, it shows echo `$var` and
`echo ${var}` mostly without quoting.~~
- Lots of 'test' and not much '\['. Not really a "problem" - more a
style thing. But...
- test and \[ and great, but what happened to \\\\? And test == is not
used to check if two strings are equal. You use =.
- ~~How to deal with case insensitive matching mentions converting to
lowercase with tr and doing a pattern like \[tT\]\[aA\]\[rR\]. I
propose a third solution: shopt -o nomasematch~~
- The for loop examples reads like a how-not-to. for i in \<don't do
this\>: \< \$(ls /tmp/\*)\>, \<1 2 3 4 5\>, \<\$files\>, \<\$\*\>.
Don't parse ls. Use {1..5}. Use an array for a list of files. Use
quotes (and \$@).
- The infinite-while-loop example has him reimplementing the bash select
builtin. Not bad for an exercise, but it ought to acknowledge that
bash does it better.
- the description for compound commands is wrong: It shows 3 compound
commands (grouping with and without subshell) where two of the
examples are the same and the reason given for using ( ) applies
equally to { }
- ~~the function definition focuses on the "function" keyword, which is
the worst way~~
- The initial example page for pipes seems like a good list of examples
of parsing things-you-do-NOT-want-to-parse
- ~~the page explaining special parameters misses \$@, which is very
important~~
- ~~the page(s) about shell variables and environment doesn't even
mention "environment" so far~~ some improvement.
- ~~backticks are **not** a quoting mechanism, they are a substitution
mechanism. but that doesn't matter since he doesn't mention all
quoting mechanisms anyways~~ some improvement, but the quoting page is
still misleading
- ~~another hint that he doesn't know what he's talking about: It's said
that command substitution is allowed inside backquotes (where
backquotes are a quoting mechanism!) - backquotes are **command
substitution!**~~
- etc. etc. etc. (nearly endless list, sadly)
**Conclusion**
Beside all the bashing (sorry!) above: I think the problem is the
following, the author did a lot using the shell, and he knows many
things. But he doesn't know and/or understand the underlying concepts of
most of the material covered. This - in my personal opinion -
disqualifies him as the author of a guide/tutorial for shell scripting.
**But**
The tutorial is under development. It improves here and there. But the
code style and robustness problems still remain.
**UPDATE:** Over time, the author fixed a lot of things and created new
chapters. From time to time, I'll visit again and re-check it.
\<BOOKMARK:rv_linuxcommand\>
### LinuxCommand.org: Writing shell scripts.
Article link: <http://linuxcommand.org/writing_shell_scripts.php>
Discussion link: *not available*
<u>**Bad:**</u>
- Difference between startup files `.bash_profile` and `.bashrc` is
wrong ("Though placing your aliases and shell functions in your
.bash_profile will work, it is not considered good form.")
- Reserved words (for the parser) are **not** disallowed as shell
variable names (if the variable name is a reserved word, the reserved
word takes precedence)
- It suggests the `which` command, which might be popular here and
there, but should not be used for various reasons
- It says, variables with `UPPERCASE` names are constants, this might be
a good style, but it's not a programming feature. Real "constants" are
made from read-only variables
- Common mistake it shares with many other tutorials: The ANSI C escapes
are not recognized by (normal) quoting mechanisms, it's the `echo`
command that interprets them! On the other hand, it misses the quoting
style that makes the shell interpret ANSI C escape sequences.
- It suggests the `function` keyword to define a function, which is
correct, but bad style and unportable
- It could use `getopts` for positional parameters, at least as an
"advanced example"
- It uses special parameters that represent positional parameters (`$@`)
unquoted, and thus unsafe or buggy
- Slightly wrong: `SIGKILL` and the process stop signal can't be
trapped. But that shouldn't really matter for daily shell coding
- "Creating safe temporary files", which is about creating a unique
name, should mention tools like `mktemp` or the like
- It's far from complete, it "suddenly stops". Maybe it's still under
development?
<u>**Good:**</u>
- Practise oriented
- It explains the behaviour of unquoted variable arguments to the `test`
command in a nice, understandable way
- It does not use `expr` or `let` for arithmetic, good

24
snipplets/start.md Normal file
View File

@ -0,0 +1,24 @@
\~~DISCUSSION:off\~~
# Small code snippets
![](keywords>bash shell scripting code download snippet example)
These snippets are **not** meant as HowTo or Tutorial or FAQ. Mostly
they are only a line of code and a short comment.
See them more as an initial idea or starting point rather than a
tutorial.
## List of snippets
[reset filter](/snipplets/start)
---- datacloud snippet ---- field: snipplet_tags
------------------------------------------------------------------------
---- datatable snippet ---- cols : %pageid%, LastUpdate_dt headers :
Snipplet, Last updated sort : %pageid% filter : type=snipplet
------------------------------------------------------------------------

584
start.md Normal file
View File

@ -0,0 +1,584 @@
[](bash4) \~~DISCUSSION:off\~~
# The Bash Hackers Wiki
![](keywords>bash shell linux scripting)
\<WRAP center round box 90%\> This wiki is intended to hold
documentation of any kind about GNU Bash. The main motivation was to
provide *human-readable documentation* and information so users aren't
forced to read every bit of the Bash manpage - which can be difficult to
understand. However, the docs here are **not** meant as a newbie
tutorial.
This wiki and any programs found in this wiki are free software: you can
redistribute it and/or modify it under the terms of the GNU General
Public License as published by the Free Software Foundation, either
version 3 of the License, or (at your option) any later version.
This wiki and its programs are distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see \<<http://www.gnu.org/licenses/>\>.
[What would YOU like to see here?](wishes) (outdated and locked, please
use the discussions)
**Stranger!** [Feel free to
register](http://wiki.bash-hackers.org/start&do=register) and comment or
edit the contents. There is a [Bash Hackers Wiki needs love
page](/meta/need_love) that lists some things to do. The registration is
only there to prevent SPAM. \</WRAP\>
## Scripting and general information
- [Bash v4 - a rough overview](bash4) (unmaintained, since Bash 4 is
more or less standard)
- [style](/scripting/style) -- an assorted collection of style and optic
hints
- [basics](/scripting/basics)
- [newbie_traps](/scripting/newbie_traps)
- [bashbehaviour](/scripting/bashbehaviour)
- [posparams](/scripting/posparams)
- [processtree](/scripting/processtree)
- [obsolete](/scripting/obsolete)
- [nonportable](/scripting/nonportable)
- [debuggingtips](/scripting/debuggingtips)
- [terminalcodes](/scripting/terminalcodes)
- [tutoriallist](/scripting/tutoriallist)
## Code snippets
There is a [section that holds small code snippets](/snipplets/start).
See also [some Bash source code excerpts](/misc/readthesourceluke).
## How to....
[Doing specific tasks: concepts, methods, ideas](/howto/start):
- [Simple locking (against parallel run)](/howto/mutex)
- [Rudimentary config files for your scripts](/howto/conffile)
- [Editing files with ed(1)](/howto/edit-ed)
- [Collapsing Functions](/howto/collapsing_functions)
- [Illustrated Redirection Tutorial](/howto/redirection_tutorial)
- [Calculate with dc(1)](/howto/calculate-dc)
- [Introduction to pax - the POSIX archiver](/howto/pax)
- [getopts_tutorial](/howto/getopts_tutorial) (*under construction!*)
- [dissectabadoneliner](/howto/dissectabadoneliner) An example of a bad
oneliner, breakdown and fix (by `kojoro`)
- [Write tests for ./your-script.sh](/howto/testing-your-scripts) by
using bashtest util
## Bash syntax and operations
- [Bash features overview by version](/scripting/bashchanges)
- [Basic grammar rules](/syntax/basicgrammar)
- [Quoting and character escaping](/syntax/quoting)
- [Parsing and execution](/syntax/grammar/parser_exec)
- [Some words about words...](/syntax/words)
- [Patterns and pattern matching](/syntax/pattern)
- [Arithmetic expressions](/syntax/arith_expr)
- [List of shell options](/internals/shell_options)
- [Redirection](/syntax/redirection)
- [Special parameters and shell variables](/syntax/shellvars)
- [Arrays](/syntax/arrays)
\<WRAP column 40%\> ***<u>Compound commands</u>***
| **[Compound commands overview](/syntax/ccmd/intro)** | |
|:-----------------------------------------------------|------------------------------------------------------------------|
| Grouping | |
| `{ ...; }` | [command grouping](/syntax/ccmd/grouping_plain) |
| `( ... )` | [command grouping in a subshell](/syntax/ccmd/grouping_subshell) |
| Conditionals | |
| `[[ ... ]]` | [conditional expression](/syntax/ccmd/conditional_expression) |
| `if ...; then ...; fi` | [conditional branching](/syntax/ccmd/if_clause) |
| `case ... esac` | [pattern-based branching](/syntax/ccmd/case) |
| Loops | |
| `for word in ...; do ...; done` | [classic for-loop](/syntax/ccmd/classic_for) |
| `for ((x=1; x<=10; x++)); do ...; done` | [C-style for-loop](/syntax/ccmd/c_for) |
| `while ...; do ...; done` | [while loop](/syntax/ccmd/while_loop) |
| `until ...; do ...; done` | [until loop](/syntax/ccmd/until_loop) |
| Misc | |
| `(( ... ))` | [arithmetic evaluation](/syntax/ccmd/arithmetic_eval) |
| `select word in ...; do ...; done` | [user selections](/syntax/ccmd/user_select) |
\</WRAP\>
\<WRAP column 40%\> ***<u>Expansions and substitutions</u>***
| **[Introduction to expansions and substitutions](/syntax/expansion/intro)** | |
|:----------------------------------------------------------------------------|------------------------------------------------------|
| `{A,B,C} {A..C}` | [Brace expansion](/syntax/expansion/brace) |
| `~/ ~root/` | [Tilde expansion](/syntax/expansion/tilde) |
| `$FOO ${BAR%.mp3}` | [Parameter expansion](/syntax/pe) |
| `` `command` $(command) `` | [Command substitution](/syntax/expansion/cmdsubst) |
| `<(command) >(command)` | [Process substitution](/syntax/expansion/proc_subst) |
| `$((1 + 2 + 3)) $[4 + 5 + 6]` | [Arithmetic expansion](/syntax/expansion/arith) |
| `Hello <---> Word!` | [Word splitting](/syntax/expansion/wordsplit) |
| `/data/*-av/*.mp?` | [Pathname expansion](/syntax/expansion/globs) |
\</WRAP\> \<WRAP clear\>\</WRAP\>
## Builtin Commands
This is a selection of builtin commands and command-like keywords,
loosely arranged by their common uses. These are provided directly by
the shell, rather than invoked as standalone external commands.
\<WRAP column 46%\>
<table>
<thead>
<tr class="header">
<th style="text-align: center;">Declaration commands<br />
&lt;wrap center round lo todo box 80%&gt;Commands that set and query
attributes/types, and manipulate simple
datastructures.&lt;/wrap&gt;</th>
<th></th>
<th style="text-align: center;">Alt</th>
<th style="text-align: center;">Type</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: center;"><a
href="/commands/builtin/declare">declare</a></td>
<td>Display or set shell variables or functions along with
attributes.</td>
<td style="text-align: center;"><code>typeset</code></td>
<td style="text-align: center;">builtin</td>
</tr>
<tr class="even">
<td style="text-align: center;"><a
href="/commands/builtin/export">export</a></td>
<td>Display or set shell variables, also giving them the export
attribute.</td>
<td style="text-align: center;"><code>typeset -x</code></td>
<td style="text-align: center;">special builtin</td>
</tr>
<tr class="odd">
<td style="text-align: center;"><a
href="/commands/builtin/eval">eval</a></td>
<td>Evaluate arguments as shell code.</td>
<td style="text-align: center;">-</td>
<td style="text-align: center;">special builtin</td>
</tr>
<tr class="even">
<td style="text-align: center;"><a
href="/commands/builtin/local">local</a></td>
<td>Declare variables as having function local scope.</td>
<td style="text-align: center;">-</td>
<td style="text-align: center;">builtin</td>
</tr>
<tr class="odd">
<td style="text-align: center;"><a
href="/commands/builtin/readonly">readonly</a></td>
<td>Mark variables or functions as read-only.</td>
<td style="text-align: center;"><code>typeset -r</code></td>
<td style="text-align: center;">special builtin</td>
</tr>
<tr class="even">
<td style="text-align: center;"><a
href="/commands/builtin/unset">unset</a></td>
<td>Unset variables and functions.</td>
<td style="text-align: center;">-</td>
<td style="text-align: center;">special builtin</td>
</tr>
<tr class="odd">
<td style="text-align: center;"><a
href="/commands/builtin/shift">shift</a></td>
<td>Shift positional parameters</td>
<td style="text-align: center;">-</td>
<td style="text-align: center;">special builtin</td>
</tr>
<tr class="even">
<td style="text-align: center;">I/O<br />
&lt;wrap center round lo todo box 80%&gt;Commands for reading/parsing
input, or producing/formatting output of standard
streams.&lt;/wrap&gt;</td>
<td></td>
<td style="text-align: center;">Alt</td>
<td style="text-align: center;">Type</td>
</tr>
<tr class="odd">
<td style="text-align: center;"><a
href="/syntax/keywords/coproc">coproc</a></td>
<td>Co-processes: Run a command in the background with pipes for reading
/ writing its standard streams.</td>
<td style="text-align: center;">-</td>
<td style="text-align: center;">keyword</td>
</tr>
<tr class="even">
<td style="text-align: center;"><a
href="/commands/builtin/echo">echo</a></td>
<td>Create output from arguments.</td>
<td style="text-align: center;">-</td>
<td style="text-align: center;">builtin</td>
</tr>
<tr class="odd">
<td style="text-align: center;"><a
href="/commands/builtin/mapfile">mapfile</a></td>
<td>Read lines of input into an array.</td>
<td style="text-align: center;"><code>readarray</code></td>
<td style="text-align: center;">builtin</td>
</tr>
<tr class="even">
<td style="text-align: center;"><a
href="/commands/builtin/printf">printf</a></td>
<td>"advanced <code>echo</code>."</td>
<td style="text-align: center;">-</td>
<td style="text-align: center;">builtin</td>
</tr>
<tr class="odd">
<td style="text-align: center;"><a
href="/commands/builtin/read">read</a></td>
<td>Read input into variables or arrays, or split strings into fields
using delimiters.</td>
<td style="text-align: center;">-</td>
<td style="text-align: center;">builtin</td>
</tr>
<tr class="even">
<td style="text-align: center;">Configuration and Debugging<br />
&lt;wrap center round lo todo box 80%&gt;Commands that modify shell
behavior, change special options, assist in debugging.&lt;/wrap&gt;</td>
<td></td>
<td style="text-align: center;">Alt</td>
<td style="text-align: center;">Type</td>
</tr>
<tr class="odd">
<td style="text-align: center;"><a
href="/commands/builtin/caller">caller</a></td>
<td>Identify/print execution frames.</td>
<td style="text-align: center;">-</td>
<td style="text-align: center;">builtin</td>
</tr>
<tr class="even">
<td style="text-align: center;"><a
href="/commands/builtin/set">set</a></td>
<td>Set the positional parameters and/or set options that affect shell
behaviour.</td>
<td style="text-align: center;">-</td>
<td style="text-align: center;">special builtin</td>
</tr>
<tr class="odd">
<td style="text-align: center;"><a
href="/commands/builtin/shopt">shopt</a></td>
<td>set/get some bash-specific shell options.</td>
<td style="text-align: center;">-</td>
<td style="text-align: center;">builtin</td>
</tr>
</tbody>
</table>
\</WRAP\>
\<WRAP column 46%\>
<table>
<thead>
<tr class="header">
<th style="text-align: center;">Control flow and data processing<br />
&lt;wrap center round lo todo box 80%&gt;Commands that operate on data
and/or affect control flow.&lt;/wrap&gt;</th>
<th></th>
<th style="text-align: center;">Alt</th>
<th style="text-align: center;">Type</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: center;"><a
href="/commands/builtin/true">colon</a></td>
<td>"true" null command.</td>
<td style="text-align: center;">true</td>
<td style="text-align: center;">special builtin</td>
</tr>
<tr class="even">
<td style="text-align: center;"><a
href="/commands/builtin/source">dot</a></td>
<td>Source external files.</td>
<td style="text-align: center;">source</td>
<td style="text-align: center;">special builtin</td>
</tr>
<tr class="odd">
<td style="text-align: center;"><a
href="/commands/builtin/false">false</a></td>
<td>Fail at doing nothing.</td>
<td style="text-align: center;">-</td>
<td style="text-align: center;">builtin</td>
</tr>
<tr class="even">
<td style="text-align: center;"><a
href="/commands/builtin/continueBreak">continue / break</a></td>
<td>continue with or break out of loops.</td>
<td style="text-align: center;">-</td>
<td style="text-align: center;">special builtin</td>
</tr>
<tr class="odd">
<td style="text-align: center;"><a
href="/commands/builtin/let">let</a></td>
<td>Arithmetic evaluation simple command.</td>
<td style="text-align: center;">-</td>
<td style="text-align: center;">builtin</td>
</tr>
<tr class="even">
<td style="text-align: center;"><a
href="/commands/builtin/return">return</a></td>
<td>Return from a function with a specified exit status.</td>
<td style="text-align: center;">-</td>
<td style="text-align: center;">special builtin</td>
</tr>
<tr class="odd">
<td style="text-align: center;"><a
href="/commands/classictest">[</a></td>
<td>The classic <code>test</code> simple command.</td>
<td style="text-align: center;">test</td>
<td style="text-align: center;">builtin</td>
</tr>
<tr class="even">
<td style="text-align: center;">Process and Job control<br />
&lt;wrap center round lo todo box 80%&gt;Commands related to jobs,
signals, process groups, subshells.&lt;/wrap&gt;</td>
<td></td>
<td style="text-align: center;">Alt</td>
<td style="text-align: center;">Type</td>
</tr>
<tr class="odd">
<td style="text-align: center;"><a
href="/commands/builtin/exec">exec</a></td>
<td>Replace the current shell process or set redirections.</td>
<td style="text-align: center;">-</td>
<td style="text-align: center;">special builtin</td>
</tr>
<tr class="even">
<td style="text-align: center;"><a
href="/commands/builtin/exit">exit</a></td>
<td>Exit the shell.</td>
<td style="text-align: center;">-</td>
<td style="text-align: center;">special builtin</td>
</tr>
<tr class="odd">
<td style="text-align: center;"><a
href="/commands/builtin/kill">kill</a></td>
<td>Send a signal to specified process(es)</td>
<td style="text-align: center;">-</td>
<td style="text-align: center;">builtin</td>
</tr>
<tr class="even">
<td style="text-align: center;"><a
href="/commands/builtin/trap">trap</a></td>
<td>Set signal handlers or output the current handlers.</td>
<td style="text-align: center;">-</td>
<td style="text-align: center;">special builtin</td>
</tr>
<tr class="odd">
<td style="text-align: center;"><a
href="/commands/builtin/times">times</a></td>
<td>Display process times.</td>
<td style="text-align: center;">-</td>
<td style="text-align: center;">special builtin</td>
</tr>
<tr class="even">
<td style="text-align: center;"><a
href="/commands/builtin/wait">wait</a></td>
<td>Wait for background jobs and asynchronous lists.</td>
<td style="text-align: center;">-</td>
<td style="text-align: center;">builtin</td>
</tr>
<tr class="odd">
<td style="text-align: center;"></td>
<td></td>
<td style="text-align: center;">Alt</td>
<td style="text-align: center;">Type</td>
</tr>
</tbody>
</table>
\</WRAP\> \<WRAP clear\>\</WRAP\> \<WRAP hide\> previous alphabetical
version \<WRAP column 40%\>
| A-G | |
|--------------------------------------|------------------------------------------------------------------------------------------------------|
| [caller](/commands/builtin/caller) | identify/print execution frames (Bash builtin) |
| [coproc](/syntax/keywords/coproc) | Co-processes (Bash keyword) |
| [declare](/commands/builtin/declare) | display or set shell variables or functions along with attributes (Bash builtin, synonym: `typeset`) |
| [exec](/commands/builtin/exec) | replace the shell, set redirections (Bash builtin) |
| I-N | |
| [let](/commands/builtin/let) | arithmetic evaluation - an old fashioned way (Bash builtin) |
| [mapfile](/commands/builtin/mapfile) | Mapping lines of input to an array, also named `readarray` (Bash builtin) |
\</WRAP\>
\<WRAP column 40%\>
| O-T | |
|----------------------------------------|------------------------------------------------------------------|
| [printf](/commands/builtin/printf) | "advanced `echo`" (Bash builtin) |
| [read](/commands/builtin/read) | getting input from `stdin` (Bash builtin) |
| [readonly](/commands/builtin/readonly) | mark variables or functions as read-only (Bash builtin) |
| [set](/commands/builtin/set) | control positional parameters and shell behaviour (Bash builtin) |
| [shopt](/commands/builtin/shopt) | set/get shell options (Bash builtin) |
| [test](/commands/classictest) | the classic `test` command (Bash builtin) |
| U-Z | |
| [unset](/commands/builtin/unset) | unset variables and functions (Bash builtin) |
\</WRAP\> \<WRAP clear\>\</WRAP\> \</WRAP\>
## Dictionary
\<note tip\>A list of expressions, words, and their meanings is
[here](/dict/index).\</note\>
## Links
### Official Bash links
- [Chet Ramey's Bash
page](http://tiswww.case.edu/php/chet/bash/bashtop.html) and its
[FAQ](http://tiswww.case.edu/php/chet/bash/FAQ).
- [GNU Bash software page](http://www.gnu.org/software/bash/)
- Official Bash mailing lists:
- **Bug reports**: <bug-bash@gnu.org>
([archives](http://mail.gnu.org/pipermail/bug-bash))
- **General questions**: <help-bash@gnu.org>
([archives](http://mail.gnu.org/pipermail/help-bash))
- Official Bash git repository:
- **Browse**: [cgit](http://git.savannah.gnu.org/cgit/bash.git)
- **Clone**: //<git://> ssh://git.sv.gnu.org/srv/git/bash.git •
//<ssh://> ssh://git.sv.gnu.org/srv/git/bash.git • //<http://>
http://git.savannah.gnu.org/r/bash.git
### Recommended Shell resources
- [Greg's wiki](http://mywiki.wooledge.org/) - Greg Wooledge's (aka
"greycat") wiki -- with **MASSIVE** information about Bash and UNIX(r)
in general.
- [BashFAQ](http://mywiki.wooledge.org/BashFAQ) •
[BashGuide](http://mywiki.wooledge.org/BashGuide) •
[BashPitfalls](http://mywiki.wooledge.org/BashPitfalls) •
[BashSheet](http://mywiki.wooledge.org/BashSheet)
- [Sven Mascheck's pages](http://www.in-ulm.de/~mascheck/) - A goldmine
of information. A must-read.
- [\#ksh channel page](https://www.mirbsd.org/ksh-chan.htm) - \#ksh
Freenode channel page maintains a solid collection of recommended
links.
- [The Grymoire Unix pages](http://www.grymoire.com/Unix/) - Good
scripting information, especially read the
[quoting](http://www.grymoire.com/Unix/Quote.html) guide.
- [Heiner's "Shell Dorado"](http://www.shelldorado.com) - Tips, tricks,
links - for every situation.
- [The Single Unix Specification (version 4, aka
POSIX-2008)](http://pubs.opengroup.org/onlinepubs/9699919799/)
- [The Austin Group](http://www.opengroup.org/austin/) - [List
archives](http://dir.gmane.org/gmane.comp.standards.posix.austin.general),
[Bug tracker](http://austingroupbugs.net/main_page.php)
- [comp.unix.shell FAQ](http://cfajohnson.com/shell/cus-faq.html)
- [Advanced Bash-Scripting
Guide](https://tldp.org/LDP/abs/html/index.html) - last update: 10 Mar
2014, but still very useful guide.
#### Documentation / Reference
- **Bash**: [man page](http://tiswww.case.edu/php/chet/bash/bash.html)
[info page](http://tiswww.case.edu/php/chet/bash/bashref.html)
- **AT&T ksh**:
[ksh88](http://www2.research.att.com/sw/download/man/man1/ksh88.html)
[ksh93](http://www2.research.att.com/sw/download/man/man1/ksh.html)
- [mksh](https://www.mirbsd.org/htman/i386/man1/mksh.htm) (pdksh
successor)
- [zsh](http://zsh.sourceforge.net/Doc/)
- [dash](http://man7.org/linux/man-pages/man1/dash.1.html)
- [Heirloom Bourne shell](http://heirloom.sourceforge.net/man/sh.1.html)
- [Thompson shell](http://v6shell.org/man/osh.1.html)
### Assorted interesting links
- [History and development of the traditional Bourne shell
family](http://www.in-ulm.de/~mascheck/bourne/) - very interesting and
nice to read!
- [Interview with Chet
Ramey](http://www.computerworld.com.au/article/222764/-z_programming_languages_bash_bourne-again_shell)
- [Interview with Steve
Bourne](http://www.computerworld.com.au/article/279011/a-z_programming_languages_sh)
• [Stephen Bourne - BSDCan 2015
keynote](https://www.youtube.com/watch?v=2kEJoWfobpA)
- [Interview with David
Korn](http://news.slashdot.org/story/01/02/06/2030205/david-korn-tells-all)
- [Kernighan on the Unix pipeline (computerphile
video)](https://www.youtube.com/watch?v=bKzonnwoR2I)
- Linux in general, with some shell related stuff: [nixCraft: Linux
Tips, Hacks, Tutorials and Ideas](http://www.cyberciti.biz/)
- Linux tutorials, guides and how-tos: [RoseHosting
Blog](https://www.rosehosting.com/blog/), [bash script for installing
WordPress](https://www.rosehosting.com/blog/script-install-wordpress-on-a-debianubuntu-vps/)
and some [basic shell
commands](https://www.rosehosting.com/blog/basic-shell-commands-after-putty-ssh-logon/)
- [Bashphorism list from the Bash IRC channel on
Freenode](/misc/bashphorisms)
- [Some more or less funny commandline stuff](/misc/shell_humor)
- [How to Enable SSH on Ubuntu
Tutorial](https://thishosting.rocks/how-to-enable-ssh-on-ubuntu/)
- [How To Make an Awesome Custom Shell with
ZSH](https://linuxstans.com/how-to-make-an-awesome-custom-shell-with-zsh/)
### Bash Libraries (needs review)
- [An oo-style bash library for bash
4](http://sourceforge.net/projects/oobash/) - provides tools for rapid
script development and huge libraries.
- [General purpose shell framework for bash
4](https://github.com/hornos/shf3) - in development.
- [General purpose bash library for bash
4](https://github.com/chilicuil/learn/blob/master/sh/lib) - active
development
\<div hide\>
## Most wanted
| Statistics for Month: **April 2012** | | |
|--------------------------------------|-------|------------------------------------------------------------|
| Rank | Views | Article |
| 1 | 6833 | [getopts_tutorial](/howto/getopts_tutorial) (March: 11663) |
| 2 | 4025 | [printf](/commands/builtin/printf) (March: 7079) |
| 3 | 2861 | [quoting](/syntax/quoting) (March: 5730) |
| 4 | 2854 | [classictest](/commands/classictest) (March: 6145) |
| 5 | 2222 | [pe](/syntax/pe) (March: 4540) |
| 6 | 1760 | [posparams](/scripting/posparams) (March: 3591) |
| 7 | 1249 | [bash4](bash4) (March: --) |
| 8 | 1212 | [edit-ed](/howto/edit-ed) (March: 2727) |
| 9 | 1205 | [read](/commands/builtin/read) (March: --) |
| 10 | 1182 | [mutex](/howto/mutex) (March: --) |
- "Total visits": 35144
- The overall "start" page is not counted
\</div\>
## Contact
\<WRAP column 40%\> Visit us in `irc.freenode.net`, channel `#bash` ;-)
If you have critiques or suggestions, please feel free to send a mail
using the contact form on the right. Note that there is a simple
discussion option below every article.
Please also see the [imprint](/user/thebonsai/imprint) if you have
problems with the site and its contents (legality, ...)!
It also would be nice to drop a line when
- it helped you
- it didn't help you (something missing / unclear)
- you like it
- you don't like it
- you found mistakes / bugs
Simply: Reader's feedback \</WRAP\>
\<WRAP column 40%\> ![](contact>subj=Wiki reader feedback) \</WRAP\>

374
syntax/arith_expr.md Normal file
View File

@ -0,0 +1,374 @@
# Arithmetic expressions
![](keywords>bash shell scripting math arithmetic C calculation integer)
Arithmetic expressions are used in several situations:
- [arithmetic evaluation command](/syntax/ccmd/arithmetic_eval)
- [arithmetic expansion](/syntax/expansion/arith)
- [substring parameter expansion](/syntax/pe#substring_expansion)
- [the `let` builtin command](/commands/builtin/let)
- [C-style for loop](/syntax/ccmd/c_for)
- [array indexing](/syntax/arrays)
- [conditional expressions](/syntax/ccmd/conditional_expression)
- Assignment statements, and arguments to declaration commands of
variables with the integer attribute.
These expressions are evaluated following some rules described below.
The operators and rules of arithmetic expressions are mainly derived
from the C programming language.
This article describes the theory of the used syntax and the behaviour.
To get practical examples without big explanations, see [this page on
Greg's
wiki](http://mywiki.wooledge.org/BashGuide/CompoundCommands#Arithmetic_Evaluation).
## Constants
Mathematical constants are simply fixed values you write: `1`, `3567`,
or `4326`. Bash interprets some notations specially:
- `0...` (leading zero) is interpreted as an **octal** value
- `0x...` is interpreted as a **hex** value
- `0X...` also interpreted as a **hex**
- `<BASE>#...` is interpreted as a number according to the **specified
base** `<BASE>`, e.g., `2#00111011` (see below)
If you have a constant set in a variable, like,
x=03254
this is interpreted as an octal value. If you want it to be interpreted
as a decimal value, you need to expand the parameter and specify base
10:
# this is interpreted as a decimal:
echo $(( 10#$x ))
# this is interpreted as an octal:
echo $(( x ))
# this is an invalid digit for base 10 (the &quot;x&quot;)...:
echo $(( 10#x ))
## Different bases
For a constant, the base can be specified using the form
<BASE>#<DIGITS...>
Regardless of the specified base, the arithmetic expressions will, if
ever displayed, be **displayed in decimal**!
When no base is specified, the base 10 (decimal) is assumed, except when
the prefixes as mentioned above (octals, hexadecimals) are present. The
specified base can range from 2 to 64. To represent digits in a
specified base greater than 10, characters other than 0 to 9 are needed
(in this order, low =\> high):
- `0 ... 9`
- `a ... z`
- `A ... Z`
- `@`
- `_`
Let's quickly invent a new number system with base 43 to show what I
mean:
$ echo $((43#1))
1
$ echo $((43#a))
10
$echo $((43#A))
36
$ echo $((43#G))
42
$ echo $((43#H))
bash: 43#H: value too great for base (error token is &quot;43#H&quot;)
If you have no clue what a base is and why there might be other bases,
and what numbers are and how they are built, then you don't need
different bases.
If you want to convert between the usual bases (octal, decimal, hex),
use [the printf command](/commands/builtin/printf) and its format
strings.
## Shell variables
Shell variables can of course be used as operands, even when the integer
attribute is not turned on (by `declare -i <NAME>`). If the variable is
empty (null) or unset, its reference evaluates to 0. If the variable
doesn't hold a value that looks like a valid expression (numbers or
operations), the expression is re-used to reference, for example, the
named parameters, e.g.:
test=string
string=3
echo $((test))
# will output &quot;3&quot;!
Of course, in the end, when it finally evaluates to something that is
**not** a valid arithmetic expression (newlines, ordinary text, ...)
then you'll get an error.
When variables are referenced, the notation `1 + $X` is equivalent to
the notation `1 + X`, both are allowed.
When variables are referenced like `$X`, the rules of [parameter
expansion](/syntax/pe) apply and are performed **before** the expression
is evaluated. Thus, a construct like `${MYSTRING:4:3}` is valid inside
an arithmetic expression.
## Truth
Unlike command exit and return codes, arithmetic expressions evaluate to
logical "true" when they are not 0. When they are 0, they evaluate to
"false". The [arithmetic evaluation compound
command](/syntax/ccmd/arithmetic_eval) reverses the "truth" of an
arithmetic expression to match the "truth" of command exit codes:
- if the arithmetic expression brings up a value not 0 (arithmetic
true), it returns 0 (shell true)
- if the arithmetic expression evaluates to 0 (arithmetic false), it
returns 1 (shell false)
That means, the following `if`-clause will execute the `else`-thread:
if ((0)); then
echo &quot;true&quot;
else
echo &quot;false&quot;
fi
## Operators
### Assignment
| Operator | Description |
|---------------------|----------------------------------------------------------------------------------------------------|
| `<ID> = <EXPR>` | normal assignment |
| `<ID> *= <EXPR>` | equivalent to `<ID> = <ID> * <EXPR>`, see [calculation operators](/syntax/arith_expr#calculations) |
| `<ID> /= <EXPR>` | equivalent to `<ID> = <ID> / <EXPR>`, see [calculation operators](/syntax/arith_expr#calculations) |
| `<ID> %= <EXPR>` | equivalent to `<ID> = <ID> % <EXPR>`, see [calculation operators](/syntax/arith_expr#calculations) |
| `<ID> += <EXPR>` | equivalent to `<ID> = <ID> + <EXPR>`, see [calculation operators](/syntax/arith_expr#calculations) |
| `<ID> -= <EXPR>` | equivalent to `<ID> = <ID> - <EXPR>`, see [calculation operators](/syntax/arith_expr#calculations) |
| `<ID> <<= <NUMBER>` | equivalent to `<ID> = <ID> << <NUMBER>`, see [bit operations](/syntax/arith_expr#bit_operations) |
| `<ID> >>= <NUMBER>` | equivalent to `<ID> = <ID> >> <NUMBER>`, see [bit operations](/syntax/arith_expr#bit_operations) |
| `<ID> &= <EXPR>` | equivalent to `<ID> = <ID> & <EXPR>`, see [bit operations](/syntax/arith_expr#bit_operations) |
| `<ID> ^= <EXPR>` | equivalent to `<ID> = <ID> ^ <EXPR>`, see [bit operations](/syntax/arith_expr#bit_operations) |
| `<ID> |= <EXPR>` | equivalent to `<ID> = <ID> | <EXPR>`, see [bit operations](/syntax/arith_expr#bit_operations) |
### Calculations
| Operator | Description |
|----------|--------------------|
| `*` | multiplication |
| `/` | division |
| `%` | remainder (modulo) |
| `+` | addition |
| `-` | subtraction |
| `**` | exponentiation |
### Comparisons
| Operator | Description |
|----------|-----------------------------------|
| `<` | comparison: less than |
| `>` | comparison: greater than |
| `<=` | comparison: less than or equal |
| `>=` | comparison: greater than or equal |
| `==` | equality |
| `!=` | inequality |
### Bit operations
| Operator | Description |
|----------|----------------------------|
| `~` | bitwise negation |
| `<<` | bitwise shifting (left) |
| `>>` | bitwise shifting (right) |
| `&` | bitwise AND |
| `^` | bitwise exclusive OR (XOR) |
| `|` | bitwise OR |
### Logical
| Operator | Description |
|----------|------------------|
| `!` | logical negation |
| `&&` | logical AND |
| `||` | logical OR |
### Misc
<table>
<thead>
<tr class="header">
<th style="text-align: left;">Operator</th>
<th style="text-align: left;">Description</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: left;"><code>id++</code></td>
<td style="text-align: left;"><strong>post-increment</strong> of the
variable <code>id</code> (not required by POSIX(r))</td>
</tr>
<tr class="even">
<td style="text-align: left;"><code>id--</code></td>
<td style="text-align: left;"><strong>post-decrement</strong> of the
variable <code>id</code> (not required by POSIX(r))</td>
</tr>
<tr class="odd">
<td style="text-align: left;"><code>++id</code></td>
<td style="text-align: left;"><strong>pre-increment</strong> of the
variable <code>id</code> (not required by POSIX(r))</td>
</tr>
<tr class="even">
<td style="text-align: left;"><code>--id</code></td>
<td style="text-align: left;"><strong>pre-decrement</strong> of the
variable <code>id</code> (not required by POSIX(r))</td>
</tr>
<tr class="odd">
<td style="text-align: left;"><code>+</code></td>
<td style="text-align: left;">unary plus</td>
</tr>
<tr class="even">
<td style="text-align: left;"><code>-</code></td>
<td style="text-align: left;">unary minus</td>
</tr>
<tr class="odd">
<td
style="text-align: left;"><code>&lt;EXPR&gt; ? &lt;EXPR&gt; : &lt;EXPR&gt;</code></td>
<td style="text-align: left;">conditional (ternary) operator<br />
&lt;condition&gt; ? &lt;result-if-true&gt; :
&lt;result-if-false&gt;</td>
</tr>
<tr class="even">
<td
style="text-align: left;"><code>&lt;EXPR&gt; , &lt;EXPR&gt;</code></td>
<td style="text-align: left;">expression list</td>
</tr>
<tr class="odd">
<td style="text-align: left;"><code>( &lt;EXPR&gt; )</code></td>
<td style="text-align: left;">subexpression (to force precedence)</td>
</tr>
</tbody>
</table>
## Precedence
The operator precedence is as follows (highest -\> lowest):
- Postfix (`id++`, `id--`)
- Prefix (`++id`, `--id`)
- Unary minus and plus (`-`, `+`)
- Logical and bitwise negation (`!`, `~`)
- Exponentiation (`**`)
- Multiplication, division, remainder (`*`, `/`, `%`)
- Addition, subtraction (`+`, `-`)
- Bitwise shifts (`<<`, `>>`)
- Comparison (`<`, `>`, `<=`, `>=`)
- (In-)equality (`==`, `!=`)
- Bitwise AND (`&`)
- Bitwise XOR (`^`)
- Bitwise OR (`|`)
- Logical AND (`&&`)
- Logical OR (`||`)
- Ternary operator (`<EXPR> ? <EXPR> : <EXPR>`)
- Assignments (`=`, `*=`, `/=`, `%=`, `+=`, `-=`, `<<=`, `>>=`, `&=`,
`^=`, `|=`)
- Expression list operator (`<EXPR> , <EXPR>`)
The precedence can be adjusted using subexpressions of the form
`( <EXPR> )` at any time. These subexpressions are always evaluated
first.
## Arithmetic expressions and return codes
Bash's overall language construct is based on exit codes or return codes
of commands or functions to be executed. `if` statements, `while` loops,
etc., they all take the return codes of commands as conditions.
Now the problem is: The return codes (0 means "TRUE" or "SUCCESS", not 0
means "FALSE" or "FAILURE") don't correspond to the meaning of the
result of an arithmetic expression (0 means "FALSE", not 0 means
"TRUE").
That's why all commands and keywords that do arithmetic operations
attempt to **translate** the arithmetical meaning into an equivalent
return code. This simply means:
- if the arithmetic operation evaluates to 0 ("FALSE"), the return code
is not 0 ("FAILURE")
- if the arithmetic operation evaluates to 1 ("TRUE"), the return code
is 0 ("SUCCESS")
This way, you can easily use arithmetic expressions (along with the
commands or keywords that operate them) as conditions for `if`, `while`
and all the others, including `set -e` for autoexit on error:
``` bash
MY_TEST_FLAG=0
if ((MY_TEST_FLAG)); then
echo &quot;MY_TEST_FLAG is ON&quot;
else
echo &quot;MY_TEST_FLAG is OFF&quot;
fi
```
\<WRAP center round important\> Beware that `set -e` can change the
runtime behavior of scripts. For example,
This non-equivalence of code behavior deserves some attention. Consider
what happens if v happens to be zero in the expression below:
``` bash
((v += 0))
echo $?
```
1
("FAILURE")
``` bash
v=$((v + 0))
echo $?
```
0
("SUCCESS")
The return code behavior is not equivalent to the arithmetic behavior,
as has been noted.
A workaround is to use a list operation that returns True, or use the
second assignment style.
``` bash
((v += 0)) || :
echo $?
```
0
("SUCCESS")
This change in code behavior was discovered once the script was run
under set -e. \</WRAP\>
## Arithmetic expressions in Bash
- [The C-style for-loop](/syntax/ccmd/c_for)
- [Arithmetic expansion](/syntax/expansion/arith)
- [Arithmetic evaluation compound command](/syntax/ccmd/arithmetic_eval)
- [The "let" builtin command](/commands/builtin/let)

718
syntax/arrays.md Normal file
View File

@ -0,0 +1,718 @@
# Arrays
## Purpose
An array is a parameter that holds mappings from keys to values. Arrays
are used to store a collection of parameters into a parameter. Arrays
(in any programming language) are a useful and common composite data
structure, and one of the most important scripting features in Bash and
other shells.
Here is an **abstract** representation of an array named `NAMES`. The
indexes go from 0 to 3.
NAMES
0: Peter
1: Anna
2: Greg
3: Jan
Instead of using 4 separate variables, multiple related variables are
grouped grouped together into *elements* of the array, accessible by
their *key*. If you want the second name, ask for index 1 of the array
`NAMES`.
## Indexing
Bash supports two different types of ksh-like one-dimensional arrays.
**Multidimensional arrays are not implemented**.
- *Indexed arrays* use positive integer numbers as keys. Indexed arrays
are **always sparse**, meaning indexes are not necessarily contiguous.
All syntax used for both assigning and dereferencing indexed arrays is
an [arithmetic evaluation context](/syntax/arith_expr) (see
[\#Referencing](#Referencing)). As in C and many other languages, the
numerical array indexes start at 0 (zero). Indexed arrays are the most
common, useful, and portable type. Indexed arrays were first
introduced to Bourne-like shells by ksh88. Similar, partially
compatible syntax was inherited by many derivatives including Bash.
Indexed arrays always carry the `-a` attribute.
- *Associative arrays* (sometimes known as a "hash" or "dict") use
arbitrary nonempty strings as keys. In other words, associative arrays
allow you to look up a value from a table based upon its corresponding
string label. **Associative arrays are always unordered**, they merely
*associate* key-value pairs. If you retrieve multiple values from the
array at once, you can't count on them coming out in the same order
you put them in. Associative arrays always carry the `-A` attribute,
and unlike indexed arrays, Bash requires that they always be declared
explicitly (as indexed arrays are the default, see
[declaration](#Declaration)). Associative arrays were first introduced
in ksh93, and similar mechanisms were later adopted by Zsh and Bash
version 4. These three are currently the only POSIX-compatible shells
with any associative array support.
## Syntax
### Referencing
To accommodate referring to array variables and their individual
elements, Bash extends the parameter naming scheme with a subscript
suffix. Any valid ordinary scalar parameter name is also a valid array
name: `[[:alpha:]_][[:alnum:]_]*`. The parameter name may be followed by
an optional subscript enclosed in square brackets to refer to a member
of the array.
The overall syntax is `arrname[subscript]` - where for indexed arrays,
`subscript` is any valid arithmetic expression, and for associative
arrays, any nonempty string. Subscripts are first processed for
parameter and arithmetic expansions, and command and process
substitutions. When used within parameter expansions or as an argument
to the [unset](commands/builtin/unset) builtin, the special subscripts
`*` and `@` are also accepted which act upon arrays analogously to the
way the `@` and `*` special parameters act upon the positional
parameters. In parsing the subscript, bash ignores any text that follows
the closing bracket up to the end of the parameter name.
With few exceptions, names of this form may be used anywhere ordinary
parameter names are valid, such as within [arithmetic
expressions](/syntax/arith_expr), [parameter expansions](/syntax/pe),
and as arguments to builtins that accept parameter names. An *array* is
a Bash parameter that has been given the `-a` (for indexed) or `-A` (for
associative) *attributes*. However, any regular (non-special or
positional) parameter may be validly referenced using a subscript,
because in most contexts, referring to the zeroth element of an array is
synonymous with referring to the array name without a subscript.
# &quot;x&quot; is an ordinary non-array parameter.
$ x=hi; printf '%s ' &quot;$x&quot; &quot;${x[0]}&quot;; echo &quot;${_[0]}&quot;
hi hi hi
The only exceptions to this rule are in a few cases where the array
variable's name refers to the array as a whole. This is the case for the
`unset` builtin (see [destruction](#Destruction)) and when declaring an
array without assigning any values (see [declaration](#Declaration)).
### Declaration
The following explicitly give variables array attributes, making them
arrays:
| Syntax | Description |
|--------------------|-------------------------------------------------------------------------------------------------------------------------|
| `ARRAY=()` | Declares an **indexed** array `ARRAY` and initializes it to be empty. This can also be used to empty an existing array. |
| `ARRAY[0]=` | Generally sets the first element of an **indexed** array. If no array `ARRAY` existed before, it is created. |
| `declare -a ARRAY` | Declares an **indexed** array `ARRAY`. An existing array is not initialized. |
| `declare -A ARRAY` | Declares an **associative** array `ARRAY`. This is the one and only way to create associative arrays. |
As an example, and for use below, let's declare our `NAMES` array as
described [above](#purpose):
declare -a NAMES=('Peter' 'Anna' 'Greg' 'Jan')
### Storing values
Storing values in arrays is quite as simple as storing values in normal
variables.
| Syntax | Description |
|:--------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `ARRAY[N]=VALUE` | Sets the element `N` of the **indexed** array `ARRAY` to `VALUE`. **`N` can be any valid [arithmetic expression](/syntax/arith_expr)**. |
| `ARRAY[STRING]=VALUE` | Sets the element indexed by `STRING` of the **associative array** `ARRAY`. |
| `ARRAY=VALUE` | As above. If no index is given, as a default the zeroth element is set to `VALUE`. Careful, this is even true of associative arrays - there is no error if no key is specified, and the value is assigned to string index "0". |
| `ARRAY=(E1\ E2\ ...)` | Compound array assignment - sets the whole array `ARRAY` to the given list of elements indexed sequentially starting at zero. The array is unset before assignment unless the += operator is used. When the list is empty (`ARRAY=()`), the array will be set to an empty array. This method obviously does not use explicit indexes. An **associative array** can **not** be set like that! Clearing an associative array using `ARRAY=()` works. |
| `ARRAY=([X]=E1\ [Y]=E2\ ...)` | Compound assignment for indexed arrays with index-value pairs declared individually (here for example `X` and `Y`). X and Y are arithmetic expressions. This syntax can be combined with the above - elements declared without an explicitly specified index are assigned sequentially starting at either the last element with an explicit index, or zero. |
| `ARRAY=([S1]=E1\ [S2]=E2\ ...)` | Individual mass-setting for **associative arrays**. The named indexes (here: `S1` and `S2`) are strings. |
| `ARRAY+=(E1\ E2\ ...)` | Append to ARRAY. |
| `ARRAY=("${ANOTHER_ARRAY[@]}")` | Copy ANOTHER_ARRAY to ARRAY, copying each element. |
As of now, arrays can't be exported.
### Getting values
\<note\> For completeness and details on several parameter expansion
variants, see the [article about parameter expansion](/syntax/pe) and
check the notes about arrays. \</note\>
| Syntax | Description |
|-----------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `${ARRAY[N]}` | Expands to the value of the index `N` in the **indexed** array `ARRAY`. If `N` is a negative number, it's treated as the offset from the maximum assigned index (can't be used for assignment) - 1 |
| `${ARRAY[S]}` | Expands to the value of the index `S` in the **associative** array `ARRAY`. |
| `"${ARRAY[@]}" ${ARRAY[@]} "${ARRAY[*]}" ${ARRAY[*]}` | Similar to [mass-expanding positional parameters](/scripting/posparams#mass_usage), this expands to all elements. If unquoted, both subscripts `*` and `@` expand to the same result, if quoted, `@` expands to all elements individually quoted, `*` expands to all elements quoted as a whole. |
| `"${ARRAY[@]:N:M}" ${ARRAY[@]:N:M} "${ARRAY[*]:N:M}" ${ARRAY[*]:N:M}` | Similar to what this syntax does for the characters of a single string when doing [substring expansion](/syntax/pe#substring_expansion), this expands to `M` elements starting with element `N`. This way you can mass-expand individual indexes. The rules for quoting and the subscripts `*` and `@` are the same as above for the other mass-expansions. |
For clarification: When you use the subscripts `@` or `*` for
mass-expanding, then the behaviour is exactly what it is for `$@` and
`$*` when [mass-expanding the positional
parameters](/scripting/posparams#mass_usage). You should read this
article to understand what's going on.
### Metadata
<table>
<thead>
<tr class="header">
<th>Syntax</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>${#ARRAY[N]}</code></td>
<td>Expands to the <strong>length</strong> of an individual array member
at index <code>N</code> (<strong>stringlength</strong>)</td>
</tr>
<tr class="even">
<td><code>${#ARRAY[STRING]}</code></td>
<td>Expands to the <strong>length</strong> of an individual associative
array member at index <code>STRING</code>
(<strong>stringlength</strong>)</td>
</tr>
<tr class="odd">
<td><code>${#ARRAY[@]}</code><br />
<code>${#ARRAY[*]}</code></td>
<td>Expands to the <strong>number of elements</strong> in
<code>ARRAY</code></td>
</tr>
<tr class="even">
<td><code>${!ARRAY[@]}</code><br />
<code>${!ARRAY[*]}</code></td>
<td>Expands to the <strong>indexes</strong> in <code>ARRAY</code> since
BASH 3.0</td>
</tr>
</tbody>
</table>
### Destruction
The [unset](commands/builtin/unset) builtin command is used to destroy
(unset) arrays or individual elements of arrays.
<table>
<thead>
<tr class="header">
<th>Syntax</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>unset -v ARRAY</code><br />
<code>unset -v ARRAY[@]</code><br />
<code>unset -v ARRAY[*]</code></td>
<td>Destroys a complete array</td>
</tr>
<tr class="even">
<td><code>unset -v ARRAY[N]</code></td>
<td>Destroys the array element at index <code>N</code></td>
</tr>
<tr class="odd">
<td><code>unset -v ARRAY[STRING]</code></td>
<td>Destroys the array element of the associative array at index
<code>STRING</code></td>
</tr>
</tbody>
</table>
It is best to [explicitly specify
-v](commands/builtin/unset#portability_considerations) when unsetting
variables with unset.
\<note warning\> Specifying unquoted array elements as arguments to any
command, such as with the syntax above **may cause [pathname
expansion](/syntax/expansion/globs) to occur** due to the presence of
glob characters.
Example: You are in a directory with a file named `x1`, and you want to
destroy an array element `x[1]`, with
unset x[1]
then pathname expansion will expand to the filename `x1` and break your
processing!
Even worse, if `nullglob` is set, your array/index will disappear.
To avoid this, **always quote** the array name and index:
unset -v 'x[1]'
This applies generally to all commands which take variable names as
arguments. Single quotes preferred. \</note\>
## Usage
### Numerical Index
Numerical indexed arrays are easy to understand and easy to use. The
[Purpose](#purpose) and [Indexing](#indexing) chapters above more or
less explain all the needed background theory.
Now, some examples and comments for you.
Let's say we have an array `sentence` which is initialized as follows:
sentence=(Be liberal in what you accept, and conservative in what you send)
Since no special code is there to prevent word splitting (no quotes),
every word there will be assigned to an individual array element. When
you count the words you see, you should get 12. Now let's see if Bash
has the same opinion:
$ echo ${#sentence[@]}
12
Yes, 12. Fine. You can take this number to walk through the array. Just
**subtract 1 from the number of elements, and start your walk at 0
(zero)**:
((n_elements=${#sentence[@]}, max_index=n_elements - 1))
for ((i = 0; i <= max_index; i++)); do
echo &quot;Element $i: '${sentence[i]}'&quot;
done
You always have to remember that, it seems newbies have problems
sometimes. Please understand that **numerical array indexing begins at 0
(zero)**!
The method above, walking through an array by just knowing its number of
elements, only works for arrays where all elements are set, of course.
If one element in the middle is removed, then the calculation is
nonsense, because the number of elements doesn't correspond to the
highest used index anymore (we call them "*sparse arrays*").
Now, suppose that you want to replace your array `sentence` with the
values in the [previously-declared array](#purpose) `NAMES` . You might
think you could just do
$ unset sentence ; declare -a sentence=NAMES
$ echo ${#sentence[@]}
1
# omit calculating max_index as above, and iterate as one-liner
$ for ((i = 0; i < ${#sentence[@]}; i++)); do echo &quot;Element $i: '${sentence[i]}'&quot; ; done
Element 0: 'NAMES'
Obviously that's wrong. What about
$ unset sentence ; declare -a sentence=${NAMES}
? Again, wrong:
$ echo ${#sentence[*]}
1
$ for ((i = 0; i < ${#sentence[@]}; i++)); do echo &quot;Element $i: '${sentence[i]}'&quot; ; done
Element 0: 'Peter'
So what's the **right** way? The (slightly ugly) answer is, reuse the
enumeration syntax:
$ unset sentence ; declare -a sentence=(&quot;${NAMES[@]}&quot;)
$ echo ${#sentence[@]}
4
$ for ((i = 0; i < ${#sentence[@]}; i++)); do echo &quot;Element $i: '${sentence[i]}'&quot; ; done
Element 0: 'Peter'
Element 1: 'Anna'
Element 2: 'Greg'
Element 3: 'Jan'
### Associative (Bash 4)
Associative arrays (or *hash tables*) are not much more complicated than
numerical indexed arrays. The numerical index value (in Bash a number
starting at zero) just is replaced with an arbitrary string:
# declare -A, introduced with Bash 4 to declare an associative array
declare -A sentence
sentence[Begin]='Be liberal in what'
sentence[Middle]='you accept, and conservative'
sentence[End]='in what you send'
sentence['Very end']=...
<u>**Beware:**</u> don't rely on the fact that the elements are ordered
in memory like they were declared, it could look like this:
# output from 'set' command
sentence=([End]=&quot;in what you send&quot; [Middle]=&quot;you accept, and conservative &quot; [Begin]=&quot;Be liberal in what &quot; [&quot;Very end&quot;]=&quot;...&quot;)
This effectively means, you can get the data back with
`"${sentence[@]}"`, of course (just like with numerical indexing), but
you can't rely on a specific order. If you want to store ordered data,
or re-order data, go with numerical indexes. For associative arrays, you
usually query known index values:
for element in Begin Middle End &quot;Very end&quot;; do
printf &quot;%s&quot; &quot;${sentence[$element]}&quot;
done
printf &quot;\n&quot;
**A nice code example:** Checking for duplicate files using an
associative array indexed with the SHA sum of the files:
# Thanks to Tramp in #bash for the idea and the code
unset flist; declare -A flist;
while read -r sum fname; do
if [[ ${flist[$sum]} ]]; then
printf 'rm -- &quot;%s&quot; # Same as >%s<\n' &quot;$fname&quot; &quot;${flist[$sum]}&quot;
else
flist[$sum]=&quot;$fname&quot;
fi
done < <(find . -type f -exec sha256sum {} +) >rmdups
### Integer arrays
Any type attributes applied to an array apply to all elements of the
array. If the integer attribute is set for either indexed or associative
arrays, then values are considered as arithmetic for both compound and
ordinary assignment, and the += operator is modified in the same way as
for ordinary integer variables.
~ $ ( declare -ia 'a=(2+4 [2]=2+2 [a[2]]=&quot;a[2]&quot;)' 'a+=(42 [a[4]]+=3)'; declare -p a )
declare -ai a='([0]=&quot;6&quot; [2]=&quot;4&quot; [4]=&quot;7&quot; [5]=&quot;42&quot;)'
`a[0]` is assigned to the result of `2+4`. `a[2]` gets the result of
`2+2`. The last index in the first assignment is the result of `a[2]`,
which has already been assigned as `4`, and its value is also given
`a[2]`.
This shows that even though any existing arrays named `a` in the current
scope have already been unset by using `=` instead of `+=` to the
compound assignment, arithmetic variables within keys can self-reference
any elements already assigned within the same compound-assignment. With
integer arrays this also applies to expressions to the right of the `=`.
(See [evaluation order](#evaluation_order), the right side of an
arithmetic assignment is typically evaluated first in Bash.)
The second compound assignment argument to declare uses `+=`, so it
appends after the last element of the existing array rather than
deleting it and creating a new array, so `a[5]` gets `42`.
Lastly, the element whose index is the value of `a[4]` (`4`), gets `3`
added to its existing value, making `a[4]` == `7`. Note that having the
integer attribute set this time causes += to add, rather than append a
string, as it would for a non-integer array.
The single quotes force the assignments to be evaluated in the
environment of `declare`. This is important because attributes are only
applied to the assignment after assignment arguments are processed.
Without them the `+=` compound assignment would have been invalid, and
strings would have been inserted into the integer array without
evaluating the arithmetic. A special-case of this is shown in the next
section.
\<note\> Bash declaration commands are really keywords in disguise. They
magically parse arguments to determine whether they are in the form of a
valid assignment. If so, they are evaluated as assignments. If not, they
are undergo normal argument expansion before being passed to the builtin
which evaluates the resulting string as an assignment (somewhat like
`eval`, but there are differences.) `'Todo:`' Discuss this in detail.
\</note\>
### Indirection
Arrays can be expanded indirectly using the indirect parameter expansion
syntax. Parameters whose values are of the form: `name[index]`,
`name[@]`, or `name[*]` when expanded indirectly produce the expected
results. This is mainly useful for passing arrays (especially multiple
arrays) by name to a function.
This example is an "isSubset"-like predicate which returns true if all
key-value pairs of the array given as the first argument to isSubset
correspond to a key-value of the array given as the second argument. It
demonstrates both indirect array expansion and indirect key-passing
without eval using the aforementioned special compound assignment
expansion.
isSubset() {
local -a 'xkeys=(&quot;${!'&quot;$1&quot;'[@]}&quot;)' 'ykeys=(&quot;${!'&quot;$2&quot;'[@]}&quot;)'
set -- &quot;${@/%/[key]}&quot;
(( ${#xkeys[@]} <= ${#ykeys[@]} )) || return 1
local key
for key in &quot;${xkeys[@]}&quot;; do
[[ ${!2+_} && ${!1} == ${!2} ]] || return 1
done
}
main() {
# &quot;a&quot; is a subset of &quot;b&quot;
local -a 'a=({0..5})' 'b=({0..10})'
isSubset a b
echo $? # true
# &quot;a&quot; contains a key not in &quot;b&quot;
local -a 'a=([5]=5 {6..11})' 'b=({0..10})'
isSubset a b
echo $? # false
# &quot;a&quot; contains an element whose value != the corresponding member of &quot;b&quot;
local -a 'a=([5]=5 6 8 9 10)' 'b=({0..10})'
isSubset a b
echo $? # false
}
main
This script is one way of implementing a crude multidimensional
associative array by storing array definitions in an array and
referencing them through indirection. The script takes two keys and
dynamically calls a function whose name is resolved from the array.
callFuncs() {
# Set up indirect references as positional parameters to minimize local name collisions.
set -- &quot;${@:1:3}&quot; ${2+'a[&quot;$1&quot;]' &quot;$1&quot;'[&quot;$2&quot;]'}
# The only way to test for set but null parameters is unfortunately to test each individually.
local x
for x; do
[[ $x ]] || return 0
done
local -A a=(
[foo]='([r]=f [s]=g [t]=h)'
[bar]='([u]=i [v]=j [w]=k)'
[baz]='([x]=l [y]=m [z]=n)'
) ${4+${a[&quot;$1&quot;]+&quot;${1}=${!3}&quot;}} # For example, if &quot;$1&quot; is &quot;bar&quot; then define a new array: bar=([u]=i [v]=j [w]=k)
${4+${a[&quot;$1&quot;]+&quot;${!4-:}&quot;}} # Now just lookup the new array. for inputs: &quot;bar&quot; &quot;v&quot;, the function named &quot;j&quot; will be called, which prints &quot;j&quot; to stdout.
}
main() {
# Define functions named {f..n} which just print their own names.
local fun='() { echo &quot;$FUNCNAME&quot;; }' x
for x in {f..n}; do
eval &quot;${x}${fun}&quot;
done
callFuncs &quot;$@&quot;
}
main &quot;$@&quot;
## Bugs and Portability Considerations
- Arrays are not specified by POSIX. One-dimensional indexed arrays are
supported using similar syntax and semantics by most Korn-like shells.
- Associative arrays are supported via `typeset -A` in Bash 4, Zsh, and
Ksh93.
- In Ksh93, arrays whose types are not given explicitly are not
necessarily indexed. Arrays defined using compound assignments which
specify subscripts are associative by default. In Bash, associative
arrays can *only* be created by explicitly declaring them as
associative, otherwise they are always indexed. In addition, ksh93 has
several other compound structures whose types can be determined by the
compound assignment syntax used to create them.
- In Ksh93, using the `=` compound assignment operator unsets the array,
including any attributes that have been set on the array prior to
assignment. In order to preserve attributes, you must use the `+=`
operator. However, declaring an associative array, then attempting an
`a=(...)` style compound assignment without specifying indexes is an
error. I can't explain this
inconsistency.` $ ksh -c 'function f { typeset -a a; a=([0]=foo [1]=bar); typeset -p a; }; f' # Attribute is lost, and since subscripts are given, we default to associative.
typeset -A a=([0]=foo [1]=bar)
$ ksh -c 'function f { typeset -a a; a+=([0]=foo [1]=bar); typeset -p a; }; f' # Now using += gives us the expected results.
typeset -a a=(foo bar)
$ ksh -c 'function f { typeset -A a; a=(foo bar); typeset -p a; }; f' # On top of that, the reverse does NOT unset the attribute. No idea why.
ksh: f: line 1: cannot append index array to associative array a
`
- Only Bash and mksh support compound assignment with mixed explicit
subscripts and automatically incrementing subscripts. In ksh93, in
order to specify individual subscripts within a compound assignment,
all subscripts must be given (or none). Zsh doesn't support specifying
individual subscripts at all.
- Appending to a compound assignment is a fairly portable way to append
elements after the last index of an array. In Bash, this also sets
append mode for all individual assignments within the compound
assignment, such that if a lower subscript is specified, subsequent
elements will be appended to previous values. In ksh93, it causes
subscripts to be ignored, forcing appending everything after the last
element. (Appending has different meaning due to support for
multi-dimensional arrays and nested compound datastructures.)
` $ ksh -c 'function f { typeset -a a; a+=(foo bar baz); a+=([3]=blah [0]=bork [1]=blarg [2]=zooj); typeset -p a; }; f' # ksh93 forces appending to the array, disregarding subscripts
typeset -a a=(foo bar baz '[3]=blah' '[0]=bork' '[1]=blarg' '[2]=zooj')
$ bash -c 'function f { typeset -a a; a+=(foo bar baz); a+=(blah [0]=bork blarg zooj); typeset -p a; }; f' # Bash applies += to every individual subscript.
declare -a a='([0]=&quot;foobork&quot; [1]=&quot;barblarg&quot; [2]=&quot;bazzooj&quot; [3]=&quot;blah&quot;)'
$ mksh -c 'function f { typeset -a a; a+=(foo bar baz); a+=(blah [0]=bork blarg zooj); typeset -p a; }; f' # Mksh does like Bash, but clobbers previous values rather than appending.
set -A a
typeset a[0]=bork
typeset a[1]=blarg
typeset a[2]=zooj
typeset a[3]=blah
`
- In Bash and Zsh, the alternate value assignment parameter expansion
(`${arr[idx]:=foo}`) evaluates the subscript twice, first to determine
whether to expand the alternate, and second to determine the index to
assign the alternate to. See [evaluation order](#evaluation_order).
` $ : ${_[$(echo $RANDOM >&2)1]:=$(echo hi >&2)}
13574
hi
14485
`
- In Zsh, arrays are indexed starting at 1 in its default mode.
Emulation modes are required in order to get any kind of portability.
- Zsh and mksh do not support compound assignment arguments to
`typeset`.
- Ksh88 didn't support modern compound array assignment syntax. The
original (and most portable) way to assign multiple elements is to use
the `set -A name arg1 arg2 ...` syntax. This is supported by almost
all shells that support ksh-like arrays except for Bash. Additionally,
these shells usually support an optional `-s` argument to `set` which
performs lexicographic sorting on either array elements or the
positional parameters. Bash has no built-in sorting ability other than
the usual comparison operators.
` $ ksh -c 'set -A arr -- foo bar bork baz; typeset -p arr' # Classic array assignment syntax
typeset -a arr=(foo bar bork baz)
$ ksh -c 'set -sA arr -- foo bar bork baz; typeset -p arr' # Native sorting!
typeset -a arr=(bar baz bork foo)
$ mksh -c 'set -sA arr -- foo &quot;[3]=bar&quot; &quot;[2]=baz&quot; &quot;[7]=bork&quot;; typeset -p arr' # Probably a bug. I think the maintainer is aware of it.
set -A arr
typeset arr[2]=baz
typeset arr[3]=bar
typeset arr[7]=bork
typeset arr[8]=foo
`
- Evaluation order for assignments involving arrays varies significantly
depending on context. Notably, the order of evaluating the subscript
or the value first can change in almost every shell for both
expansions and arithmetic variables. See [evaluation
order](#evaluation_order) for details.
- Bash 4.1.\* and below cannot use negative subscripts to address array
indexes relative to the highest-numbered index. You must use the
subscript expansion, i.e. `"${arr[@]:(-n):1}"`, to expand the nth-last
element (or the next-highest indexed after `n` if `arr[n]` is unset).
In Bash 4.2, you may expand (but not assign to) a negative index. In
Bash 4.3, ksh93, and zsh, you may both assign and expand negative
offsets.
- ksh93 also has an additional slice notation: `"${arr[n..m]}"` where
`n` and `m` are arithmetic expressions. These are needed for use with
multi-dimensional arrays.
- Assigning or referencing negative indexes in mksh causes wrap-around.
The max index appears to be `UINT_MAX`, which would be addressed by
`arr[-1]`.
- So far, Bash's `-v var` test doesn't support individual array
subscripts. You may supply an array name to test whether an array is
defined, but can't check an element. ksh93's `-v` supports both. Other
shells lack a `-v` test.
### Bugs
- **Fixed in 4.3** Bash 4.2.\* and earlier considers each chunk of a
compound assignment, including the subscript for globbing. The
subscript part is considered quoted, but any unquoted glob characters
on the right-hand side of the `[...]=` will be clumped with the
subscript and counted as a glob. Therefore, you must quote anything on
the right of the `=` sign. This is fixed in 4.3, so that each
subscript assignment statement is expanded following the same rules as
an ordinary assignment. This also works correctly in ksh93.
`$ touch '[1]=a'; bash -c 'a=([1]=*); echo &quot;${a[@]}&quot;'
[1]=a
` mksh has a similar but even worse problem in that the entire
subscript is considered a glob.
`$ touch 1=a; mksh -c 'a=([123]=*); print -r -- &quot;${a[@]}&quot;'
1=a
`
- **Fixed in 4.3** In addition to the above globbing issue, assignments
preceding "declare" have an additional effect on brace and pathname
expansion. `$ set -x; foo=bar declare arr=( {1..10} )
+ foo=bar
+ declare 'arr=(1)' 'arr=(2)' 'arr=(3)' 'arr=(4)' 'arr=(5)' 'arr=(6)' 'arr=(7)' 'arr=(8)' 'arr=(9)' 'arr=(10)'
$ touch xy=foo
+ touch xy=foo
$ declare x[y]=*
+ declare 'x[y]=*'
$ foo=bar declare x[y]=*
+ foo=bar
+ declare xy=foo
` Each word (the entire assignment) is subject to globbing and brace
expansion. This appears to trigger the same strange expansion mode as
`let`, `eval`, other declaration commands, and maybe more.
- **Fixed in 4.3** Indirection combined with another modifier expands
arrays to a single word.
`$ a=({a..c}) b=a[@]; printf '<%s> ' &quot;${!b}&quot;; echo; printf '<%s> ' &quot;${!b/%/foo}&quot;; echo
<a> <b> <c>
<a b cfoo>
`
- **Fixed in 4.3** Process substitutions are evaluated within array
indexes. Zsh and ksh don't do this in any arithmetic context.
`# print &quot;moo&quot;
dev=fd=1 _[1<(echo moo >&2)]=
# Fork bomb
${dev[${dev='dev[1>(${dev[dev]})]'}]}
`
### Evaluation order
Here are some of the nasty details of array assignment evaluation order.
You can use this [testcase code](https://gist.github.com/ormaaj/4942297)
to generate these results.
Each testcase prints evaluation order for indexed array assignment
contexts. Each context is tested for expansions (represented by digits) and
arithmetic (letters), ordered from left to right within the expression. The
output corresponds to the way evaluation is re-ordered for each shell:
a[ $1 a ]=${b[ $2 b ]:=${c[ $3 c ]}} No attributes
a[ $1 a ]=${b[ $2 b ]:=c[ $3 c ]} typeset -ia a
a[ $1 a ]=${b[ $2 b ]:=c[ $3 c ]} typeset -ia b
a[ $1 a ]=${b[ $2 b ]:=c[ $3 c ]} typeset -ia a b
(( a[ $1 a ] = b[ $2 b ] ${c[ $3 c ]} )) No attributes
(( a[ $1 a ] = ${b[ $2 b ]:=c[ $3 c ]} )) typeset -ia b
a+=( [ $1 a ]=${b[ $2 b ]:=${c[ $3 c ]}} [ $4 d ]=$(( $5 e )) ) typeset -a a
a+=( [ $1 a ]=${b[ $2 b ]:=c[ $3 c ]} [ $4 d ]=${5}e ) typeset -ia a
bash: 4.2.42(1)-release
2 b 3 c 2 b 1 a
2 b 3 2 b 1 a c
2 b 3 2 b c 1 a
2 b 3 2 b c 1 a c
1 2 3 c b a
1 2 b 3 2 b c c a
1 2 b 3 c 2 b 4 5 e a d
1 2 b 3 2 b 4 5 a c d e
ksh93: Version AJM 93v- 2013-02-22
1 2 b b a
1 2 b b a
1 2 b b a
1 2 b b a
1 2 3 c b a
1 2 b b a
1 2 b b a 4 5 e d
1 2 b b a 4 5 d e
mksh: @(#)MIRBSD KSH R44 2013/02/24
2 b 3 c 1 a
2 b 3 1 a c
2 b 3 c 1 a
2 b 3 c 1 a
1 2 3 c a b
1 2 b 3 c a
1 2 b 3 c 4 5 e a d
1 2 b 3 4 5 a c d e
zsh: 5.0.2
2 b 3 c 2 b 1 a
2 b 3 2 b 1 a c
2 b 1 a
2 b 1 a
1 2 3 c b a
1 2 b a
1 2 b 3 c 2 b 4 5 e
1 2 b 3 2 b 4 5
## See also
- [Parameter expansion](/syntax/pe) (contains sections for arrays)
- [classic_for](/syntax/ccmd/classic_for) (contains some examples to
iterate over arrays)
- [declare](/commands/builtin/declare)
- [BashFAQ 005 - How can I use array
variables?](http://mywiki.wooledge.org/BashFAQ/005) - A very detailed
discussion on arrays with many examples.
- [BashSheet - Arrays](http://mywiki.wooledge.org/BashSheet#Arrays) -
Bashsheet quick-reference on Greycat's wiki.

329
syntax/basicgrammar.md Normal file
View File

@ -0,0 +1,329 @@
# 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 &quot;No root user defined... eh?&quot;
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 &quot;Sorry, no help available&quot;; }
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 &quot;$1&quot;
}
# this $1 belongs the script itself!
mycmd &quot;$1&quot; # 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=&quot;() { echo test; }&quot;
$ 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 &quot;Hello world...&quot;
<u>All of the following are simple commands</u>
x=5
>tmpfile
{x}<&quot;$x&quot; _=${x=<(echo moo)} <&0$(cat <&&quot;$x&quot; >&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)

View File

@ -0,0 +1,30 @@
# Arithmetic evaluation (command)
## Synopsis
(( <EXPRESSION> ))
## Description
This command evaluates the [arithmetic expression](/syntax/arith_expr)
`<EXPRESSION>`.
If the expression evaluates to 0 then the exit code of the expression is
set to 1 (`FALSE`). If the expression evaluates to something else than
0, then the exit code of the expression is set to 0 (`TRUE`). For this
return code mapping, please see [this
section](/syntax/arith_expr#arithmetic_expressions_and_return_codes).
The functionality basically is equivalent to what the [`let` builtin
command](/commands/builtin/let) does. The arithmetic evaluation compound
command should be preferred.
## Examples
## Portability considerations
## See also
- Internal: [arithmetic expressions](/syntax/arith_expr)
- Internal: [arithmetic expansion](/syntax/expansion/arith)
- Internal: [The `let` builtin command](/commands/builtin/let)

236
syntax/ccmd/c_for.md Normal file
View File

@ -0,0 +1,236 @@
# The C-style for-loop
## Synopsis
for (( <EXPR1> ; <EXPR2> ; <EXPR3> )); do
<LIST>
done
# as a special case: without semicolon after ((...))
for (( <EXPR1> ; <EXPR2> ; <EXPR3> )) do
<LIST>
done
# alternative, historical and undocumented syntax
for (( <EXPR1> ; <EXPR2> ; <EXPR3> )) {
<LIST>
}
## Description
The C-style for-loop is a [compound
command](syntax/basicgrammar#compound_commands) derived from the
equivalent ksh88 feature, which is in turn derived from the C "for"
keyword. Its purpose is to provide a convenient way to evaluate
arithmetic expressions in a loop, plus initialize any required
arithmetic variables. It is one of the main "loop with a counter"
mechanisms available in the language.
The `((;;))` syntax at the top of the loop is not an ordinary
[arithmetic compound command](syntax/ccmd/arithmetic_eval), but is part
of the C-style for-loop's own syntax. The three sections separated by
semicolons are [arithmetic expression](/syntax/arith_expr) contexts.
Each time one of the sections is to be evaluated, the section is first
processed for: brace, parameter, command, arithmetic, and process
substitution/expansion as usual for arithmetic contexts. When the loop
is entered for the first time, `<EXPR1>` is evaluated, then `<EXPR2>` is
evaluated and checked. If `<EXPR2>` is true, then the loop body is
executed. After the first and all subsequent iterations, `<EXPR1>` is
skipped, `<EXPR3>` is evaluated, then `<EXPR2>` is evaluated and checked
again. This process continues until `<EXPR2>` is false.
- `<EXPR1>` is to **initialize variables** before the first run.
- `<EXPR2>` is to **check** for a termination condition. This is always
the last section to evaluate prior to leaving the loop.
- `<EXPR3>` is to **change** conditions after every iteration. For
example, incrementing a counter.
:!: If one of these arithmetic expressions in the for-loop is empty, it
behaves as if it would be 1 (**TRUE** in arithmetic context).
:!: Like all loops (Both types of `for`-loop, `while` and `until`), this
loop can be:
- Terminated (broken) by the [break](commands/builtin/continuebreak)
builtin, optionally as `break N` to break out of `N` levels of nested
loops.
- Forced immediately to the next iteration using the
[continue](commands/builtin/continuebreak) builtin, optionally as the
`continue N` analog to `break N`.
The equivalent construct using a [while loop](syntax/ccmd/while_loop)
and the [arithmetic expression compound
command](/syntax/ccmd/arithmetic_eval) would be structured as:
(( <EXPR1> ))
while (( <EXPR2> )); do
<LIST>
(( <EXPR3> ))
done
The equivalent `while` construct isn't exactly the same, because both,
the `for` and the `while` loop behave differently in case you use the
[continue](commands/builtin/continuebreak) command.
### Alternate syntax
Bash, Ksh93, Mksh, and Zsh also provide an alternate syntax for the
`for` loop - enclosing the loop body in `{...}` instead of
`do ... done`:
for ((x=1; x<=3; x++))
{
echo $x
}
This syntax is **not documented** and shouldn't be used. I found the
parser definitions for it in 1.x code, and in modern 4.x code. My guess
is that it's there for compatibility reasons. Unlike the other
aforementioned shells, Bash does not support the analogous syntax for
[case..esac](syntax/ccmd/case#portability_considerations).
### Return status
The return status is that of the last command executed from `<LIST>`, or
`FALSE` if any of the arithmetic expressions failed.
## Alternatives and best practice
\<div center round todo 60%\>TODO: Show some alternate usages involving
functions and local variables for initialization.\</div\>
## Examples
### Simple counter
A simple counter, the loop iterates 101 times ("0" to "100" are 101
numbers -\> 101 runs!), and everytime the variable `x` is set to the
current value.
- It **initializes** `x = 0`
- Before every iteration it **checks** if `x ≤ 100`
- After every iteration it **changes** `x++`
<!-- -->
for ((x = 0 ; x <= 100 ; x++)); do
echo &quot;Counter: $x&quot;
done
### Stepping counter
This is the very same counter (compare it to the simple counter example
above), but the **change** that is made is a `x += 10`. That means, it
will count from 0 to 100, but with a **step of 10**.
for ((x = 0 ; x <= 100 ; x += 10)); do
echo &quot;Counter: $x&quot;
done
### Bits analyzer
This example loops through the bit-values of a Byte, beginning from 128,
ending at 1. If that bit is set in the `testbyte`, it prints "`1`", else
"`0`" =\> it prints the binary representation of the `testbyte` value (8
bits).
#!/usr/bin/env bash
# Example written for http://wiki.bash-hackers.org/syntax/ccmd/c_for#bits_analyzer
# Based on TheBonsai's original.
function toBin {
typeset m=$1 n=2 x='x[(n*=2)>m]'
for ((x = x; n /= 2;)); do
printf %d $(( m & n && 1))
done
}
function main {
[[ $1 == +([0-9]) ]] || return
typeset result
if (( $(ksh -c 'printf %..2d $1' _ &quot;$1&quot;) == ( result = $(toBin &quot;$1&quot;) ) )); then
printf '%s is %s in base 2!\n' &quot;$1&quot; &quot;$result&quot;
else
echo 'Oops, something went wrong with our calculation.' >&2
exit 1
fi
}
main &quot;${1:-123}&quot;
# vim: set fenc=utf-8 ff=unix ft=sh :
\<div hide\>
testbyte=123
for (( n = 128 ; n >= 1 ; n /= 2 )); do
if (( testbyte & n )); then
printf %d 1
else
printf %s 0
fi
done
echo
\</div\>
Why that one begins at 128 (highest value, on the left) and not 1
(lowest value, on the right)? It's easier to print from left to right...
We arrive at 128 for `n` through the recursive arithmetic expression
stored in `x`, which calculates the next-greatest power of 2 after `m`.
To show that it works, we use ksh93 to double-check the answer, because
it has a built-in feature for `printf` to print a representation of any
number in an arbitrary base (up to 64). Very few languages have that
ability built-in, even things like Python.
### Up, down, up, down...
This counts up and down from `0` to `${1:-5}`, `${2:-4}` times,
demonstrating more complicated arithmetic expressions with multiple
variables.
for (( incr = 1, n=0, times = ${2:-4}, step = ${1:-5}; (n += incr) % step || (incr *= -1, --times);)); do
printf '%*s\n' &quot;$((n+1))&quot; &quot;$n&quot;
done
\<code\> ~ \$ bash \<(xclip -o) 1
2
3
4
5
4
3
2
1 0 1
2
3
4
5
4
3
2
1 \</code\>
## Portability considerations
- C-style for loops aren't POSIX. They are available in Bash, ksh93, and
zsh. All 3 have essentially the same syntax and behavior.
- C-style for loops aren't available in mksh.
## Bugs
- *Fixed in 4.3*. ~~There appears to be a bug as of Bash 4.2p10 in which
command lists can't be distinguished from the for loop's arithmetic
argument delimiter (both semicolons), so command substitutions within
the C-style for loop expression can't contain more than one command.~~
## See also
- Internal: [Arithmetic expressions](/syntax/arith_expr)
- Internal: [The classic for-loop](/syntax/ccmd/classic_for)
- Internal: [The while-loop](/syntax/ccmd/while_loop)

161
syntax/ccmd/case.md Normal file
View File

@ -0,0 +1,161 @@
# The case statement
## Synopsis
case <WORD> in
[(] <PATTERN1> ) <LIST1> ;; # or ;& or ;;& in Bash 4
[(] <PATTERN2> ) <LIST2> ;;
[(] <PATTERN3> | <PATTERN4> ) <LIST3-4> ;;
...
[(] <PATTERNn>) <LISTn> [;;]
esac
## Description
The `case`-statement can execute commands based on a [pattern
matching](/syntax/pattern) decision. The word `<WORD>` is matched
against every pattern `<PATTERNn>` and on a match, the associated
[list](/syntax/basicgrammar#lists) `<LISTn>` is executed. Every
commandlist is terminated by `;;`. This rule is optional for the very
last commandlist (i.e., you can omit the `;;` before the `esac`). Every
`<PATTERNn>` is separated from it's associated `<LISTn>` by a `)`, and
is optionally preceded by a `(`.
Bash 4 introduces two new action terminators. The classic behavior using
`;;` is to execute only the list associated with the first matching
pattern, then break out of the `case` block. The `;&` terminator causes
`case` to also execute the next block without testing its pattern. The
`;;&` operator is like `;;`, except the case statement doesn't terminate
after executing the associated list - Bash just continues testing the
next pattern as though the previous pattern didn't match. Using these
terminators, a `case` statement can be configured to test against all
patterns, or to share code between blocks, for example.
The word `<WORD>` is expanded using *tilde*, *parameter* and *variable
expansion*; *arithmetic*, *command* and *process substitution*; and
*quote removal*. **No word splitting, brace, or pathname expansion is
done**, which means you can leave expansions unquoted without problems:
var=&quot;test word&quot;
case $var in
...
esac
This is similar to the behavior of the [conditional expression command
("new test command")](/syntax/ccmd/conditional_expression) (also no word
splitting for expansions).
Unlike the C-case-statement, only the matching list and nothing else is
executed. If more patterns match the word, only the first match is
taken. (**Note** the comment about Bash v4 changes above.)
Multiple `|`-delimited patterns can be specified for a single block.
This is a POSIX-compatable equivalent to the `@(pattern-list)` extglob
construct.
The `case` statement is one of the most difficult commands to indent
clearly, and people frequently ask about the most "correct" style. Just
do your best - there are many variations of indenting style for `case`
and no real agreed-upon best practice.
## Examples
Another one of my stupid examples...
printf '%s ' 'Which fruit do you like most?'
read -${BASH_VERSION+e}r fruit
case $fruit in
apple)
echo 'Mmmmh... I like those!'
;;
banana)
echo 'Hm, a bit awry, no?'
;;
orange|tangerine)
echo $'Eeeks! I don\'t like those!\nGo away!'
exit 1
;;
*)
echo &quot;Unknown fruit - sure it isn't toxic?&quot;
esac
Here's a practical example showing a common pattern involving a `case`
statement. If the first argument is one of a valid set of alternatives,
then perform some sysfs operations under Linux to control a video card's
power profile. Otherwise, show a usage synopsis, and print the current
power profile and GPU temperature.
``` bash
# Set radeon power management
function clk {
typeset base=/sys/class/drm/card0/device
[[ -r ${base}/hwmon/hwmon0/temp1_input && -r ${base}/power_profile ]] || return 1
case $1 in
low|high|default)
printf '%s\n' &quot;temp: $(<${base}/hwmon/hwmon0/temp1_input)C&quot; &quot;old profile: $(<${base}/power_profile)&quot;
echo &quot;$1&quot; >${base}/power_profile
echo &quot;new profile: $(<${base}/power_profile)&quot;
;;
*)
echo &quot;Usage: $FUNCNAME [ low | high | default ]&quot;
printf '%s\n' &quot;temp: $(<${base}/hwmon/hwmon0/temp1_input)C&quot; &quot;current profile: $(<${base}/power_profile)&quot;
esac
}
```
A template for experiments with `case` logic, showing shared code
between blocks using `;&`, and the non-short-circuiting `;;&` operator:
``` bash
#!/usr/bin/env bash
f() {
local -a &quot;$@&quot;
local x
for x; do
case $x in
$1)
local &quot;$x&quot;'+=(1)' ;;&
$2)
local &quot;$x&quot;'+=(2)' ;&
$3)
local &quot;$x&quot;'+=(3)' ;;
$1|$2)
local &quot;$x&quot;'+=(4)'
esac
IFS=, local -a &quot;$x&quot;'=(&quot;${x}: ${'&quot;$x&quot;'[*]}&quot;)'
done
for x; do
echo &quot;${!x}&quot;
done
}
f a b c
# output:
# a: 1,4
# b: 2,3
# c: 3
```
## Portability considerations
- Only the `;;` delimiter is specified by POSIX.
- zsh and mksh use the `;|` control operator instead of Bash's `;;&`.
Mksh has `;;&` for Bash compatability (undocumented).
- ksh93 has the `;&` operator, but no `;;&` or equivalent.
- ksh93, mksh, zsh, and posh support a historical syntax where open and
close braces may be used in place of `in` and `esac`:
`case word { x) ...; };`. This is similar to the alternate form Bash
supports for its [for loops](syntax/ccmd/classic_for), but Bash
doesn't support this syntax for `case..esac`.
## See also
- [POSIX case conditional
construct](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_09_04_05)

186
syntax/ccmd/classic_for.md Normal file
View File

@ -0,0 +1,186 @@
# The classic for-loop
## Synopsis
for <NAME>; do
<LIST>
done
for <NAME> in <WORDS>; do
<LIST>
done
alternative, historical and undocumented syntax [^1]
for <NAME>; {
<LIST>
}
for <NAME> in <WORDS>; {
<LIST>
}
## Description
For every word in `<WORDS>`, one iteration of the loop is performed and
the variable `<NAME>` is set to the current word. If no "`in <WORDS>`"
is present to give an own word-list, then the positional parameters
(`"$@"`) are used (the arguments to the script or function). In this
case (and only in this case), the semicolon between the variable name
and the `do` is optional.
If you use the loop-variable inside the for-loop and it can contain
spaces, you need to quote it, since normal word-splitting procedures
apply.
:!: Like all loops (both `for`-loops, `while` and `until`), this loop
can be
- terminated (broken) by the `break` command, optionally as `break N` to
break `N` levels of nested loops
- forced to immediately do the next iteration using the `continue`
command, optionally as `continue N` analog to `break N`
Bash knows an alternative syntax for the `for` loop, enclosing the loop
body in `{...}` instead of `do ... done`:
``` bash
for x in 1 2 3
{
echo $x
}
```
This syntax is **not documented** and should not be used. I found the
parser definitions for it in 1.x code, and in modern 4.x code. My guess
is that it's there for compatiblity reasons. This syntax is not
specified by POSIX(r).
### Return status
The return status is the one of the last command executed in `<LIST>` or
`0` (`TRUE`), if the item list `<WORDS>` evaluates to nothing (i.e.: "is
empty"!).
## Examples
### Iterate over array elements
With some array syntax (see [arrays](/syntax/arrays)) you can easily
"feed" the for-loop to iterate over all elements in an array (by
mass-expanding all elements):
``` bash
for element in &quot;${myarray[@]}&quot;; do
echo &quot;Element: $element&quot;
done
```
Another way is to mass-expand all used indexes and access the array by
index:
``` bash
for index in &quot;${!myarray[@]}&quot;; do
echo &quot;Element[$index]: ${myarray[$index]}&quot;
done
```
### List positional parameters
You can use this
[function](/syntax/basicgrammar#shell_function_definitions) to test how
arguments to a command will be interpreted and parsed, and finally used:
``` bash
argtest() {
n=1
for arg; do
echo &quot;Argument $((n++)): \&quot;$arg\&quot;&quot;
done
}
```
### Loop through a directory
Since pathname expansion will expand all filenames to separate words,
regardless of spaces, you can use the for-loop to iterate through
filenames in a directory:
``` bash
for fn in *; do
if [ -h &quot;$fn&quot; ]; then
echo -n &quot;Symlink: &quot;
elif [ -d &quot;$fn&quot; ]; then
echo -n &quot;Dir: &quot;
elif [ -f &quot;$fn&quot; ]; then
echo -n &quot;File: &quot;
else
echo -n &quot;Unknown: &quot;
fi
echo &quot;$fn&quot;
done
```
Stupid example, I know ;-)
### Loop over lines of output
To be complete: You can change the internal field separator (IFS) to a
newline and thus make a for-loop iterating over lines instead of words:
``` bash
IFS=$'\n'
for f in $(ls); do
echo $f
done
```
This is just an example. In *general*
- it's not a good idea to parse `ls(1)` output
- the [while loop](/syntax/ccmd/while_loop) (using the `read` command)
is a better joice to iterate over lines
### Nested for-loops
It's of course possible to use another for-loop as `<LIST>`. Here,
counting from 0 to 99 in a weird way:
``` bash
for x in 0 1 2 3 4 5 6 7 8 9; do
for y in 0 1 2 3 4 5 6 7 8 9; do
echo $x$y
done
done
```
### Loop over a number range
Beginning in Bash 4, you can also use "sequence expression" form of
[brace expansion](/syntax/expansion/brace) syntax when looping over
numbers, and this form does not create leading zeroes unless you ask for
them:
``` bash
# 100 numbers, no leading zeroes
for x in {0..99}; do
echo $x
done
```
``` bash
# Every other number, width 3
for x in {000..99..2}; do
echo $x
done
```
WARNING: the entire list is created before looping starts. If your list
is huge this may be an issue, but no more so than for a glob that
expands to a huge list.
## Portability considerations
## See also
[^1]: <http://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xcu_chap02.html#tag_23_02_09_12>

View File

@ -0,0 +1,71 @@
# Grouping commands
## Synopsis
{ <LIST>; }
{
<LIST>
}
## Description
The [list](/syntax/basicgrammar#lists) `<LIST>` is simply executed in
the **current** shell environment. The list must be terminated with a
**newline** or **semicolon**. For parsing reasons, the curly braces must
be separated from `<LIST>` by a **semicolon** and **blanks** if they're
in the same line! [^1][^2]
This is known as a **group command**. The return status is the [exit
status (exit code)](/scripting/basics#exit_codes) of the list.
The input and output **filedescriptors** are cumulative:
{
echo &quot;PASSWD follows&quot;
cat /etc/passwd
echo
echo &quot;GROUPS follows&quot;
cat /etc/group
} >output.txt
This compound command also usually is the body of a [function
definition](/syntax/basicgrammar#shell_function_definitions), though not
the only compound command that's valid there:
print_help() {
echo &quot;Options:&quot;
echo &quot;-h This help text&quot;
echo &quot;-f FILE Use config file FILE&quot;
echo &quot;-u USER Run as user USER&quot;
}
## Examples
### A Try-Catch block
try_catch() {
{ # Try-block:
eval &quot;$@&quot;
} ||
{ # Catch-block:
echo &quot;An error occurred&quot;
return -1
}
}
## Portability considerations
## See also
* [[syntax:ccmd:grouping_subshell | grouping commands in a subshell]]
[^1]: Actually any properly terminated compound command will work
without extra separator (also in some other shells), **example**:
`{ while sleep 1; do echo ZzZzzZ; done }` is valid. But this is not
documented, infact the documentation explicitly says that a
semicolon or a newline must separate the enclosed list. -- thanks
`geirha` at Freenode
[^2]: The main reason is the fact that in shell grammar, the curly
braces are not control operators but reserved words -- TheBonsai

View File

@ -0,0 +1,35 @@
# Grouping commands in a subshell
## Synopsis
( <LIST> )
## Description
The [list](/syntax/basicgrammar#lists) `<LIST>` is executed in a
separate shell - a subprocess. No changes to the environment (variables
etc...) are reflected in the "main shell".
## Examples
Execute a command in a different directory.
``` bash
echo &quot;$PWD&quot;
( cd /usr; echo &quot;$PWD&quot; )
echo &quot;$PWD&quot; # Still in the original directory.
```
## Portability considerations
- The subshell compound command is specified by POSIX.
- Avoid ambiguous syntax.
``` bash
(((1+1))) # Equivalent to: (( (1+1) ))
```
## See also
- [grouping commands](/syntax/ccmd/grouping_plain)
- [Subshells on Greycat's wiki](http://mywiki.wooledge.org/SubShell)

91
syntax/ccmd/if_clause.md Normal file
View File

@ -0,0 +1,91 @@
# The if-clause
## Synopsis
if <LIST>; then
<LIST>
fi
if <LIST>; then
<LIST>
else
<LIST>
fi
if <LIST>; then
<LIST>
elif <LIST>; then
<LIST>
else
<LIST>
fi
## Description
The `if`-clause can control the script's flow (what's executed) by
looking at the exit codes of other commands.
All commandsets `<LIST>` are interpreted as [command
lists](/syntax/basicgrammar#lists), thus they can contain the whole
palette from [simple commands](/syntax/basicgrammar#simple_commands)
over [pipelines](/syntax/basicgrammar#pipelines) to [compound
commands](/syntax/basicgrammar#compound_commands) (and their
combination) as condition.
### Operation
The **`if <LIST>`** commands are executed. If the exit code was 0 (TRUE)
then the **`then <LIST>`** commands are executed, otherwise the
**`elif <LIST>`** commands and their **`then <LIST>`** statements are
executed in turn, if all down to the last one fails, the
**`else <LIST>`** commands are executed, if one of the `elif` succeeds,
its `then` thread is executed, and the `if`-clause finishes.
Basically, the `elif` clauses are just additional conditions to test
(like a chain of conditions) if the very first condition failed. If one
of the conditions fails, the `else` commands are executed, otherwise the
commands of the condition that succeeded.
## Examples
**Check if a specific user exists in /etc/passwd :-)**
if grep ^myuser: /etc/passwd >/dev/null 2>&1; then
echo &quot;Yes, it seems I'm real&quot;
else
echo &quot;Uh - am I a ghost?&quot;
fi
**Mount with check**
if ! mount /mnt/backup >/dev/null 2>&1; then
echo &quot;FATAL: backup mount failed&quot; >&2
exit 1
fi
**Multiple commands as condition**
It's perfectly valid to do:
if echo &quot;I'm testing!&quot;; [ -e /some/file ]; then
...
fi
The exit code that dictates the condition's value is the exit code of
the very last command executed in the condition-list (here: The
`[ -e /some/file ]`)
**A complete pipe as condition**
A complete pipe can also be used as condition. It's very similar to the
example above (multiple commands):
if echo &quot;Hello world!&quot; | grep -i hello >/dev/null 2>&1; then
echo &quot;You just said 'hello', yeah?&quot;
fi
## Portability considerations
## See also
- Internal: [the classic test command](/commands/classictest)

34
syntax/ccmd/intro.md Normal file
View File

@ -0,0 +1,34 @@
# Bash compound commands
The main part of Bash's syntax are the so-called **compound commands**.
They're called like that because they use "real" commands ([simple
commands](/syntax/basicgrammar#simple_commands) or
[lists](/syntax/basicgrammar#lists)) and knit some intelligence around
them. That is what the essential "Bash language" is made of.
## Command grouping
- grouping: [command grouping](grouping_plain)
- grouping again: [command grouping in a subshell](grouping_subshell)
## Conditional reactions
Note that conditionals can also be scripted using
[list](/syntax/basicgrammar#lists), which are syntax elements, not
commands.
- the "new" test command: [conditional
expression](conditional_expression)
- if-clause: [conditional branching](if_clause)
- case statement: [pattern-based branching](case)
## Loops
- [classic for-loop](classic_for)
- [C-style for-loop](c_for)
- [while loop](while_loop)
- [until loop](until_loop)
## Misc
- math: [arithmetic evaluation](arithmetic_eval)

38
syntax/ccmd/until_loop.md Normal file
View File

@ -0,0 +1,38 @@
# The until loop
## Synopsis
until <LIST1> ; do
<LIST2>
done
## Description
The until-loop is relatively simple in what it does: it executes the
[command list](/syntax/basicgrammar#lists) `<LIST1>` and if the exit
code of it was **not** 0 (FALSE) it executes `<LIST2>`. This happens
again and again until `<LIST1>` returns TRUE.
This is exactly the opposite of the [while
loop](/syntax/ccmd/while_loop).
:!: Like all loops (both `for`-loops, `while` and `until`), this loop
can be
- terminated (broken) by the `break` command, optionally as `break N` to
break `N` levels of nested loops
- forced to immediately do the next iteration using the `continue`
command, optionally as `continue N` analog to `break N`
### Return status
The return status is the one of the last command executed in `<LIST2>`,
or `0` (`TRUE`) if none was executed.
## Examples
## Portability considerations
## See also
- Internal: [The while loop](/syntax/ccmd/while_loop)

View File

@ -0,0 +1,56 @@
# User selections
## Synopsis
select <NAME>; do
<LIST>
done
select <NAME> in <WORDS>; do
<LIST>
done
# alternative, historical and undocumented syntax
select <NAME>
{
<LIST>
}
select <NAME> in <WORDS>
{
<LIST>
}
## Description
This compound command provides a kind of menu. The user is prompted with
a *numbered list* of the given words, and is asked to input the index
number of the word. If a word was selected, the variable `<NAME>` is set
to this word, and the [list](/syntax/basicgrammar#lists) `<LIST>` is
executed.
If no `in <WORDS>` is given, then the positional parameters are taken as
words (as if `in "$@"` was written).
Regardless of the functionality, the *number* the user entered is saved
in the variable `REPLY`.
Bash knows an alternative syntax for the `select` command, enclosing the
loop body in `{...}` instead of `do ... done`:
select x in 1 2 3
{
echo $x
}
This syntax is **not documented** and should not be used. I found the
parser definitions for it in 1.x code, and in modern 4.x code. My guess
is that it's there for compatiblity reasons. This syntax is not
specified by POSIX(R).
## Examples
## Portability considerations
## See also

41
syntax/ccmd/while_loop.md Normal file
View File

@ -0,0 +1,41 @@
# The while-loop
## Synopsis
while <LIST1> ; do
<LIST2>
done
## Description
The while-loop is relatively simple in what it does: it executes the
[command list](/syntax/basicgrammar#lists) `<LIST1>` and if the exit
code of it was 0 (TRUE) it executes `<LIST2>`. This happens again and
again until `<LIST1>` returns FALSE.
This is exactly the opposite of the [until
loop](/syntax/ccmd/until_loop).
:!: Like all loops (both `for`-loops, `while` and `until`), this loop
can be
- terminated (broken) by the `break` command, optionally as `break N` to
break `N` levels of nested loops
- forced to immediately do the next iteration using the `continue`
command, optionally as `continue N` analog to `break N`
### Return status
The return status is the one of the last command executed in `<LIST2>`,
or `0` (`TRUE`) if none was executed.
## Examples
## Portability considerations
## See also
- Internal: [The until loop](/syntax/ccmd/until_loop)
- Internal: [code examples of the read builtin
command](/commands/builtin/read#code_examples) to see how you can loop
over lines

73
syntax/expansion/arith.md Normal file
View File

@ -0,0 +1,73 @@
# Arithmetic expansion
$(( <EXPRESSION> ))
$[ <EXPRESSION> ]
The [arithmetic expression](/syntax/arith_expr) `<EXPRESSION>` is
evaluated and expands to the result. The output of the arithmetic
expansion is guaranteed to be one word and a digit in Bash.
Please **do not use the second form `$[ ... ]`**! It's deprecated. The
preferred and standardized form is `$(( ... ))`!
Example
``` bash
function printSum {
typeset -A args
typeset name
for name in first second; do
[[ -t 0 ]] && printf 'Enter %s positive integer: ' &quot;$name&quot; >&2
read -r ${BASH_VERSION+-e} &quot;args[$name]&quot;
[[ ${args[$name]} == +([[:digit:]]) ]] || return 1 # Validation is extremely important whenever user input is used in arithmetic.
done
printf 'The sum is %d.' $((${args[first]} + ${args[second]}))
}
```
**Note** that in Bash you don't need the arithmetic expansion to check
for the boolean value of an arithmetic expression. This can be done
using the [arithmetic evaluation compound
command](/syntax/ccmd/arithmetic_eval):
``` bash
printf %s 'Enter a number: ' >&2
read -r number
if ((number == 1234)); then
echo 'Good guess'
else
echo 'Haha... :-P'
fi
```
**Variables** used inside the arithmetic expansion, as in all arithmetic
contexts, can be used with or without variable expansion:
``` bash
x=1
echo $((x)) # Good.
echo $(($x)) # Ok. Avoid expansions within arithmetic. Use variables directly.
echo $((&quot;$x&quot;)) # Error. There is no quote-removal in arithmetic contexts. It expands to $((&quot;1&quot;)), which is an invalid arithmetic expression.
echo $((x[0])) # Good.
echo $((${x[0]})) # Ok. Nested expansion again.
echo $((${x[$((${x[!$x]}-$x))]})) # Same as above but more ridiculous.
echo $(($x[0])) # Error. This expands to $((1[0])), an invalid expression.
```
## Bugs and Portability considerations
- The original Bourne shell doesn't have arithmetic expansions. You have
to use something like `expr(1)` within backticks instead. Since `expr`
is horrible (as are backticks), and arithmetic expansion is required
by POSIX, you should not worry about this, and preferably fix any code
you find that's still using `expr`.
## See also
- [arithmetic expressions](/syntax/arith_expr)
- [arithmetic evaluation compound command](/syntax/ccmd/arithmetic_eval)
- [Introduction to expansion and substitution](/syntax/expansion/intro)
- [POSIX
definition](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_04)

266
syntax/expansion/brace.md Normal file
View File

@ -0,0 +1,266 @@
# Brace expansion
![](keywords>bash shell scripting expansion substitution text list brace)
{string1,string2,...,stringN}
{<START>..<END>}
{<START>..<END>..<INCR>} (Bash 4)
<PREFIX>{........}
{........}<SUFFIX>
<PREFIX>{........}<SUFFIX>
Brace expansion is used to generate arbitrary strings. The specified
strings are used to generate **all possible combinations** with the
optional surrounding prefixes and suffixes.
Usually it's used to generate mass-arguments for a command, that follow
a specific naming-scheme.
:!: It is the very first step in expansion-handling, it's important to
understand that. When you use
echo {a,b}$PATH
then the brace expansion **does not expand the variable** - this is done
in a **later step**. Brace expansion just makes it being:
echo a$PATH b$PATH
Another common pitfall is to assume that a range like `{1..200}` can be
expressed with variables using `{$a..$b}`. Due to what I described
above, it **simply is not possible**, because it's the very first step
in doing expansions. A possible way to achieve this, if you really can't
handle this in another way, is using the `eval` command, which basically
evaluates a commandline twice: `eval echo {$a..$b}` For instance, when
embedded inside a for loop : `for i in $(eval echo {$a..$b})` This
requires that the entire command be properly escaped to avoid unexpected
expansions. If the sequence expansion is to be assigned to an array,
another method is possible using [declaration
commands](/commands/builtin/declare):
`declare -a 'pics=(img{'&quot;$a..$b&quot;'}.png)'; mv &quot;${pics[@]}&quot; ../imgs`
This is significantly safer, but one must still be careful to control
the values of \$a and \$b. Both the exact quoting, and explicitly
including "-a" are important.
The brace expansion is present in two basic forms, **string lists** and
**ranges**.
It can be switched on and off under runtime by using the `set` builtin
and the option `-B` and `+B` or the long option `braceexpand`. If brace
expansion is enabled, the stringlist in `SHELLOPTS` contains
`braceexpand`.
## String lists
{string1,string2,...,stringN}
Without the optional prefix and suffix strings, the result is just a
space-separated list of the given strings:
$ echo {I,want,my,money,back}
I want my money back
With prefix or suffix strings, the result is a space-separated list of
**all possible combinations** of prefix or suffix specified strings:
$ echo _{I,want,my,money,back}
_I _want _my _money _back
$ echo {I,want,my,money,back}_
I_ want_ my_ money_ back_
$ echo _{I,want,my,money,back}-
_I- _want- _my- _money- _back-
The brace expansion is only performed, if the given string list is
really a **list of strings**, i.e., if there is a minimum of one "`,`"
(comma)! Something like `{money}` doesn't expand to something special,
it's really only the text "`{money}`".
## Ranges
{<START>..<END>}
Brace expansion using ranges is written giving the startpoint and the
endpoint of the range. This is a "sequence expression". The sequences
can be of two types
- integers (optionally zero padded, optionally with a given increment)
- characters
<!-- -->
$ echo {5..12}
5 6 7 8 9 10 11 12
$ echo {c..k}
c d e f g h i j k
When you mix these both types, brace expansion is **not** performed:
$ echo {5..k}
{5..k}
When you zero pad one of the numbers (or both) in a range, then the
generated range is zero padded, too:
$ echo {01..10}
01 02 03 04 05 06 07 08 09 10
There's a chapter of Bash 4 brace expansion changes at [the end of this
article](#new_in_bash_4.0).
Similar to the expansion using stringlists, you can add prefix and
suffix strings:
$ echo 1.{0..9}
1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9
$ echo ---{A..E}---
---A--- ---B--- ---C--- ---D--- ---E---
## Combining and nesting
When you combine more brace expansions, you effectively use a brace
expansion as prefix or suffix for another one. Let's generate all
possible combinations of uppercase letters and digits:
$ echo {A..Z}{0..9}
A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 C0 C1 C2 C3 C4 C5 C6
C7 C8 C9 D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 F0 F1 F2 F3
F4 F5 F6 F7 F8 F9 G0 G1 G2 G3 G4 G5 G6 G7 G8 G9 H0 H1 H2 H3 H4 H5 H6 H7 H8 H9 I0
I1 I2 I3 I4 I5 I6 I7 I8 I9 J0 J1 J2 J3 J4 J5 J6 J7 J8 J9 K0 K1 K2 K3 K4 K5 K6 K7
K8 K9 L0 L1 L2 L3 L4 L5 L6 L7 L8 L9 M0 M1 M2 M3 M4 M5 M6 M7 M8 M9 N0 N1 N2 N3 N4
N5 N6 N7 N8 N9 O0 O1 O2 O3 O4 O5 O6 O7 O8 O9 P0 P1 P2 P3 P4 P5 P6 P7 P8 P9 Q0 Q1
Q2 Q3 Q4 Q5 Q6 Q7 Q8 Q9 R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 S0 S1 S2 S3 S4 S5 S6 S7 S8
S9 T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 U0 U1 U2 U3 U4 U5 U6 U7 U8 U9 V0 V1 V2 V3 V4 V5
V6 V7 V8 V9 W0 W1 W2 W3 W4 W5 W6 W7 W8 W9 X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 Y0 Y1 Y2
Y3 Y4 Y5 Y6 Y7 Y8 Y9 Z0 Z1 Z2 Z3 Z4 Z5 Z6 Z7 Z8 Z9
Hey.. that **saves you writing** 260 strings!
Brace expansions can be nested, but too much of it usually makes you
losing overview a bit ;-)
Here's a sample to generate the alphabet, first the uppercase letters,
then the lowercase ones:
$ echo {{A..Z},{a..z}}
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z
## Common use and examples
### Massdownload from the Web
In this example, `wget` is used to download documentation that is split
over several numbered webpages.
`wget` won't see your braces. It will see **6 different URLs** to
download.
wget http://docs.example.com/documentation/slides_part{1,2,3,4,5,6}.html
Of course it's possible, and even easier, to do that with a sequence:
wget http://docs.example.com/documentation/slides_part{1..6}.html
### Generate a subdirectory structure
Your life is hard? Let's ease it a bit - that's what shells are here
for.
mkdir /home/bash/test/{foo,bar,baz,cat,dog}
### Generate numbers with a prefix 001 002 ...
- Using a prefix:
<!-- -->
for i in 0{1..9} 10; do printf &quot;%s\n&quot; &quot;$i&quot;;done
If you need to create words with the number embedded, you can use nested
brace:
printf &quot;%s\n&quot; img{00{1..9},0{10..99},{100..999}}.png
- Formatting the numbers with printf:
<!-- -->
echo $(printf &quot;img%02d.png &quot; {1..99})
See the [text below](#news_in_bash_4.0) for a new Bash 4 method.
### Repeating arguments or words
somecommand -v -v -v -v -v
Can be written as
somecommand -v{,,,,}
...which is a kind of a hack, but hey, it works.
\<div round info\>
#### More fun
The most optimal possible brace expansion to expand n arguments of
course consists of n's prime factors. We can use the "factor" program
bundled with GNU coreutils to emit a brace expansion that will expand
any number of arguments.
function braceify {
[[ $1 == +([[:digit:]]) ]] || return
typeset -a a
read -ra a < <(factor &quot;$1&quot;)
eval &quot;echo $(printf '{$(printf ,%%.s {1..%s})}' &quot;${a[@]:1}&quot;)&quot;
}
printf 'eval printf &quot;$arg&quot;%s' &quot;$(braceify 1000000)&quot;
"Braceify" generates the expansion code itself. In this example we
inject that output into a template which displays the most terse brace
expansion code that would expand `"$arg"` 1,000,000 times if evaluated.
In this case, the output is:
eval printf &quot;$arg&quot;{,,}{,,}{,,}{,,}{,,}{,,}{,,,,,}{,,,,,}{,,,,,}{,,,,,}{,,,,,}{,,,,,}
\</div\>
## New in Bash 4.0
### Zero padded number expansion
Prefix either of the numbers in a numeric range with `0` to pad the
expanded numbers with the correct amount of zeros:
$ echo {0001..5}
0001 0002 0003 0004 0005
### Increment
It is now possible to specify an increment using ranges:
{<START>..<END>..<INCR>}
`<INCR>` is numeric, you can use a negative integer but the correct sign
is deduced from the order of `<START>` and `<END>` anyways.
$ echo {1..10..2}
1 3 5 7 9
$ echo {10..1..2}
10 8 6 4 2
Interesting feature: The increment specification also works for
letter-ranges:
$ echo {a..z..3}
a d g j m p s v y
## See also

View File

@ -0,0 +1,137 @@
# Command substitution
![](keywords>bash shell scripting expansion substitution text variable output execute stdout save result return value)
$( <COMMANDS> )
` <COMMANDS> `
The command substitution expands to the output of commands. These
commands are executed in a subshell, and their `stdout` data is what the
substitution syntax expands to.
All **trailing** newlines are removed (below is an example for a
workaround).
In later steps, **if not quoted**, the results undergo [word
splitting](/syntax/expansion/wordsplit) and [pathname
expansion](/syntax/expansion/globs). You have to remember that, because
the word splitting will also remove embedded newlines and other `IFS`
characters and break the results up into several words. Also you'll
probably get unexpected pathname matches. **If you need the literal
results, quote the command substitution!**
The second form `` `COMMAND` `` is more or less obsolete for Bash, since
it has some trouble with nesting ("inner" backticks need to be escaped)
and escaping characters. Use `$(COMMAND)`, it's also POSIX!
When you [call an explicit subshell](/syntax/ccmd/grouping_subshell)
`(COMMAND)` inside the command substitution `$()`, then take care, this
way is **wrong**:
$((COMMAND))
Why? because it collides with the syntax for [arithmetic
expansion](/syntax/expansion/arith). You need to separate the command
substitution from the inner `(COMMAND)`:
$( (COMMAND) )
## Specialities
When the inner command is only an input redirection, and nothing else,
for example
$( <FILE )
# or
` <FILE `
then Bash attempts to read the given file and act just if the given
command was `cat FILE`.
## A closer look at the two forms
In general you really should only use the form `$()`, it's
escaping-neutral, it's nestable, it's also POSIX. But take a look at the
following code snips to decide yourself which form you need under
specific circumstances:
**<u>Nesting</u>**
Backtick form `` `...` `` is not directly nestable. You will have to
escape the "inner" backticks. Also, the deeper you go, the more escape
characters you need. Ugly.
echo `echo `ls`` # INCORRECT
echo `echo \`ls\`` # CORRECT
echo $(echo $(ls)) # CORRECT
**<u>Parsing</u>**
All is based on the fact that the backquote-form is simple character
substitution, while every `$()`-construct opens an own, subsequent
parsing step. Everything inside `$()` is interpreted as if written
normal on a commandline. No special escaping of **nothing** is needed:
echo &quot;$(echo &quot;$(ls)&quot;)&quot; # nested double-quotes - no problem
**<u>Constructs you should avoid</u>**
It's not all shiny with `$()`, at least for my current Bash
(`3.1.17(1)-release`. :!: <u>**Update:** Fixed since `3.2-beta` together
with a misinterpretion of '))' being recognized as arithmetic expansion
\[by redduck666\]</u>). This command seems to incorrectly close the
substitution step and echo prints "ls" and ")":
echo $(
# some comment ending with a )
ls
)
It seems that every closing ")" confuses this construct. Also a (very
uncommon ;-)) construct like:
echo $(read VAR; case &quot;$var&quot; in foo) blah ;; esac) # spits out some error, when it sees the &quot;;;&quot;
# fixes it:
echo $(read VAR; case &quot;$var&quot; in (foo) blah ;; esac) # will work, but just let it be, please ;-)
**<u>Conclusion:</u>**
In general, the `$()` should be the preferred method:
- it's clean syntax
- it's intuitive syntax
- it's more readable
- it's nestable
- its inner parsing is separate
## Examples
**To get the date:**
DATE=&quot;$(date)&quot;
**To copy a file and get `cp` error output:**
COPY_OUTPUT=&quot;$(cp file.txt /some/where 2>&1)&quot;
Attention: Here, you need to redirect `cp` `STDERR` to its `STDOUT`
target, because command substitution only catches `STDOUT`!
**Catch stdout and preserve trailing newlines:**
var=$(echo -n $'\n'); echo -n &quot;$var&quot;; # $var == &quot;&quot;
var=$(echo -n $'\n'; echo -n x); var=&quot;${var%x}&quot;; echo -n &quot;$var&quot; # $var == &quot;\n&quot;
This adds "x" to the output, which prevents the trailing newlines of the
previous commands' output from being deleted by \$().
By removing this "x" later on, we are left with the previous commands'
output with its trailing newlines.
## See also
- Internal: [Introduction to expansion and
substitution](/syntax/expansion/intro)
- Internal: [Obsolete and deprecated syntax](/scripting/obsolete)

113
syntax/expansion/globs.md Normal file
View File

@ -0,0 +1,113 @@
# Pathname expansion (globbing)
## General
Unlike on other platforms you may have seen, on UNIX(r), the shell is
responsible for interpreting and expanding globs ("filename wildcards").
A called program will never see the glob itself; it will only see the
expanded filenames as its arguments (here, all filenames matching
`*.log`):
grep &quot;changes:&quot; *.log
The base syntax for the pathname expansion is the [pattern
matching](/syntax/pattern) syntax. The pattern you describe is matched
against all existing filenames and the matching ones are substituted.
Since this substitution happens **after [word
splitting](/syntax/expansion/wordsplit)**, all resulting filenames are
literal and treated as separate words, no matter how many spaces or
other `IFS`-characters they contain.
## Normal behaviour
- with [the set command](/commands/builtin/set) (`-f`, `noglob`) you can
entirely disable pathname expansion
- when matching a pathname, the slash-character (`/`) always needs to be
matched explicitly
- the dot at the beginning of a filename must be matched explicitly
(also one following a `/` in the glob)
- a glob that doesn't match a filename is unchanged and remains what it
is
## Customization
- when the shell option `nullglob` is set, non-matching globs are
removed, rather than preserved
- when the shell option `failglob` is set, non-matching globs produce an
error message and the current command is not executed
- when the shell option `nocaseglob` is set, the match is performed
case-insensitive
- when the shell option `dotglob` is set, wildcard-characters can match
a dot at the beginning of a filename
- when the shell option `dirspell` is set, Bash performs spelling
corrections when matching directory names
- when the shell option `globstar` is set, the glob `**` will
recursively match all files and directories. This glob isn't
"configurable", i.e. you **can't** do something like `**.c` to
recursively get all `*.c` filenames.
- when the shell option `globasciiranges` is set, the bracket-range
globs (e.g. `[A-Z]`) use C locale order rather than the configured
locale's order (i.e. `ABC...abc...` instead of e.g. `AaBbCc...`) -
since 4.3-alpha
- the variable [GLOBIGNORE](/syntax/shellvars#GLOBIGNORE) can be set to
a colon-separated list of patterns to be removed from the list before
it is returned
### nullglob
Normally, when no glob specified matches an existing filename, no
pathname expansion is performed, and the globs are <u>**not**</u>
removed:
$ echo &quot;Textfiles here:&quot; *.txt
Textfiles here: *.txt
In this example, no files matched the pattern, so the glob was left
intact (a literal asterisk, followed by dot-txt).
This can be very annoying, for example when you drive a
[for-loop](/syntax/ccmd/classic_for) using the pathname expansion:
for filename in *.txt; do
echo &quot;=== BEGIN: $filename ===&quot;
cat &quot;$filename&quot;
echo &quot;=== END: $filename ===&quot;
done
When no file name matches the glob, the loop will not only output stupid
text ("`BEGIN: *.txt`"), but also will make the `cat`-command fail with
an error, since no file named `*.txt` exists.
Now, when the shell option `nullglob` is set, Bash will remove the
entire glob from the command line. In case of the for-loop here, not
even one iteration will be done. It just won't run.
So in our first example:
$ shopt -s nullglob
$ echo &quot;Textfiles here:&quot; *.txt
Textfiles here:
and the glob is gone.
### Glob characters
- \* - means 'match any number of characters'. '/' is not matched (and
depending on your settings, things like '.' may or may not be matched,
see above)
- ? - means 'match any single character'
- \[abc\] - match any of the characters listed. This syntax also
supports ranges, like \[0-9\]
For example, to match something beginning with either 'S' or 'K'
followed by two numbers, followed by at least 3 more characters:
[SK][0-9][0-9]???*
## See also
- [Introduction to expansion and substitution](/syntax/expansion/intro)
- [pattern matching syntax](/syntax/pattern)
- [the set builtin command](/commands/builtin/set)
- [the shopt builtin command](/commands/builtin/shopt)
- [list of shell options](/internals/shell_options)

91
syntax/expansion/intro.md Normal file
View File

@ -0,0 +1,91 @@
# Expansions and substitutions
![](keywords>bash shell scripting expansion substitution text variable filename macro wildcard)
Before executing your commands, Bash checks whether there are any syntax
elements in the command line that should be interpreted rather than
taken literally. After splitting the command line into tokens (words),
Bash scans for these special elements and interprets them, resulting in
a changed command line: the elements are said to be **expanded** to or
**substituted** to **new text and maybe new tokens** (words).
The most simple example of this behaviour is a referenced variable:
mystring=&quot;Hello world&quot;
echo &quot;$mystring&quot;
The `echo` program definitely doesn't care about what a shell variable
is. It is Bash's job to deal with the variable. Bash **expands** the
string "`$mystring`" to "`Hello world`", so that `echo` will only see
`Hello world`, not the variable or anything else!
After all these expansions and substitutions are done, all quotes that
are not meant literally (i.e., [the quotes that marked contiguous
words](/syntax/quoting), as part of the shell syntax) are removed from
the commandline text, so the called program won't see them. This step is
called **quote-removal**.
## Overview
Saw a possible expansion syntax but don't know what it is? Here's a
small list.
- [Parameter expansion](/syntax/pe) (it has its own [overview
section](/syntax/pe#overview))
- `$WORD`
- `${STUFF...}`
- [Pathname expansion](/syntax/expansion/globs)
- `*.txt`
- `page_1?.html`
- [Arithmetic expansion](/syntax/expansion/arith)
- `$(( EXPRESSION ))`
- `$[ EXPRESSION ]`
- [Command substitution](/syntax/expansion/cmdsubst)
- `$( COMMAND )`
- `` ` COMMAND ` ``
- [Tilde expansion](/syntax/expansion/tilde)
- `~`
- `~+`
- `~-`
- [Brace expansion](/syntax/expansion/brace)
- `{X,Y,Z}`
- `{X..Y}`
- `{X..Y..Z}`
- [Process substitution](/syntax/expansion/proc_subst)
- `<( COMMAND )`
- `>( COMMAND )`
## Order
Bash performs expansions and substitutions in a defined order. This
explains why globbing (pathname expansion), for example, is safe to use
on filenames with spaces (because it happens **after** the final word
splitting!).
The order is (from first to last):
- [Brace expansion](/syntax/expansion/brace)
- [Tilde expansion](/syntax/expansion/tilde)
- The following expansions happen at the same time, in a left-to-right
fashion on the commandline (see below)
- [Parameter expansion](/syntax/pe)
- [Arithmetic expansion](/syntax/expansion/arith)
- [Command substitution](/syntax/expansion/cmdsubst)
- [Word splitting](/syntax/expansion/wordsplit)
- [Pathname expansion](/syntax/expansion/globs)
[Process substitution](/syntax/expansion/proc_subst) is performed
**simultaneously** with [parameter expansion](/syntax/pe), [command
substitution](/syntax/expansion/cmdsubst) and [arithmetic
expansion](/syntax/expansion/arith). It is only performed when the
underlying operating system supports it.
The 3 steps [parameter expansion](/syntax/pe), [arithmetic
expansion](/syntax/expansion/arith) and [command
substitution](/syntax/expansion/cmdsubst) happen at the same time in a
left-to-right fashion on nthe commandline. This means
i=1
echo $i $((i++)) $i
will output `1 1 2` and not `1 1 1`.

View File

@ -0,0 +1,184 @@
# Process substitution
![](keywords>bash shell scripting expansion substitution text stdin stdout save capture)
Process substitution is a form of redirection where the input or output
of a process (some sequence of commands) appear as a temporary file.
<( <LIST> )
>( <LIST> )
Process substitution is performed **simultaneously** with [parameter
expansion](/syntax/pe), [command
substitution](/syntax/expansion/cmdsubst) and [arithmetic
expansion](/syntax/expansion/arith).
The [command list](/syntax/basicgrammar#lists) `<LIST>` is executed and
its
- standard output filedescriptor in the `<( ... )` form or
- standard input filedescriptor in the `>( ... )` form
is connected to a FIFO or a file in `/dev/fd/`. The filename (where the
filedescriptor is connected) is then used as a substitution for the
`<(...)`-construct.
That, for example, allows to give data to a command that can't be
reached by pipelining (that doesn't expect its data from `stdin` but
from a file).
### Scope
\<note important\> Note: According to multiple comments and sources, the
scope of process substitution file descriptors is **not** stable,
guaranteed, or specified by bash. Newer versions of bash (5.0+) seem to
have shorter scope, and substitutions scope seems to be shorter than
function scope. See
[stackexchange](https://unix.stackexchange.com/questions/425456/conditional-process-substitution)
and
[stackoverflow](https://stackoverflow.com/questions/46660020/bash-what-is-the-scope-of-the-process-substitution);
the latter discussion contains a script that can test the scoping
behavior case-by-case \</note\>
If a process substitution is expanded as an argument to a function,
expanded to an environment variable during calling of a function, or
expanded to any assignment within a function, the process substitution
will be "held open" for use by any command within the function or its
callees, until the function in which it was set returns. If the same
variable is set again within a callee, unless the new variable is local,
the previous process substitution is closed and will be unavailable to
the caller when the callee returns.
In essence, process substitutions expanded to variables within functions
remain open until the function in which the process substitution occured
returns - even when assigned to locals that were set by a function's
caller. Dynamic scope doesn't protect them from closing.
## Examples
This code is useless, but it demonstrates how it works:
``` bash
$ echo <(ls)
/dev/fd/63
```
The **output** of the `ls`-program can then be accessed by reading the
file `/dev/fd/63`.
Consider the following:
``` bash
diff <(ls &quot;$first_directory&quot;) <(ls &quot;$second_directory&quot;)
```
This will compare the contents of each directory. In this command, each
*process* is *substituted* for a *file*, and diff doesn't see \<(bla),
it sees two files, so the effective command is something like
``` bash
diff /dev/fd/63 /dev/fd/64
```
where those files are written to and destroyed automatically.
### Avoiding subshells
\<WRAP center round info 60%\> See Also:
[BashFAQ/024](http://mywiki.wooledge.org/BashFAQ/024) -- *I set
variables in a loop that's in a pipeline. Why do they disappear after
the loop terminates? Or, why can't I pipe data to read?* \</WRAP\>
One of the most common uses for process substitutions is to avoid the
final subshell that results from executing a pipeline. The following is
a **wrong** piece of code to count all files in `/etc` is:
``` bash
counter=0
find /etc -print0 | while IFS= read -rd '' _; do
((counter++))
done
echo &quot;$counter files&quot; # prints &quot;0 files&quot;
```
Due to the pipe, the `while read; do ... done` part is executed in a
subshell (in Bash, by default), which means `counter` is only
incremented within the subshell. When the pipeline finishes, the
subshell is terminated, and the `counter` visible to `echo` is still at
"0"!
Process substitution helps us avoid the pipe operator (the reason for
the subshell):
``` bash
counter=0
while IFS= read -rN1 _; do
((counter++))
done < <(find /etc -printf ' ')
echo &quot;$counter files&quot;
```
This is the normal input file redirection `< FILE`, just that the `FILE`
in this case is the result of process substitution. It's important to
note that the space is required in order to disambiguate the syntax from
[here documents](/syntax/redirection#here_documents).
``` bash
: < <(COMMAND) # Good.
: <<(...) # Wrong. Will be parsed as a heredoc. Bash fails when it comes across the unquoted metacharacter ''(''
: ><(...) # Technically valid but pointless syntax. Bash opens the pipe for writing, while the commands within the process substitution have their stdout connected to the pipe.
```
### Process substitution assigned to a parameter
This example demonstrates how process substitutions can be made to
resemble "passable" objects. This results in converting the output of
`f`'s argument to uppercase.
``` bash
f() {
cat &quot;$1&quot; >&quot;$x&quot;
}
x=>(tr '[:lower:]' '[:upper:]') f <(echo 'hi there')
```
See the above section on [\#scope](#scope)
## Bugs and Portability Considerations
- Process substitution is not specified by POSIX.
- Process substitution is disabled completely in Bash POSIX mode.
- Process substitution is implemented by Bash, Zsh, Ksh{88,93}, but not
(yet) pdksh derivatives (mksh). Coprocesses may be used instead.
- Process substitution is supported only on systems that support either
named pipes (FIFO - a [special file](/dict/terms/special_file)) or the
`/dev/fd/*` method for accessing open files. If the system doesn't
support `/dev/fd/*`, Bash falls back to creating named pipes. Note
that not all shells that support process substitution have that
fallback.
- Bash evaluates process substitutions within array indices, but not
other arithmetic contexts. Ksh and Zsh do not. (Possible Bug)
``` bash
# print &quot;moo&quot;
dev=fd=1 _[1<(echo moo >&2)]=
# fork bomb
${dev[${dev='dev[1>(${dev[dev]})]'}]}
```
- Issues with wait, race conditions, etc:
<https://groups.google.com/forum/?fromgroups=#!topic/comp.unix.shell/GqLNzUA4ulA>
## See also
- Internal: [Introduction to expansion and
substitution](/syntax/expansion/intro)
- Internal: [Bash in the process tree](/scripting/processtree)
(subshells)
- Internal: [Redirection](/syntax/redirection)

109
syntax/expansion/tilde.md Normal file
View File

@ -0,0 +1,109 @@
# Tilde expansion
![](keywords>bash shell scripting expansion substitution tilde home homedir shortcut)
~
~/...
~NAME
~NAME/...
~+
~+/...
~-
~-/...
The tilde expansion is used to expand to several specific pathnames:
- home directories
- current working directory
- previous working directory
Tilde expansion is only performed, when the tilde-construct is at the
beginning of a word, or a separate word.
If there's nothing to expand, i.e., in case of a wrong username or any
other error condition, the tilde construct is not replaced, it stays
what it is.
Tilde expansion is also performed everytime a variable is assigned:
- after the **first** `=`: `TARGET=~moonman/share`
- after **every** `:` (colon) in the assigned value:
`TARGET=file:~moonman/share`
\<note info\> As of now (Bash 4.3-alpha) the following constructs
**also** works, though it's not a variable assignment:
echo foo=~
echo foo=:~
I don't know yet, if this is a bug or intended. \</note\>
This way you can correctly use the tilde expansion in your
[PATH](/syntax/shellvars#PATH):
PATH=~/mybins:~peter/mybins:$PATH
**Spaces in the referenced pathes?** A construct like...
~/&quot;my directory&quot;
...is perfectly valid and works!
## Home directory
~
~<NAME>
This form expands to the home-directory of the current user (`~`) or the
home directory of the given user (`~<NAME>`).
If the given user doesn't exist (or if his home directory isn't
determinable, for some reason), it doesn't expand to something else, it
stays what it is. The requested home directory is found by asking the
operating system for the associated home directory for `<NAME>`.
To find the home directory of the current user (`~`), Bash has a
precedence:
- expand to the value of [HOME](/syntax/shellvars#HOME) if it's defined
- expand to the home directory of the user executing the shell
(operating system)
That means, the variable `HOME` can override the "real" home directory,
at least regarding tilde expansion.
## Current working directory
~+
This expands to the value of the [PWD](/syntax/shellvars#PWD) variable,
which holds the currect working directory:
echo &quot;CWD is $PWD&quot;
is equivalent to (note it **must** be a separate word!):
echo &quot;CWD is&quot; ~+
## Previous working directory
~-
This expands to the value of the [OLDPWD](/syntax/shellvars#OLDPWD)
variable, which holds the previous working directory (the one before the
last `cd`). If `OLDPWD` is unset (never changed the directory), it is
not expanded.
$ pwd
/home/bash
$ cd /etc
$ echo ~-
/home/bash
## See also
- Internal: [Introduction to expansion and
substitution](/syntax/expansion/intro)

View File

@ -0,0 +1,47 @@
# Word splitting
FIXME to be continued!
Word splitting occurs once any of the following expansions are done (and
only then!)
- [Parameter expansion](/syntax/pe)
- [Command substitution](/syntax/expansion/cmdsubst)
- [Arithmetic expansion](/syntax/expansion/arith)
Bash will scan the results of these expansions for special `IFS`
characters that mark word boundaries. This is only done on results that
are **not double-quoted**!
## Internal Field Separator IFS
The `IFS` variable holds the characters that Bash sees as word
boundaries in this step. The default contains the characters
- \<space\>
- \<tab\>
- \<newline\>
These characters are also assumed when IFS is **unset**. When `IFS` is
**empty** (nullstring), no word splitting is performed at all.
## Behaviour
The results of the expansions mentioned above are scanned for
`IFS`-characters. If **one or more** (in a sequence) of them is found,
the expansion result is split at these positions into multiple words.
This doesn't happen when the expansion results were **double-quoted**.
When a null-string (e.g., something that before expanded to
\>\>nothing\<\<) is found, it is removed, unless it is quoted (`''` or
`""`).
<u>**Again note:**</u> Without any expansion beforehand, Bash won't
perform word splitting! In this case, the initial token parsing is
solely responsible.
## See also
- [Introduction to expansion and substitution](/syntax/expansion/intro)
- [Quoting and escaping](/syntax/quoting)

View File

@ -0,0 +1,124 @@
FIXME work in progress...
# Parsing and execution
![](keywords>bash shell scripting syntax language behaviour executing execution)
Nearly everything in [Bash grammar](/syntax/basicgrammar) can be broken
down to a "simple command". The only thing Bash has to expand, evaluate
and execute is the simple command.
## Simple command expansion
\<div center round info 60%\>
- <http://lists.gnu.org/archive/html/bug-bash/2013-01/msg00040.html>
- <http://lists.research.att.com/pipermail/ast-developers/2013q2/002456.html>
\</div\>
This step happens after the initial command line splitting.
The expansion of a simple command is done in four steps (interpreting
the simple command **from left to right**):
1. The words the parser has marked as **variable assignments** and
**redirections** are saved for later processing.
- variable assignments precede the command name and have the form
`WORD=WORD`
- redirections can appear anywhere in the simple command
2. The rest of the words are [expanded](/syntax/expansion/intro). If
any words remain after expansion, the first word is taken to be the
**name of the command** and the remaining words are the
**arguments**.
3. [Redirections](/syntax/redirection) are performed.
4. The text after the `=` in each variable assignment undergoes [tilde
expansion](/syntax/expansion/tilde), [parameter
expansion](/syntax/pe), [command
substitution](/syntax/expansion/cmdsubst), [arithmetic
expansion](/syntax/expansion/arith), and quote removal before being
assigned to the variable.
If **no command name** results after expansion:
- The variable assignments affect the **current shell** environment.
- This is what happens when you enter only a variable assignment at
the command prompt.
- Assignment to readonly variables causes an error and the command
exits non-zero.
- Redirections are performed, but do not affect the current shell
environment.
- that means, a `> FILE` without any command **will** be performed:
the `FILE` will be created!
- The command exits
- with an exit code indicating the redirection error, if any
- with the exit code of the last command-substitution parsed, if any
- with exit code 0 (zero) if no redirection error happened and no
command substitution was done
Otherwise, if a command name results:
- The variables saved and parsed are added to the environment of the
executed command (and thus do not affect the current environment)
- Assignment to readonly variables causes an error and the command
exits with a non-zero error code.
- **Assignment errors** in non-POSIX modes cause the *enclosing
commands (e.g. loops) to completely terminate*
- **Assignment errors** in (non-interactive) POSIX mode cause *the
entire script to terminate*
The behavior regarding the variable assignment errors can be tested:
\<div center round info
60%\><http://lists.gnu.org/archive/html/bug-bash/2013-01/msg00054.html>\</div\>
**<u>This one exits the script completely</u>**
#!/bin/sh
# This shell runs in POSIX mode!
echo PRE
# The following is an assignment error, since there is no digit '9'
# for a base eight number!
foo=$((8#9))
echo POST
**<u>This one terminates only the enclosing compound command (the
`{ ...; }`):</u>**
#!/bin/bash
# This shell runs in native Bash-mode!
echo PRE
# The following is an assignment error!
# The &quot;echo TEST&quot; won't be executed, since the { ...; } is terminated
{ foo=$((8#9)); echo TEST; }
echo POST
## Simple command execution
If a parsed simple command contains no slashes, the shell attempts to
locate and execute it:
- shell functions
- shell builtin commands
- check own hash table
- search along `PATH`
As of Bash Version 4, when a command search fails, the shell executes a
shell function named `command_not_found_handle()` using the failed
command as arguments. This can be used to provide user friendly messages
or install software packages etc. Since this function runs in a separate
execution environment, you can't really influence the main shell with it
(changing directory, setting variables).
FIXME to be continued
## See also
- Internal: [Redirection](/syntax/redirection)
- Internal: [Introduction to expansions and
substitutions](/syntax/expansion/intro)

266
syntax/keywords/coproc.md Normal file
View File

@ -0,0 +1,266 @@
# The coproc keyword
## Synopsis
coproc [NAME] command [redirections]
## Description
Bash 4.0 introduced *coprocesses*, a feature certainly familiar to ksh
users. The `coproc` keyword starts a command as a background job,
setting up pipes connected to both its stdin and stdout so that you can
interact with it bidirectionally. Optionally, the co-process can have a
name `NAME`. If `NAME` is given, the command that follows **must be a
compound command**. If no `NAME` is given, then the command can be
either simple or compound.
The process ID of the shell spawned to execute the coprocess is
available through the value of the variable named by `NAME` followed by
a `_PID` suffix. For example, the variable name used to store the PID of
a coproc started with no `NAME` given would be `COPROC_PID` (because
`COPROC` is the default `NAME`). [wait](/commands/builtin/wait) may be
used to wait for the coprocess to terminate. Additionally, coprocesses
may be manipulated through their `jobspec`.
### Return status
The return status of a coprocess is the exit status of its command.
### Redirections
The optional redirections are applied after the pipes have been set up.
Some examples:
``` bash
# redirecting stderr in the pipe
$ coproc { ls thisfiledoesntexist; read; } 2>&1
[2] 23084
$ IFS= read -ru ${COPROC[0]} x; printf '%s\n' &quot;$x&quot;
ls: cannot access thisfiledoesntexist: No such file or directory
```
``` bash
#let the output of the coprocess go to stdout
$ { coproc mycoproc { awk '{print &quot;foo&quot; $0;fflush()}'; } >&3; } 3>&1
[2] 23092
$ echo bar >&${mycoproc[1]}
$ foobar
```
Here we need to save the previous file descriptor of stdout, because by
the time we redirect the fds of the coprocess, stdout has already been
redirected to the pipe.
### Pitfalls
#### Avoid the final pipeline subshell
The traditional Ksh workaround to avoid the subshell when doing
`command | while read` is to use a coprocess. Unfortunately, Bash's
behavior differs.
In Ksh you would do:
``` bash
# ksh93 or mksh/pdksh derivatives
ls |& # start a coprocess
while IFS= read -rp file; do print -r -- &quot;$file&quot;; done # read its output
```
In bash:
``` bash
#DOESN'T WORK
$ coproc ls
[1] 23232
$ while IFS= read -ru ${COPROC[0]} line; do printf '%s\n' &quot;$line&quot;; done
bash: read: line: invalid file descriptor specification
[1]+ Done coproc COPROC ls
```
By the time we start reading from the output of the coprocess, the file
descriptor has been closed.
See [this FAQ entry on Greg's
wiki](http://mywiki.wooledge.org/BashFAQ/024) for other pipeline
subshell workarounds.
#### Buffering
In the first example, we GNU awk's `fflush()` command. As always, when
you use pipes the I/O operations are buffered. Let's see what happens
with `sed`:
``` bash
$ coproc sed s/^/foo/
[1] 22981
$ echo bar >&${COPROC[1]}
$ read -t 3 -ru ${COPROC[0]} _; (( $? > 127 )) && echo &quot;nothing read&quot;
nothing read
```
Even though this example is the same as the first `awk` example, the
`read` doesn't return because the output is waiting in a buffer.
See [this faq entry on Greg's
wiki](http://mywiki.wooledge.org/BashFAQ/009) for some workarounds and
more information on buffering issues.
#### background processes
A coprocess' file descriptors are accessible only to the process from
which the `coproc` was started. They are not inherited by subshells.
Here is a not-so-meaningful illustration. Suppose we want to
continuously read the output of a coprocess and `echo` the result:
``` bash
#NOT WORKING
$ coproc awk '{print &quot;foo&quot; $0;fflush()}'
[2] 23100
$ while IFS= read -ru ${COPROC[0]} x; do printf '%s\n' &quot;$x&quot;; done &
[3] 23104
bash: line 243: read: 61: invalid file descriptor: Bad file descriptor
```
This fails because the file descriptors created by the parent are not
available to the subshell created by &.
A possible workaround:
``` bash
#WARNING: for illustration purpose ONLY
# this is not the way to make the coprocess print its output
# to stdout, see the redirections above.
$ coproc awk '{print &quot;foo&quot; $0;fflush()}'
[2] 23109
$ exec 3<&${COPROC[0]}
$ while IFS= read -ru 3 x; do printf '%s\n' &quot;$x&quot;; done &
[3] 23110
$ echo bar >&${COPROC[1]}
$ foobar
```
Here, fd 3 is inherited.
## Examples
### Anonymous Coprocess
Unlike ksh, Bash doesn't have true anonymous coprocesses. Instead, Bash
assigns FDs to a default array named `COPROC` if no `NAME` is supplied.
Here's an example:
``` bash
$ coproc awk '{print &quot;foo&quot; $0;fflush()}'
[1] 22978
```
This command starts in the background, and `coproc` returns immediately.
Two new file descriptors are now available via the `COPROC` array. We
can send data to our command:
``` bash
$ echo bar >&${COPROC[1]}
```
And then read its output:
``` bash
$ IFS= read -ru ${COPROC[0]} x; printf '%s\n' &quot;$x&quot;
foobar
```
When we don't need our command anymore, we can kill it via its pid:
$ kill $COPROC_PID
$
[1]+ Terminated coproc COPROC awk '{print &quot;foo&quot; $0;fflush()}'
### Named Coprocess
Using a named coprocess is simple. We just need a compound command (like
when defining a function), and the resulting FDs will be assigned to the
indexed array `NAME` we supply instead.
``` bash
$ coproc mycoproc { awk '{print &quot;foo&quot; $0;fflush()}' ;}
[1] 23058
$ echo bar >&${mycoproc[1]}
$ IFS= read -ru ${mycoproc[0]} x; printf '%s\n' &quot;$x&quot;
foobar
$ kill $mycoproc_PID
$
[1]+ Terminated coproc mycoproc { awk '{print &quot;foo&quot; $0;fflush()}'; }
```
### Redirecting the output of a script to a file and to the screen
``` bash
#!/bin/bash
# we start tee in the background
# redirecting its output to the stdout of the script
{ coproc tee { tee logfile ;} >&3 ;} 3>&1
# we redirect stding and stdout of the script to our coprocess
exec >&${tee[1]} 2>&1
```
## Portability considerations
- The `coproc` keyword is not specified by POSIX(R)
- The `coproc` keyword appeared in Bash version 4.0-alpha
- The `-p` option to Bash's `print` loadable is a NOOP and not connected
to Bash coprocesses in any way. It is only recognized as an option for
ksh compatibility, and has no effect.
- The `-p` option to Bash's `read` builtin conflicts with that of all
kshes and zsh. The equivalent in those shells is to add a `\?prompt`
suffix to the first variable name argument to `read`. i.e., if the
first variable name given contains a `?` character, the remainder of
the argument is used as the prompt string. Since this feature is
pointless and redundant, I suggest not using it in either shell.
Simply precede the `read` command with a `printf %s prompt >&2`.
### Other shells
ksh93, mksh, zsh, and Bash all support something called "coprocesses"
which all do approximately the same thing. ksh93 and mksh have virtually
identical syntax and semantics for coprocs. A *list* operator: `|&` is
added to the language which runs the preceding *pipeline* as a coprocess
(This is another reason not to use the special `|&` pipe operator in
Bash -- its syntax is conflicting). The `-p` option to the `read` and
`print` builtins can then be used to read and write to the pipe of the
coprocess (whose FD isn't yet known). Special redirects are added to
move the last spawned coprocess to a different FD: `<&p` and `>&p`, at
which point it can be accessed at the new FD using ordinary redirection,
and another coprocess may then be started, again using `|&`.
zsh coprocesses are very similar to ksh except in the way they are
started. zsh adds the shell reserved word `coproc` to the pipeline
syntax (similar to the way Bash's `time` keyword works), so that the
pipeline that follows is started as a coproc. The coproc's input and
output FDs can then be accessed and moved using the same `read`/`print`
`-p` and redirects used by the ksh shells.
It is unfortunate that Bash chose to go against existing practice in
their coproc implementation, especially considering it was the last of
the major shells to incorporate this feature. However, Bash's method
accomplishes the same without requiring nearly as much additional
syntax. The `coproc` keyword is easy enough to wrap in a function such
that it takes Bash code as an ordinary argument and/or stdin like
`eval`. Coprocess functionality in other shells can be similarly wrapped
to create a `COPROC` array automatically.
### Only one coprocess at a time
The title says it all, complain to the bug-bash mailing list if you want
more. See
<http://lists.gnu.org/archive/html/bug-bash/2011-04/msg00056.html> for
more details
The ability to use multiple coprocesses in Bash is considered
"experimental". Bash will throw an error if you attempt to start more
than one. This may be overridden at compile-time with the
`MULTIPLE_COPROCS` option. However, at this time there are still issues
-- see the above mailing list discussion.
## See also

160
syntax/pattern.md Normal file
View File

@ -0,0 +1,160 @@
# Patterns and pattern matching
![](keywords>bash shell scripting glob globbing wildcards filename pattern matching)
A pattern is a **string description**. Bash uses them in various ways:
- [Pathname expansion](/syntax/expansion/globs) (Globbing - matching
filenames)
- Pattern matching in [conditional
expressions](/syntax/ccmd/conditional_expression)
- [Substring removal](/syntax/pe#substring_removal) and [search and
replace](/syntax/pe#search_and_replace) in [Parameter
Expansion](/syntax/pe)
- Pattern-based branching using the [case command](/syntax/ccmd/case)
The pattern description language is relatively easy. Any character
that's not mentioned below matches itself. The `NUL` character may not
occur in a pattern. If special characters are quoted, they're matched
literally, i.e., without their special meaning.
Do **not** confuse patterns with ***regular expressions***, because they
share some symbols and do similar matching work.
## Normal pattern language
| Sequence | Description |
|----------|----------------------------------------------------------------------------------------------------------------|
| `*` | Matches **any string**, including the null string (empty string) |
| `?` | Matches any **single character** |
| `X` | Matches the character `X` which can be any character that has no special meaning |
| `\X` | Matches the character `X`, where the character's special meaning is stripped by the backslash |
| `\\` | Matches a backslash |
| `[...]` | Defines a pattern **bracket expression** (see below). Matches any of the enclosed characters at this position. |
### Bracket expressions
The bracket expression `[...]` mentioned above has some useful
applications:
| Bracket expression | Description |
|----------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `[XYZ]` | The "normal" bracket expression, matching either `X`, `Y` or `Z` |
| `[X-Z]` | A range expression: Matching all the characters from `X` to `Y` (your current **locale**, defines how the characters are **sorted**!) |
| `[[:class:]]` | Matches all the characters defined by a [POSIX(r) character class](https://pubs.opengroup.org/onlinepubs/009696899/basedefs/xbd_chap07.html#tag_07_03_01): `alnum`, `alpha`, `ascii`, `blank`, `cntrl`, `digit`, `graph`, `lower`, `print`, `punct`, `space`, `upper`, `word` and `xdigit` |
| `[^...]` | A negating expression: It matches all the characters that are **not** in the bracket expression |
| `[!...]` | Equivalent to `[^...]` |
| `[]...]` or `[-...]` | Used to include the characters `]` and `-` into the set, they need to be the first characters after the opening bracket |
| `[=C=]` | Matches any character that is eqivalent to the collation weight of `C` (current locale!) |
| `[[.SYMBOL.]]` | Matches the collating symbol `SYMBOL` |
### Examples
Some simple examples using normal pattern matching:
- Pattern `"Hello world"` matches
- `Hello world`
- Pattern `[Hh]"ello world"` matches
- =\> `Hello world`
- =\> `hello world`
- Pattern `Hello*` matches (for example)
- =\> `Hello world`
- =\> `Helloworld`
- =\> `HelloWoRlD`
- =\> `Hello`
- Pattern `Hello world[[:punct:]]` matches (for example)
- =\> `Hello world!`
- =\> `Hello world.`
- =\> `Hello world+`
- =\> `Hello world?`
- Pattern
`[[.backslash.]]Hello[[.vertical-line.]]world[[.exclamation-mark.]]`
matches (using [collation
symbols](https://pubs.opengroup.org/onlinepubs/009696899/basedefs/xbd_chap07.html#tag_07_03_02_04))
- =\> `\Hello|world!`
## Extended pattern language
If you set the [shell option](/internals/shell_options) `extglob`, Bash
understands some powerful patterns. A `<PATTERN-LIST>` is one or more
patterns, separated by the pipe-symbol (`PATTERN|PATTERN`).
| | |
|---------------------|------------------------------------------------------------|
| `?(<PATTERN-LIST>)` | Matches **zero or one** occurrence of the given patterns |
| `*(<PATTERN-LIST>)` | Matches **zero or more** occurrences of the given patterns |
| `+(<PATTERN-LIST>)` | Matches **one or more** occurrences of the given patterns |
| `@(<PATTERN-LIST>)` | Matches **one** of the given patterns |
| `!(<PATTERN-LIST>)` | Matches anything **except** one of the given patterns |
### Examples
**<u>Delete all but one specific file</u>**
rm -f !(survivior.txt)
## Pattern matching configuration
### Related shell options
| option | classification | description |
|-------------------|-------------------------------------|-------------------------------------------------------------------------------|
| `dotglob` | [globbing](/syntax/expansion/globs) | see [Pathname expansion customization](/syntax/expansion/globs#Customization) |
| `extglob` | global | enable/disable extended pattern matching language, as described above |
| `failglob` | [globbing](/syntax/expansion/globs) | see [Pathname expansion customization](/syntax/expansion/globs#Customization) |
| `nocaseglob` | [globbing](/syntax/expansion/globs) | see [Pathname expansion customization](/syntax/expansion/globs#Customization) |
| `nocasematch` | pattern/string matching | perform pattern matching without regarding the case of individual letters |
| `nullglob` | [globbing](/syntax/expansion/globs) | see [Pathname expansion customization](/syntax/expansion/globs#Customization) |
| `globasciiranges` | [globbing](/syntax/expansion/globs) | see [Pathname expansion customization](/syntax/expansion/globs#Customization) |
## Bugs and Portability considerations
\* Counter-intuitively, only the `[!chars]` syntax for negating a
character class is specified by POSIX for shell pattern matching.
`[^chars]` is merely a commonly-supported extension. Even dash supports
`[^chars]`, but not posh.
\* All of the extglob quantifiers supported by bash were supported by
ksh88. The set of extglob quantifiers supported by ksh88 are identical
to those supported by Bash, mksh, ksh93, and zsh.
\* mksh does not support POSIX character classes. Therefore, character
ranges like `[0-9]` are somewhat more portable than an equivalent POSIX
class like `[:digit:]`.
\* Bash uses a custom runtime interpreter for pattern matching. (at
least) ksh93 and zsh translate patterns into regexes and then use a
regex compiler to emit and cache optimized pattern matching code. This
means Bash may be an order of magnitude or more slower in cases that
involve complex back-tracking (usually that means extglob quantifier
nesting). You may wish to use Bash's regex support (the `=~` operator)
if performance is a problem, because Bash will use your C library regex
implementation rather than its own pattern matcher.
TODO: describe the pattern escape bug
<https://gist.github.com/ormaaj/6195070>
### ksh93 extras
ksh93 supports some very powerful pattern matching features in addition
to those described above.
\* ksh93 supports arbitrary quantifiers just like ERE using the
`{from,to}(pattern-list)` syntax. `{2,4}(foo)bar` matches between 2-4
"foo"'s followed by "bar". `{2,}(foo)bar` matches 2 or more "foo"'s
followed by "bar". You can probably figure out the rest. So far, none of
the other shells support this syntax.
\* In ksh93, a `pattern-list` may be delimited by either `&` or `|`. `&`
means "all patterns must be matched" instead of "any pattern". For
example, `[[ fo0bar == @(fo[0-9]&+([[:alnum:]]))bar ]]` would be true
while `[[ f00bar == @(fo[0-9]&+([[:alnum:]]))bar ]]` is false, because
all members of the and-list must be satisfied. No other shell supports
this so far, but you can simulate some cases in other shells using
double extglob negation. The aforementioned ksh93 pattern is equivalent
in Bash to: `[[ fo0bar == !(!(fo[0-9])|!(+([[:alnum:]])))bar ]]`, which
is technically more portable, but ugly.
\* ksh93's [printf](commands/builtin/printf) builtin can translate from
shell patterns to ERE and back again using the `%R` and `%P` format
specifiers respectively.

1044
syntax/pe.md Normal file

File diff suppressed because it is too large Load Diff

271
syntax/quoting.md Normal file
View File

@ -0,0 +1,271 @@
# Quotes and escaping
![](keywords>bash shell scripting quoting quotes escape backslash marks singlequotes doublequotes single double)
Quoting and escaping are important, as they influence the way Bash acts
upon your input. There are three recognized types:
- **per-character escaping** using a backslash: `\$stuff`
- **weak quoting** with double-quotes: `"stuff"`
- **strong quoting** with single-quotes: `'stuff'`
All three forms have the very same purpose: **They give you general
control over parsing, expansion and expansion results.**
Besides these basic variants, there are some special quoting methods
(like interpreting ANSI-C escapes in a string) you'll meet below.
:!: **ATTENTION** :!: The quote characters (`"`, double quote and `'`,
single quote) are a syntax element that influence parsing. It is not
related to the quote characters passed as text to the command line! The
syntax quotes are removed before the command is called! Example:
### NO NO NO: this passes three strings:
### (1) &quot;my
### (2) multiword
### (3) argument&quot;
MYARG=&quot;\&quot;my multiword argument\&quot;&quot;
somecommand $MYARG
### THIS IS NOT (!) THE SAME AS ###
command &quot;my multiword argument&quot;
### YOU NEED ###
MYARG=&quot;my multiword argument&quot;
command &quot;$MYARG&quot;
## Per-character escaping
Per-character escaping is useful in on expansions and substitutions. In
general, a character that has a special meaning to Bash, like the
dollar-sign (`$`) can be masked to not have a special meaning using the
backslash:
echo \$HOME is set to \&quot;$HOME\&quot;
- `\$HOME` won't expand because it's not in variable-expansion syntax
anymore
- The backslash changes the quotes into literals - otherwise Bash would
interpret them
The sequence `\<newline>` (an unquoted backslash, followed by a
`<newline>` character) is interpreted as **line continuation**. It is
removed from the input stream and thus effectively ignored. Use it to
beautify your code:
# escapestr_sed()
# read a stream from stdin and escape characters in text that could be interpreted as
# special characters by sed
escape_sed() {
sed \
-e 's/\//\\\//g' \
-e 's/\&/\\\&/g'
}
The backslash can be used to mask every character that has a special
meaning to bash. <u>Exception:</u> Inside a single-quoted string (see
below).
## Weak quoting
Inside a weak-quoted string there's **no special interpretion of**:
- spaces as word-separators (on inital command line splitting and on
[word splitting](/syntax/expansion/wordsplit)!)
- single-quotes to introduce strong-quoting (see below)
- characters for pattern matching
- tilde expansion
- pathname expansion
- process substitution
Everything else, especially [parameter expansion](/syntax/pe), is
performed!
ls -l &quot;*&quot;
Will not be expanded. `ls` gets the literal `*` as argument. It will,
unless you have a file named `*`, spit out an error.
echo &quot;Your PATH is: $PATH&quot;
Will work as expected. `$PATH` is expanded, because it's double (weak)
quoted.
If a backslash in double quotes ("weak quoting") occurs, there are 2
ways to deal with it
- if the baskslash is followed by a character that would have a special
meaning even inside double-quotes, the backslash is removed and the
following character looses its special meaning
- if the backslash is followed by a character without special meaning,
the backslash is not removed
In particuar this means that `"\$"` will become `$`, but `"\x"` will
become `\x`.
## Strong quoting
Strong quoting is very easy to explain:
Inside a single-quoted string **nothing** is interpreted, except the
single-quote that closes the string.
echo 'Your PATH is: $PATH'
`$PATH` won't be expanded, it's interpreted as ordinary text because
it's surrounded by strong quotes.
In practise that means, to produce a text like `Here's my test...` as a
single-quoted string, you have to leave and re-enter the single quoting
to get the character "`'`" as literal text:
# WRONG
echo 'Here's my test...'
# RIGHT
echo 'Here'\''s my test...'
# ALTERNATIVE: It's also possible to mix-and-match quotes for readability:
echo &quot;Here's my test&quot;
## ANSI C like strings
Bash provides another quoting mechanism: Strings that contain ANSI
C-like escape sequences. The Syntax is:
$'string'
where the following escape sequences are decoded in `string`:
| Code | Meaning |
|--------------|-------------------------------------------------------------------------------------------------------------------------------------|
| `\"` | double-quote |
| `\'` | single-quote |
| `\\` | backslash |
| `\a` | terminal alert character (bell) |
| `\b` | backspace |
| `\e` | escape (ASCII 033) |
| `\E` | escape (ASCII 033) **\E is non-standard** |
| `\f` | form feed |
| `\n` | newline |
| `\r` | carriage return |
| `\t` | horizontal tab |
| `\v` | vertical tab |
| `\cx` | a control-x character, for example, `$'\cZ'` to print the control sequence composed of Ctrl-Z (`^Z`) |
| `\uXXXX` | Interprets `XXXX` as a hexadecimal number and prints the corresponding character from the character set (4 digits) (Bash 4.2-alpha) |
| `\UXXXXXXXX` | Interprets `XXXX` as a hexadecimal number and prints the corresponding character from the character set (8 digits) (Bash 4.2-alpha) |
| `\nnn` | the eight-bit character whose value is the octal value nnn (one to three digits) |
| `\xHH` | the eight-bit character whose value is the hexadecimal value HH (one or two hex digits) |
This is especially useful when you want to pass special characters as
arguments to some programs, like passing a newline to sed.
The resulting text is treated as if it were **single-quoted**. No
further expansion happens.
The `$'...'` syntax comes from ksh93, but is portable to most modern
shells including pdksh. A
[specification](http://austingroupbugs.net/view.php?id=249#c590) for it
was accepted for SUS issue 7. There are still some stragglers, such as
most ash variants including dash, (except busybox built with "bash
compatibility" features).
## I18N/L10N
A dollar-sign followed by a double-quoted string, for example
echo $&quot;generating database...&quot;
means I18N. If there is a translation available for that string, it is
used instead of the given text. If not, or if the locale is `C`/`POSIX`,
the dollar sign is simply ignored, which results in a normal double
quoted string.
If the string was replaced (translated), the result is double quoted.
In case you're a C programmer: The purpose of `$"..."` is the same as
for `gettext()` or `_()`.
For useful examples to localize your scripts, please see [Appendix I of
the Advanced Bash Scripting
Guide](http://tldp.org/LDP/abs/html/localization.html).
**Attention:** There is a security hole. Please read [the gettext
documentation](http://www.gnu.org/software/gettext/manual/html_node/bash.html)
## Common mistakes
### String lists in for-loops
The [classic for loop](/syntax/ccmd/classic_for) uses a list of words to
iterate through. The list can also be in a variable:
mylist=&quot;DOG CAT BIRD HORSE&quot;
**<u>WRONG</u>** way to iterate through this list:
for animal in &quot;$mylist&quot;; do
echo $animal
done
Why? Due to the double-quotes, technically, the expansion of `$mylist`
is seen as **one word**. The for loop iterates exactly one time, with
`animal` set to the whole list.
**<u>RIGHT</u>** way to iterate through this list:
for animal in $mylist; do
echo $animal
done
### Working out the test-command
The command `test` or `[ ... ]` ([the classic test
command](/commands/classictest)) is an ordinary command, so ordinary
syntax rules apply. Let's take string comparison as an example:
[ WORD = WORD ]
The `]` at the end is a convenience; if you type `which [` you will see
that there is in fact a binary file with that name. So if we were
writing this as a test command it would be:
test WORD = WORD
When you compare variables, it's wise to quote them. Let's create a test
string with spaces:
mystring=&quot;my string&quot;
And now check that string against the word "testword":
[ $mystring = testword ] # WRONG!
This fails! These are too many arguments for the string comparison test.
After expansion is performed, you really execute:
[ my string = testword ]
test my string = testword
Which is wrong, because `my` and `string` are two separate arguments.
So what you really want to do is:
[ &quot;$mystring&quot; = testword ] # RIGHT!
test 'my string' = testword
Now the command has three parameters, which makes sense for a binary
(two argument) operator.
**<u>Hint:</u>** Inside the [conditional
expression](/syntax/ccmd/conditional_expression) (`[[ ]]`) Bash doesn't
perform word splitting, and thus you don't need to quote your variable
references - they are always seen as "one word".
## See also
- Internal: [Some words about words...](/syntax/words)
- Internal: [Word splitting](/syntax/expansion/wordsplit)
- Internal: [Introduction to expansions and
substitutions](/syntax/expansion/intro)

247
syntax/redirection.md Normal file
View File

@ -0,0 +1,247 @@
# Redirection
\<wrap left todo\>Fix me: To be continued\</wrap\>
Redirection makes it possible to control where the output of a command
goes to, and where the input of a command comes from. It's a mighty tool
that, together with pipelines, makes the shell powerful. The redirection
operators are checked whenever a [simple command is about to be
executed](/syntax/grammar/parser_exec).
Under normal circumstances, there are 3 files open, accessible by the
file descriptors 0, 1 and 2, all connected to your terminal:
| Name | FD | Description |
|----------|-----|--------------------------------------------------------|
| `stdin` | 0 | standard input stream (e.g. keyboard) |
| `stdout` | 1 | standard output stream (e.g. monitor) |
| `stderr` | 2 | standard error output stream (usually also on monitor) |
\<wrap center info\>The terms "monitor" and "keyboard" refer to the same
device, the **terminal** here. Check your preferred UNIX(r)-FAQ for
details, I'm too lazy to explain what a terminal is ;-) \</wrap\>
Both, `stdout` and `stderr` are output file descriptors. Their
difference is the **convention** that a program outputs payload on
`stdout` and diagnostic- and error-messages on `stderr`. If you write a
script that outputs error messages, please make sure you follow this
convention!
Whenever you **name** such a filedescriptor, i.e. you want to redirect
this descriptor, you just use the number:
# this executes the cat-command and redirects its error messages (stderr) to the bit bucket
cat some_file.txt 2>/dev/null
Whenever you **reference** a descriptor, to point to its current target
file, then you use a "`&`" followed by a the descriptor number:
# this executes the echo-command and redirects its normal output (stdout) to the standard error target
echo &quot;There was an error&quot; 1>&2
The redirection operation can be **anywhere** in a simple command, so
these examples are equivalent:
cat foo.txt bar.txt >new.txt
cat >new.txt foo.txt bar.txt
>new.txt cat foo.txt bar.txt
\<wrap center important\>Every redirection operator takes one or two
words as operands. If you have to use operands (e.g. filenames to
redirect to) that contain spaces you **must** quote them!\</wrap\>
## Valid redirection targets and sources
This syntax is recognized whenever a `TARGET` or a `SOURCE`
specification (like below in the details descriptions) is used.
| Syntax | Description |
|----------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------|
| `FILENAME` | references a normal, ordinary filename from the filesystem (which can of course be a FIFO, too. Simply everything you can reference in the filesystem) |
| `&N` | references the current target/source of the filedescriptor `N` ("duplicates" the filedescriptor) |
| `&-` | closes the redirected filedescriptor, useful instead of `> /dev/null` constructs (`> &-`) |
| `/dev/fd/N` | duplicates the filedescriptor `N`, if `N` is a valid integer |
| `/dev/stdin` | duplicates filedescriptor 0 (`stdin`) |
| `/dev/stdout` | duplicates filedescriptor 1 (`stdout`) |
| `/dev/stderr` | duplicates filedescriptor 2 (`stderr`) |
| `/dev/tcp/HOST/PORT` | assuming `HOST` is a valid hostname or IP address, and `PORT` is a valid port number or service name: redirect from/to the corresponding TCP socket |
| `/dev/udp/HOST/PORT` | assuming `HOST` is a valid hostname or IP address, and `PORT` is a valid port number or service name: redirect from/to the corresponding UDP socket |
If a target/source specification fails to open, the whole redirection
operation fails. Avoid referencing file descriptors above 9, since you
may collide with file descriptors Bash uses internally.
## Redirecting output
N > TARGET
This redirects the file descriptor number `N` to the target `TARGET`. If
`N` is omitted, `stdout` is assumed (FD 1). The `TARGET` is
**truncated** before writing starts.
If the option `noclobber` is set with [the set
builtin](/commands/builtin/set), with cause the redirection to fail,
when `TARGET` names a regular file that already exists. You can manually
override that behaviour by forcing overwrite with the redirection
operator `>|` instead of `>`.
## Appending redirected output
N >> TARGET
This redirects the file descriptor number `N` to the target `TARGET`. If
`N` is omitted, `stdout` is assumed (FD 1). The `TARGET` is **not
truncated** before writing starts.
## Redirecting output and error output
&> TARGET
>& TARGET
This special syntax redirects both, `stdout` and `stderr` to the
specified target. It's **equivalent** to
> TARGET 2>&1
Since Bash4, there's `&>>TARGET`, which is equivalent to
`>> TARGET 2>&1`.
\<wrap center important\>This syntax is deprecated and should not be
used. See the page about [obsolete and deprecated
syntax](/scripting/obsolete).\</wrap\>
## Appending redirected output and error output
To append the cumulative redirection of `stdout` and `stderr` to a file
you simply do
>> FILE 2>&1
&>> FILE
## Transporting stdout and stderr through a pipe
COMMAND1 2>&1 | COMMAND2
COMMAND1 |& COMMAND2
## Redirecting input
N < SOURCE
The input descriptor `N` uses `SOURCE` as its data source. If `N` is
omitted, filedescriptor 0 (`stdin`) is assumed.
## Here documents
\<BOOKMARK:tag_heredoc\>
<<TAG
...
TAG
<<-TAG
...
TAG
A here-document is an input redirection using source data specified
directly at the command line (or in the script), no "external" source.
The redirection-operator `<<` is used together with a tag `TAG` that's
used to mark the end of input later:
# display help
cat <<EOF
Sorry...
No help available yet for $PROGRAM.
Hehe...
EOF
As you see, substitutions are possible. To be precise, the following
substitutions and expansions are performed in the here-document data:
- [Parameter expansion](/syntax/pe)
- [Command substitution](/syntax/expansion/cmdsubst)
- [Arithmetic expansion](/syntax/expansion/arith)
You can avoid that by quoting the tag:
cat <<&quot;EOF&quot;
This won't be expanded: $PATH
EOF
Last but not least, if the redirection operator `<<` is followed by a
`-` (dash), all **leading TAB** from the document data will be ignored.
This might be useful to have optical nice code also when using
here-documents.
The tag you use **must** be the only word in the line, to be recognized
as end-of-here-document marker.
\<wrap center info\>It seems that here-documents (tested on versions
`1.14.7`, `2.05b` and `3.1.17`) are correctly terminated when there is
an EOF before the end-of-here-document tag. The reason is unknown, but
it seems to be done on purpose. Bash 4 introduced a warning message when
end-of-file is seen before the tag is reached.\</wrap\>
## Here strings
<<< WORD
The here-strings are a variation of the here-documents. The word `WORD`
is taken for the input redirection:
cat <<< &quot;Hello world... $NAME is here...&quot;
Just beware to quote the `WORD` if it contains spaces. Otherwise the
rest will be given as normal parameters.
The here-string will append a newline (`\n`) to the data.
## Multiple redirections
More redirection operations can occur in a line of course. The order is
**important**! They're evaluated from **left to right**. If you want to
redirect both, `stderr` and `stdout` to the same file (like `/dev/null`,
to hide it), this is **the wrong way**:
``` bash
# { echo OUTPUT; echo ERRORS >&2; } is to simulate something that outputs to STDOUT and STDERR
# you can test with it
{ echo OUTPUT; echo ERRORS >&2; } 2>&1 1>/dev/null
```
Why? Relatively easy:
- initially, `stdout` points to your terminal (you read it)
- same applies to `stderr`, it's connected to your terminal
- `2>&1` redirects `stderr` away from the terminal to the target for
`stdout`: **the terminal** (again...)
- `1>/dev/null` redirects `stdout` away from your terminal to the file
`/dev/null`
What remains? `stdout` goes to `/dev/null`, `stderr` still (or better:
"again") goes to the terminal. You have to swap the order to make it do
what you want:
``` bash
{ echo OUTPUT; echo ERRORS >&2; } 1>/dev/null 2>&1
```
## Examples
How to make a program quiet (assuming all output goes to `STDOUT` and
`STDERR`?
command >/dev/null 2>&1
## See also
- Internal: [Illustrated Redirection
Tutorial](/howto/redirection_tutorial)
- Internal: [The noclobber option](/commands/builtin/set#tag_noclobber)
- Internal: [The exec builtin command](/commands/builtin/exec)
- Internal: [Simple commands parsing and
execution](/syntax/grammar/parser_exec)
- Internal: [Process substitution syntax](/syntax/expansion/proc_subst)
- Internal: [Obsolete and deprecated syntax](/scripting/obsolete)

1507
syntax/shellvars.md Normal file

File diff suppressed because it is too large Load Diff

164
syntax/words.md Normal file
View File

@ -0,0 +1,164 @@
# Words...
![](keywords>bash shell scripting token words split splitting recognition)
FIXME This article needs a review, it covers two topics (command line
splitting and word splitting) and mixes both a bit too much. But in
general, it's still usable to help understand this behaviour, it's
"wrong but not wrong".
One fundamental principle of Bash is to recognize words entered at the
command prompt, or under other circumstances like variable-expansion.
## Splitting the commandline
Bash scans the command line and splits it into words, usually to put the
parameters you enter for a command into the right C-memory (the `argv`
vector) to later correctly call the command. These words are recognized
by splitting the command line at the special character position,
**Space** or **Tab** (the manual defines them as **blanks**). For
example, take the echo program. It displays all its parameters separated
by a space. When you enter an echo command at the Bash prompt, Bash will
look for those special characters, and use them to separate the
parameters.
You don't know what I'm talking about? I'm talking about this:
$ echo Hello little world
Hello little world
In other words, something you do (and Bash does) everyday. The
characters where Bash splits the command line (SPACE, TAB i.e. blanks)
are recognized as delimiters. There is no null argument generated when
you have 2 or more blanks in the command line. **A sequence of more
blank characters is treated as a single blank.** Here's an example:
$ echo Hello little world
Hello little world
Bash splits the command line at the blanks into words, then it calls
echo with **each word as an argument**. In this example, echo is called
with three arguments: "`Hello`", "`little`" and "`world`"!
<u>Does that mean we can't echo more than one Space?</u> Of course not!
Bash treats blanks as special characters, but there are two ways to tell
Bash not to treat them special: **Escaping** and **quoting**.
Escaping a character means, to **take away its special meaning**. Bash
will use an escaped character as text, even if it's a special one.
Escaping is done by preceeding the character with a backslash:
$ echo Hello\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ little \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ world
Hello little world
None of the escaped spaces will be used to perform word splitting. Thus,
echo is called with one argument: "`Hello little world`".
Bash has a mechanism to "escape" an entire string: **Quoting**. In the
context of command-splitting, which this section is about, it doesn't
matter which kind of quoting you use: weak quoting or strong quoting,
both cause Bash to not treat spaces as special characters:
$ echo &quot;Hello little world&quot;
Hello little world
$ echo 'Hello little world'
Hello little world
<u>What is it all about now?</u> Well, for example imagine a program
that expects a filename as an argument, like cat. Filenames can have
spaces in them:
$ ls -l
total 4
-rw-r--r-- 1 bonsai bonsai 5 Apr 18 18:16 test file
$ cat test file
cat: test: No such file or directory
cat: file: No such file or directory
$ cat test\ file
m00!
$ cat &quot;test file&quot;
m00!
If you enter that on the command line with Tab completion, that will
take care of the spaces. But Bash also does another type of splitting.
## Word splitting
For a more technical description, please read the [article about word
splitting](/syntax/expansion/wordsplit)!
The first kind of splitting is done to parse the command line into
separate tokens. This is what was described above, it's a pure **command
line parsing**.
After the command line has been split into words, Bash will perform
expansion, if needed - variables that occur in the command line need to
be expanded (substituted by their value), for example. This is where the
second type of word splitting comes in - several expansions undergo
**word splitting** (but others do not).
Imagine you have a filename stored in a variable:
MYFILE=&quot;test file&quot;
When this variable is used, its occurance will be replaced by its
content.
$ cat $MYFILE
cat: test: No such file or directory
cat: file: No such file or directory
Though this is another step where spaces make things difficult,
**quoting** is used to work around the difficulty. Quotes also affect
word splitting:
$ cat &quot;$MYFILE&quot;
m00!
## Example
Let's follow an unquoted command through these steps, assuming that the
variable is set:
MYFILE=&quot;THE FILE.TXT&quot;
and the first review is:
echo The file is named $MYFILE
The parser will scan for blanks and mark the relevant words ("splitting
the command line"):
| Initial command line splitting: | | | | | |
|---------------------------------|--------|--------|--------|---------|-----------|
| Word 1 | Word 2 | Word 3 | Word 4 | Word 5 | Word 6 |
| `echo` | `The` | `file` | `is` | `named` | `$MYFILE` |
A [parameter/variable expansion](/syntax/pe) is part of that command
line, Bash will perform the substitution, and the [word
splitting](/syntax/expansion/wordsplit) on the results:
| Word splitting after substitution: | | | | | | |
|------------------------------------|--------|--------|--------|---------|--------|------------|
| Word 1 | Word 2 | Word 3 | Word 4 | Word 5 | Word 6 | Word 7 |
| `echo` | `The` | `file` | `is` | `named` | `THE` | `FILE.TXT` |
Now let's imagine we quoted `$MYFILE`, the command line now looks like:
echo The file is named &quot;$MYFILE&quot;
| Word splitting after substitution (quoted!): | | | | | |
|----------------------------------------------|--------|--------|--------|---------|----------------|
| Word 1 | Word 2 | Word 3 | Word 4 | Word 5 | Word 6 |
| `echo` | `The` | `file` | `is` | `named` | `THE FILE.TXT` |
***Bold Text*72i love this world**===== See also =====
- Internal: [Quoting and character escaping](/syntax/quoting)
- Internal: [Word splitting](/syntax/expansion/wordsplit)
- Internal: [Introduction to expansions and
substitutions](/syntax/expansion/intro)

20
user/thebonsai/imprint.md Normal file
View File

@ -0,0 +1,20 @@
# Imprint
My provider forces me to publish my full and true name and address here.
Otherwise they can close and lock the account. **I strictly contradict
every unpermitted use of my name and address. Especially sending
commercials of your own accord and/or selling the address are strictly
forbidden! For exceptions my written permission is absolutely
necessary.**
Responsible for site content and design:
`Jan Schampera Erich-Schott-Str. 2 95666 Mitterteich Federal Republic of Germany`
\<note warning\>*"I am not responsible for contents of linked pages. I
dissociate myself from illegal contents, especially child porn, any
radical statements, picture material as well as illegal links. The
webmasters of linked pages are responsible by themselves! I assure my
users of trying to link only serious pages, and of checking my links
first. But at least I can not guarantee for unknown people and their
thoughts."*\</note\>