mirror of
https://github.com/rawiriblundell/wiki.bash-hackers.org
synced 2024-12-24 13:50:39 +01:00
Correct some HTML chars
This commit is contained in:
parent
90923ad061
commit
4fb2ba4e21
@ -83,8 +83,9 @@ scrape_targets() {
|
|||||||
# First, remove everything between the first line and 'name="sectok"'
|
# First, remove everything between the first line and 'name="sectok"'
|
||||||
# Next, remove everything after '</textarea>'
|
# Next, remove everything after '</textarea>'
|
||||||
# This should remove everything above and below our desired markdown
|
# This should remove everything above and below our desired markdown
|
||||||
|
# We also take the opportunity to convert some HTML chars
|
||||||
extract_markdown() {
|
extract_markdown() {
|
||||||
sed -e '1,/name="sectok"/d' -e '/<\/textarea>/,$d' "${1:-/dev/stdin}"
|
sed -e '1,/name="sectok"/d' -e '/<\/textarea>/,$d' -e 's/>/>/g' -e 's/</</g' -e 's/&/\&/g' "${1:-/dev/stdin}"
|
||||||
}
|
}
|
||||||
|
|
||||||
###### Beyond this point things get a little wishy-washy ######
|
###### Beyond this point things get a little wishy-washy ######
|
||||||
|
30
bash4.md
30
bash4.md
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
:V4:
|
: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 [[scripting:bashchanges]] page for new stuff introduced.</note>
|
<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 [[scripting:bashchanges]] page for new stuff introduced.</note>
|
||||||
|
|
||||||
Besides many bugfixes since Bash 3.2, Bash 4 will bring some interesting new
|
Besides many bugfixes since Bash 3.2, Bash 4 will bring some interesting new
|
||||||
features for shell users and scripters. See also [[scripting:bashchanges]] for a small general overview with more details.
|
features for shell users and scripters. See also [[scripting:bashchanges]] for a small general overview with more details.
|
||||||
@ -15,11 +15,11 @@ feel free to write a chapter here.**
|
|||||||
The complete list of fixes and changes is in the CHANGES or NEWS file of your
|
The complete list of fixes and changes is in the CHANGES or NEWS file of your
|
||||||
Bash 4 distribution.
|
Bash 4 distribution.
|
||||||
|
|
||||||
<WRAP center round download 60%>
|
<WRAP center round download 60%>
|
||||||
The current available **stable** version is 4.4.18 release (February 03, 2018):
|
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.cwru.edu/pub/bash/bash-4.4.18.tar.gz
|
||||||
* ftp://ftp.gnu.org/pub/gnu/bash/bash-4.4.18.tar.gz
|
* ftp://ftp.gnu.org/pub/gnu/bash/bash-4.4.18.tar.gz
|
||||||
</WRAP>
|
</WRAP>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -50,10 +50,10 @@ See: [[commands:builtin:mapfile]]
|
|||||||
|
|
||||||
The ''case'' construct understands two new action list terminators:
|
The ''case'' construct understands two new action list terminators:
|
||||||
|
|
||||||
The '';&'' terminator causes execution to continue with the next action list
|
The '';&'' terminator causes execution to continue with the next action list
|
||||||
(rather than terminate the ''case'' construct).
|
(rather than terminate the ''case'' construct).
|
||||||
|
|
||||||
The '';;&'' terminator causes the ''case'' construct to test the next given
|
The '';;&'' terminator causes the ''case'' construct to test the next given
|
||||||
pattern instead of terminating the whole execution.
|
pattern instead of terminating the whole execution.
|
||||||
|
|
||||||
See [[syntax:ccmd:case]]
|
See [[syntax:ccmd:case]]
|
||||||
@ -124,15 +124,15 @@ See [[syntax:pe#case_modification | Case modification on parameter expansion]]
|
|||||||
When using substring expansion on the positional parameters, a starting
|
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
|
index of 0 now causes $0 to be prepended to the list (if the positional parameters are used). Before, this expansion
|
||||||
started with $1:
|
started with $1:
|
||||||
<code>
|
<code>
|
||||||
# this should display $0 on Bash v4, $1 on Bash v3
|
# this should display $0 on Bash v4, $1 on Bash v3
|
||||||
echo ${@:0:1}
|
echo ${@:0:1}
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== Globbing ====
|
==== Globbing ====
|
||||||
|
|
||||||
There's a new shell option ''[[internals:shell_options#globstar | globstar]]''. When enabled, Bash will perform
|
There's a new shell option ''[[internals:shell_options#globstar | globstar]]''. When enabled, Bash will perform
|
||||||
recursive globbing on ''<nowiki>**</nowiki>'' -- this means it matches all directories and files
|
recursive globbing on ''<nowiki>**</nowiki>'' -- this means it matches all directories and files
|
||||||
from the current position in the filesystem, rather than only the current level.
|
from the current position in the filesystem, rather than only the current level.
|
||||||
|
|
||||||
The new shell option ''[[internals:shell_options#dirspell | dirspell]]'' enables spelling corrections on directory
|
The new shell option ''[[internals:shell_options#dirspell | dirspell]]'' enables spelling corrections on directory
|
||||||
@ -145,23 +145,23 @@ See [[syntax:expansion:globs]]
|
|||||||
Besides the classic method of integer indexed arrays, Bash 4 supports 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
|
An associative array is an array indexed by an arbitrary string, something like
|
||||||
<code>
|
<code>
|
||||||
declare -A ASSOC
|
declare -A ASSOC
|
||||||
|
|
||||||
ASSOC[First]="first element"
|
ASSOC[First]="first element"
|
||||||
ASSOC[Hello]="second element"
|
ASSOC[Hello]="second element"
|
||||||
ASSOC[Peter Pan]="A weird guy"
|
ASSOC[Peter Pan]="A weird guy"
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
See [[syntax:arrays]]
|
See [[syntax:arrays]]
|
||||||
|
|
||||||
===== Redirection =====
|
===== Redirection =====
|
||||||
|
|
||||||
There is a new ''<nowiki>&>></nowiki>'' redirection operator, which appends the standard output
|
There is a new ''<nowiki>&>></nowiki>'' redirection operator, which appends the standard output
|
||||||
and standard error to the named file. This is the same as the good old ''<nowiki>>>FILE 2>&1</nowiki>''
|
and standard error to the named file. This is the same as the good old ''<nowiki>>>FILE 2>&1</nowiki>''
|
||||||
notation.
|
notation.
|
||||||
|
|
||||||
The parser now understands ''|&'' as a synonym for ''2>&1 |'', which redirects
|
The parser now understands ''|&'' as a synonym for ''2>&1 |'', which redirects
|
||||||
the standard error for a command through a pipe.
|
the standard error for a command through a pipe.
|
||||||
|
|
||||||
See [[syntax:redirection]]
|
See [[syntax:redirection]]
|
||||||
@ -180,9 +180,9 @@ The mentioned shell options are **off by default** unless otherwise mentioned.
|
|||||||
|
|
||||||
^Option ^Description ^
|
^Option ^Description ^
|
||||||
|''[[internals:shell_options#checkjobs | checkjobs]]'' | check for and report any running jobs at shell exit |
|
|''[[internals:shell_options#checkjobs | checkjobs]]'' | check for and report any running jobs at shell exit |
|
||||||
|''[[internals:shell_options#compat31 | compat*]]'' | set compatiblity modes for older shell versions (influences regular expression matching in ''<nowiki>[[ ... ]]</nowiki>'' |
|
|''[[internals:shell_options#compat31 | compat*]]'' | set compatiblity modes for older shell versions (influences regular expression matching in ''<nowiki>[[ ... ]]</nowiki>'' |
|
||||||
|''[[internals:shell_options#dirspell | dirspell]]'' | enables spelling corrections on directory names during globbing |
|
|''[[internals:shell_options#dirspell | dirspell]]'' | enables spelling corrections on directory names during globbing |
|
||||||
|''[[internals:shell_options#globstar | globstar]]'' | enables recursive globbing with ''<nowiki>**</nowiki>'' |
|
|''[[internals:shell_options#globstar | globstar]]'' | enables recursive globbing with ''<nowiki>**</nowiki>'' |
|
||||||
|''[[internals:shell_options#lastpipe | lastpipe]]'' | (4.2) to execute the last command in a pipeline in the current environment |
|
|''[[internals:shell_options#lastpipe | lastpipe]]'' | (4.2) to execute the last command in a pipeline in the current environment |
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
====== The caller builtin command ======
|
====== The caller builtin command ======
|
||||||
|
|
||||||
===== Synopsis =====
|
===== Synopsis =====
|
||||||
<code>caller [FRAMENUMBER]</code>
|
<code>caller [FRAMENUMBER]</code>
|
||||||
|
|
||||||
===== Description =====
|
===== 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.
|
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.
|
||||||
@ -18,7 +18,7 @@ This way, you can print a "stack trace" for debugging or logging purpo
|
|||||||
|
|
||||||
The code is made very simple, just to show the basic purposes.
|
The code is made very simple, just to show the basic purposes.
|
||||||
|
|
||||||
<code bash>
|
<code bash>
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
die() {
|
die() {
|
||||||
@ -35,19 +35,19 @@ f2() { f1; }
|
|||||||
f3() { f2; }
|
f3() { f2; }
|
||||||
|
|
||||||
f3
|
f3
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
**Output**
|
**Output**
|
||||||
<code>
|
<code>
|
||||||
12 f1 ./callertest.sh
|
12 f1 ./callertest.sh
|
||||||
13 f2 ./callertest.sh
|
13 f2 ./callertest.sh
|
||||||
14 f3 ./callertest.sh
|
14 f3 ./callertest.sh
|
||||||
16 main ./callertest.sh
|
16 main ./callertest.sh
|
||||||
*** an error occured ***
|
*** an error occured ***
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Notes =====
|
===== 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. <code>{ bash /dev/stdin; } <<<$'f(){ g; }\ng(){ h; }\nh(){ while caller $((n++)); do :; done; }\nf'</code>
|
* ''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. <code>{ bash /dev/stdin; } <<<$'f(){ g; }\ng(){ h; }\nh(){ while caller $((n++)); do :; done; }\nf'</code>
|
||||||
* 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 [[ http://bashdb.sourceforge.net/|Bashdb ]] can assist in using some of Bash's more advanced debug features.
|
* 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 [[ http://bashdb.sourceforge.net/|Bashdb ]] 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.
|
* 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.
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<label class="block control-label" for="focus__this"><span>Username</span> <input type="text" id="focus__this" name="u" class="edit form-control"/></label><br/>
|
<label class="block control-label" for="focus__this"><span>Username</span> <input type="text" id="focus__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="block control-label"><span>Password</span> <input type="password" name="p" class="edit form-control"/></label><br/>
|
||||||
<label class="simple control-label" for="remember__me"><input type="checkbox" id="remember__me" name="r" value="1" class="checkbox-inline"/> <span>Remember me</span></label>
|
<label class="simple control-label" for="remember__me"><input type="checkbox" id="remember__me" name="r" value="1" class="checkbox-inline"/> <span>Remember me</span></label>
|
||||||
<div id="plugin__captcha_wrapper"><input type="hidden" name="6e809c536862962bdd4db54ddd356008" value="AMMohIAc6LXIfPoUSz5NVCj8gp9c8TyOgzc39+zbTCs="/><label for="plugin__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>
|
<div id="plugin__captcha_wrapper"><input type="hidden" name="6e809c536862962bdd4db54ddd356008" value="AMMohIAc6LXIfPoUSz5NVCj8gp9c8TyOgzc39+zbTCs="/><label for="plugin__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>
|
</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>
|
<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>
|
||||||
@ -89,7 +89,7 @@
|
|||||||
<span class="visible-lg-block"></span>
|
<span class="visible-lg-block"></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<img src="/web/20211028115259im_/https://wiki.bash-hackers.org/lib/exe/indexer.php?id=commands%3Abuiltin%3Acontinuebreak&1635421979" width="2" height="1" alt=""/>
|
<img src="/web/20211028115259im_/https://wiki.bash-hackers.org/lib/exe/indexer.php?id=commands%3Abuiltin%3Acontinuebreak&1635421979" width="2" height="1" alt=""/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
====== The declare builtin command ======
|
====== The declare builtin command ======
|
||||||
|
|
||||||
===== Synopsis =====
|
===== Synopsis =====
|
||||||
<code>
|
<code>
|
||||||
declare [-aAfFgilrtux] [-p] [NAME[=VALUE] ...]
|
declare [-aAfFgilrtux] [-p] [NAME[=VALUE] ...]
|
||||||
|
|
||||||
# obsolete typeset synonym
|
# obsolete typeset synonym
|
||||||
typeset [-aAfFgilrtux] [-p] [NAME[=VALUE] ...]
|
typeset [-aAfFgilrtux] [-p] [NAME[=VALUE] ...]
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Description =====
|
===== Description =====
|
||||||
|
|
||||||
@ -61,7 +61,7 @@ Unix shells offer very few datatypes. Bash and some other shells extend this by
|
|||||||
|
|
||||||
==== Display defined functions ====
|
==== Display defined functions ====
|
||||||
''declare -f'' can be used to display all defined functions...
|
''declare -f'' can be used to display all defined functions...
|
||||||
<code>
|
<code>
|
||||||
$ declare -f
|
$ declare -f
|
||||||
foo ()
|
foo ()
|
||||||
{
|
{
|
||||||
@ -71,21 +71,21 @@ world ()
|
|||||||
{
|
{
|
||||||
echo "Hello World!"
|
echo "Hello World!"
|
||||||
}
|
}
|
||||||
</code>
|
</code>
|
||||||
...or just a specific defined function.
|
...or just a specific defined function.
|
||||||
<code>
|
<code>
|
||||||
$ declare -f foo
|
$ declare -f foo
|
||||||
foo ()
|
foo ()
|
||||||
{
|
{
|
||||||
echo "FOO is BAR"
|
echo "FOO is BAR"
|
||||||
}
|
}
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== Nameref ====
|
==== 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.
|
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.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
# Sum a set of arrays and assign the result indirectly, also printing each intermediary result (without portability workarounds)
|
# Sum a set of arrays and assign the result indirectly, also printing each intermediary result (without portability workarounds)
|
||||||
# sum name arrname [ arrname ... ]
|
# sum name arrname [ arrname ... ]
|
||||||
function sum {
|
function sum {
|
||||||
@ -101,8 +101,8 @@ function sum {
|
|||||||
a=(1 2 3) b=(6 5 4) c=(2 4 6)
|
a=(1 2 3) b=(6 5 4) c=(2 4 6)
|
||||||
sum total a b c
|
sum total a b c
|
||||||
printf 'Final value of "total" is: %d\n' "$total"
|
printf 'Final value of "total" is: %d\n' "$total"
|
||||||
</code>
|
</code>
|
||||||
<div hide>
|
<div hide>
|
||||||
function sum {
|
function sum {
|
||||||
typeset -n _result=$1
|
typeset -n _result=$1
|
||||||
shift
|
shift
|
||||||
@ -118,7 +118,7 @@ function sum {
|
|||||||
a=(1 2 3); b=(6 5 4); c=(2 4 6)
|
a=(1 2 3); b=(6 5 4); c=(2 4 6)
|
||||||
sum total a b c
|
sum total a b c
|
||||||
printf 'Final value of "total" is: %d\n' "$total"
|
printf 'Final value of "total" is: %d\n' "$total"
|
||||||
</div>
|
</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.
|
''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.
|
||||||
|
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
====== The echo builtin command ======
|
====== The echo builtin command ======
|
||||||
|
|
||||||
===== Synopsis =====
|
===== Synopsis =====
|
||||||
<code>
|
<code>
|
||||||
echo [-neE] [arg ...]
|
echo [-neE] [arg ...]
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Description =====
|
===== Description =====
|
||||||
''echo'' outputs it's args to stdout, separated by spaces, followed by a newline. The return status is always ''0''. If the [[commands:builtin:shopt|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 ''<nowiki>--</nowiki>'' as the end of options, and will simply print this string if given.
|
''echo'' outputs it's args to stdout, separated by spaces, followed by a newline. The return status is always ''0''. If the [[commands:builtin:shopt|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 ''<nowiki>--</nowiki>'' as the end of options, and will simply print this string if given.
|
||||||
|
|
||||||
==== Options ====
|
==== Options ====
|
||||||
^Option ^Description ^
|
^Option ^Description ^
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
====== The eval builtin command ======
|
====== The eval builtin command ======
|
||||||
===== Synopsis =====
|
===== Synopsis =====
|
||||||
<code>eval: eval [arg ...]</code>
|
<code>eval: eval [arg ...]</code>
|
||||||
|
|
||||||
===== Description =====
|
===== Description =====
|
||||||
|
|
||||||
@ -9,21 +9,21 @@
|
|||||||
===== Examples =====
|
===== Examples =====
|
||||||
In this example, the literal text within the [[syntax:redirection#here_documents|here-document]] is executed as Bash code exactly as though it were to appear within the script in place of the ''eval'' command below it.
|
In this example, the literal text within the [[syntax:redirection#here_documents|here-document]] is executed as Bash code exactly as though it were to appear within the script in place of the ''eval'' command below it.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
{ myCode=$(</dev/stdin); } <<\EOF
|
{ myCode=$(</dev/stdin); } <<\EOF
|
||||||
... arbitrary bash code here ...
|
... arbitrary bash code here ...
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
eval "$myCode"
|
eval "$myCode"
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== Expansion side-effects ====
|
==== 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).
|
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.
|
This code defines a set of identical functions using the supplied names. ''eval'' is the only way to achieve this effect.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
main() {
|
main() {
|
||||||
local fun='() { echo "$FUNCNAME"; }' x
|
local fun='() { echo "$FUNCNAME"; }' x
|
||||||
|
|
||||||
@ -35,17 +35,17 @@ main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
main "$@"
|
main "$@"
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== Using printf %q ====
|
==== Using printf %q ====
|
||||||
The ''[[commands:builtin:printf|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.
|
The ''[[commands:builtin:printf|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.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
while (( ++n <= 5 )) || ! evalBall="eval $evalBall"; do
|
while (( ++n <= 5 )) || ! evalBall="eval $evalBall"; do
|
||||||
printf -v evalBall 'eval %q' "printf $n;${evalBall-printf '0\n'}"
|
printf -v evalBall 'eval %q' "printf $n;${evalBall-printf '0\n'}"
|
||||||
done
|
done
|
||||||
$evalBall
|
$evalBall
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
The above example is mostly fun and games but illustrates the ''printf %q'' property.
|
The above example is mostly fun and games but illustrates the ''printf %q'' property.
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ Since all current POSIX-compatible shells lack support for [[http://en.wikipedia
|
|||||||
|
|
||||||
This example shows [[http://en.wikipedia.org/wiki/Partial_application | partial application]] using ''eval''.
|
This example shows [[http://en.wikipedia.org/wiki/Partial_application | partial application]] using ''eval''.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
function partial {
|
function partial {
|
||||||
eval shift 2 \; function "$1" \{ "$2" "$(printf '%q ' "${@:3}")" '"$@"; }'
|
eval shift 2 \; function "$1" \{ "$2" "$(printf '%q ' "${@:3}")" '"$@"; }'
|
||||||
}
|
}
|
||||||
@ -62,7 +62,7 @@ function partial {
|
|||||||
function repeat {
|
function repeat {
|
||||||
[[ $1 == +([0-9]) ]] || return
|
[[ $1 == +([0-9]) ]] || return
|
||||||
typeset n
|
typeset n
|
||||||
while ((n++ < $1)); do
|
while ((n++ < $1)); do
|
||||||
"${@:2}"
|
"${@:2}"
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
@ -70,29 +70,29 @@ function repeat {
|
|||||||
partial print3 repeat 3 printf '%s ' # Create a new function named print3
|
partial print3 repeat 3 printf '%s ' # Create a new function named print3
|
||||||
print3 hi # Print "hi" 3 times
|
print3 hi # Print "hi" 3 times
|
||||||
echo
|
echo
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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.
|
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 =====
|
===== 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 <del>ksh93</del> and zsh don't do this properly ([[http://article.gmane.org/gmane.comp.programming.tools.ast.devel/686|fixed]] 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.
|
* 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 <del>ksh93</del> and zsh don't do this properly ([[http://article.gmane.org/gmane.comp.programming.tools.ast.devel/686|fixed]] 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'' is another one of the few Bash builtins with keyword-like conditional parsing of arguments that are in the form of compound assignments.
|
||||||
<code>
|
<code>
|
||||||
$ ( eval a=( a b\\ c d ); printf '<%s> ' "${a[@]}"; echo ) # Only works in Bash.
|
$ ( eval a=( a b\\ c d ); printf '<%s> ' "${a[@]}"; echo ) # Only works in Bash.
|
||||||
<a> <b c> <d>
|
<a> <b c> <d>
|
||||||
$ ( x=a; eval "$x"=( a b\\ c d ); printf '<%s> ' "${a[@]}"; echo ) # Argument is no longer in the form of a valid assignment, therefore ordinary parsing rules apply.
|
$ ( x=a; eval "$x"=( a b\\ c d ); printf '<%s> ' "${a[@]}"; echo ) # Argument is no longer in the form of a valid assignment, therefore ordinary parsing rules apply.
|
||||||
-bash: syntax error near unexpected token `('
|
-bash: syntax error near unexpected token `('
|
||||||
$ ( x=a; eval "$x"'=( a b\ c d )'; printf '<%s> ' "${a[@]}"; echo ) # Proper quoting then gives us the expected results.
|
$ ( x=a; eval "$x"'=( a b\ c d )'; printf '<%s> ' "${a[@]}"; echo ) # Proper quoting then gives us the expected results.
|
||||||
<a> <b c> <d>
|
<a> <b c> <d>
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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.
|
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. [[commands/builtin/declare | declare]], the initial expansion is still subject to all expansions including [[syntax/expansion/wordsplit | word-splitting]] and [[syntax/expansion/glob | pathname expansion]].
|
In the case of ''eval'' it isn't recommended to use this behavior, because unlike e.g. [[commands/builtin/declare | declare]], the initial expansion is still subject to all expansions including [[syntax/expansion/wordsplit | word-splitting]] and [[syntax/expansion/glob | pathname expansion]].
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
$ ( set -x; touch 'x+=(\[[123]\]=*)' 'x+=([3]=yo)'; eval x+=(*); echo "${x[@]}" )
|
$ ( set -x; touch 'x+=(\[[123]\]=*)' 'x+=([3]=yo)'; eval x+=(*); echo "${x[@]}" )
|
||||||
+ touch 'x+=(\[[123]\]=*)' 'x+=([3]=yo)'
|
+ touch 'x+=(\[[123]\]=*)' 'x+=([3]=yo)'
|
||||||
+ eval 'x+=(\[[123]\]=*)' 'x+=([3]=yo)'
|
+ eval 'x+=(\[[123]\]=*)' 'x+=([3]=yo)'
|
||||||
@ -100,7 +100,7 @@ In the case of ''eval'' it isn't recommended to use this behavior, because unlik
|
|||||||
++ x+=([3]=yo)
|
++ x+=([3]=yo)
|
||||||
+ echo '[[123]]=*' yo
|
+ echo '[[123]]=*' yo
|
||||||
[[123]]=* yo
|
[[123]]=* yo
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Other commands known to be affected by compound assignment arguments include: [[commands/builtin/let | let]], [[commands/builtin/declare | declare]], [[commands/builtin/typeset | typeset]], [[commands/builtin/local | local]], [[commands/builtin/export | export]], and [[commands/builtin/readonly | readonly]]. More oddities below show both similarities and differences to commands like [[commands/builtin/declare | declare]]. The rules for ''eval'' appear identical to those of [[commands/builtin/let | let]].
|
Other commands known to be affected by compound assignment arguments include: [[commands/builtin/let | let]], [[commands/builtin/declare | declare]], [[commands/builtin/typeset | typeset]], [[commands/builtin/local | local]], [[commands/builtin/export | export]], and [[commands/builtin/readonly | readonly]]. More oddities below show both similarities and differences to commands like [[commands/builtin/declare | declare]]. The rules for ''eval'' appear identical to those of [[commands/builtin/let | let]].
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
====== The exec builtin command ======
|
====== The exec builtin command ======
|
||||||
|
|
||||||
===== Synopsis =====
|
===== Synopsis =====
|
||||||
<code>exec [-a NAME] [-cl] [COMMAND] [ARG...] [REDIRECTION...]</code>
|
<code>exec [-a NAME] [-cl] [COMMAND] [ARG...] [REDIRECTION...]</code>
|
||||||
|
|
||||||
===== Description =====
|
===== Description =====
|
||||||
The ''exec'' builtin command is used to
|
The ''exec'' builtin command is used to
|
||||||
@ -27,7 +27,7 @@ If only redirections are given, the redirections affect the current shell withou
|
|||||||
===== Examples =====
|
===== Examples =====
|
||||||
|
|
||||||
==== Wrapper around a program ====
|
==== Wrapper around a program ====
|
||||||
<code bash>
|
<code bash>
|
||||||
myprog=/bin/ls
|
myprog=/bin/ls
|
||||||
echo "This is the wrapper script, it will exec $myprog"
|
echo "This is the wrapper script, it will exec $myprog"
|
||||||
|
|
||||||
@ -35,30 +35,30 @@ echo "This is the wrapper script, it will exec $myprog"
|
|||||||
# well, stuff a wrapper is there for
|
# well, stuff a wrapper is there for
|
||||||
|
|
||||||
exec "$myprog" "$@"
|
exec "$myprog" "$@"
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== Open a file as input for the script ====
|
==== Open a file as input for the script ====
|
||||||
<code bash>
|
<code bash>
|
||||||
# open it
|
# open it
|
||||||
exec 3< input.txt
|
exec 3< input.txt
|
||||||
|
|
||||||
# for example: read one line from the file(-descriptor)
|
# for example: read one line from the file(-descriptor)
|
||||||
read -u 3 LINE
|
read -u 3 LINE
|
||||||
# or
|
# or
|
||||||
read LINE <&3
|
read LINE <&3
|
||||||
|
|
||||||
# finally, close it
|
# finally, close it
|
||||||
exec 3<&-
|
exec 3<&-
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== Overall script logfile ====
|
==== 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:
|
To redirect the whole ''stdout'' and ''stderr'' of the shell or shellscript to a file, you can use the ''exec'' builtin command:
|
||||||
<code bash>
|
<code bash>
|
||||||
exec >/var/adm/my.log 2>&1
|
exec >/var/adm/my.log 2>&1
|
||||||
|
|
||||||
# script continues here...
|
# script continues here...
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Portability considerations =====
|
===== Portability considerations =====
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
====== The exit builtin command ======
|
====== The exit builtin command ======
|
||||||
|
|
||||||
===== Synopsis =====
|
===== Synopsis =====
|
||||||
<code>exit [N]</code>
|
<code>exit [N]</code>
|
||||||
|
|
||||||
===== Description =====
|
===== Description =====
|
||||||
The ''exit'' command terminates the current shell (or script).
|
The ''exit'' command terminates the current shell (or script).
|
||||||
@ -25,9 +25,9 @@ Naturally, you can't ask for the exit status from within the shell that executed
|
|||||||
===== Examples =====
|
===== Examples =====
|
||||||
|
|
||||||
==== Exit the shell and explicitely set its exit status ====
|
==== Exit the shell and explicitely set its exit status ====
|
||||||
<code>
|
<code>
|
||||||
exit 3
|
exit 3
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Portability considerations =====
|
===== Portability considerations =====
|
||||||
* if ''N'' is specified, but its value is not between 0 and 255 inclusively, the exit status is undefined.
|
* if ''N'' is specified, but its value is not between 0 and 255 inclusively, the exit status is undefined.
|
||||||
|
@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
===== Synopsis =====
|
===== Synopsis =====
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
export [-fn] [NAME[=VALUE] ...]
|
export [-fn] [NAME[=VALUE] ...]
|
||||||
export -p
|
export -p
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Description =====
|
===== Description =====
|
||||||
|
|
||||||
@ -29,14 +29,14 @@ An argument of ''%%--%%'' disables further option processing.
|
|||||||
===== Examples =====
|
===== Examples =====
|
||||||
|
|
||||||
Set the display to use when launching a GUI application (useful during SSH sessions):
|
Set the display to use when launching a GUI application (useful during SSH sessions):
|
||||||
<code>
|
<code>
|
||||||
export DISPLAY=":0"
|
export DISPLAY=":0"
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Set your default text editor (e.g. SublimeText):
|
Set your default text editor (e.g. SublimeText):
|
||||||
<code>
|
<code>
|
||||||
export EDITOR=subl
|
export EDITOR=subl
|
||||||
</code>
|
</code>
|
||||||
===== Portability considerations =====
|
===== Portability considerations =====
|
||||||
|
|
||||||
* in POSIX(r), only the ''-p'' option is specified
|
* in POSIX(r), only the ''-p'' option is specified
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<label class="block control-label" for="focus__this"><span>Username</span> <input type="text" id="focus__this" name="u" class="edit form-control"/></label><br/>
|
<label class="block control-label" for="focus__this"><span>Username</span> <input type="text" id="focus__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="block control-label"><span>Password</span> <input type="password" name="p" class="edit form-control"/></label><br/>
|
||||||
<label class="simple control-label" for="remember__me"><input type="checkbox" id="remember__me" name="r" value="1" class="checkbox-inline"/> <span>Remember me</span></label>
|
<label class="simple control-label" for="remember__me"><input type="checkbox" id="remember__me" name="r" value="1" class="checkbox-inline"/> <span>Remember me</span></label>
|
||||||
<div id="plugin__captcha_wrapper"><input type="hidden" name="13ecbdf86c5379f064d2ee7d7cfc9263" value="guYldxsoHtyI6Sc3BwVa2fZOrJpGvuyc+1/gLza+u+c="/><label for="plugin__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>
|
<div id="plugin__captcha_wrapper"><input type="hidden" name="13ecbdf86c5379f064d2ee7d7cfc9263" value="guYldxsoHtyI6Sc3BwVa2fZOrJpGvuyc+1/gLza+u+c="/><label for="plugin__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>
|
</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>
|
<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>
|
||||||
@ -89,7 +89,7 @@
|
|||||||
<span class="visible-lg-block"></span>
|
<span class="visible-lg-block"></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<img src="/web/20211127235659im_/https://wiki.bash-hackers.org/lib/exe/indexer.php?id=commands%3Abuiltin%3Afalse&1638057419" width="2" height="1" alt=""/>
|
<img src="/web/20211127235659im_/https://wiki.bash-hackers.org/lib/exe/indexer.php?id=commands%3Abuiltin%3Afalse&1638057419" width="2" height="1" alt=""/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
====== The kill builtin command ======
|
====== The kill builtin command ======
|
||||||
|
|
||||||
===== Synopsis =====
|
===== Synopsis =====
|
||||||
<code>kill [-s SIGNAL | -n SIGNALNUMBER | -SIGNAL] PID|JOB</code>
|
<code>kill [-s SIGNAL | -n SIGNALNUMBER | -SIGNAL] PID|JOB</code>
|
||||||
<code>kill -l|-L [SIGNAL...]</code>
|
<code>kill -l|-L [SIGNAL...]</code>
|
||||||
|
|
||||||
===== Description =====
|
===== Description =====
|
||||||
The ''kill'' command is used to send signals to processes specified by their ''PID'' or their ''JOB''-specification.
|
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:
|
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.
|
* 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 (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>'')
|
* 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.
|
Without any specified signal, the command sends the ''SIGTERM''-signal.
|
||||||
|
|
||||||
@ -39,23 +39,23 @@ The ''kill'' command is a Bash builtin command instead of relying on the externa
|
|||||||
|
|
||||||
==== List supported signals ====
|
==== List supported signals ====
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
kill -l
|
kill -l
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== Send KILL to a process ID ====
|
==== Send KILL to a process ID ====
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
kill -9 12345
|
kill -9 12345
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
kill -KILL 12345
|
kill -KILL 12345
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
kill -SIGKILL 12345
|
kill -SIGKILL 12345
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Portability considerations =====
|
===== Portability considerations =====
|
||||||
|
|
||||||
|
@ -2,40 +2,40 @@
|
|||||||
|
|
||||||
===== Synopsis =====
|
===== Synopsis =====
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
let arg [arg ...]
|
let arg [arg ...]
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Description =====
|
===== Description =====
|
||||||
The ''let'' builtin command evaluates each supplied word from left to right as an [[syntax:arith_expr | arithmetic expression]] and returns an exit code according to the truth value of the rightmost expression.
|
The ''let'' builtin command evaluates each supplied word from left to right as an [[syntax:arith_expr | arithmetic expression]] and returns an exit code according to the truth value of the rightmost expression.
|
||||||
* 0 (TRUE) when ''arg'' evaluated to not 0 (arithmetic "true")
|
* 0 (TRUE) when ''arg'' evaluated to not 0 (arithmetic "true")
|
||||||
* 1 (FALSE) when ''arg'' evaluated to 0 (arithmetic "false")
|
* 1 (FALSE) when ''arg'' evaluated to 0 (arithmetic "false")
|
||||||
For this return code mapping, please see [[syntax:arith_expr#arithmetic_expressions_and_return_codes | this section]]. They work in the same way as ''<nowiki>((</nowiki>''.
|
For this return code mapping, please see [[syntax:arith_expr#arithmetic_expressions_and_return_codes | this section]]. They work in the same way as ''<nowiki>((</nowiki>''.
|
||||||
|
|
||||||
===== Examples =====
|
===== Examples =====
|
||||||
''let'' is very similar to [[syntax:ccmd:arithmetic_eval | ((]] - the only difference being ''let'' is a builtin (simple command), and ''<nowiki>((</nowiki>'' 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 ''<nowiki>((</nowiki>'' aren't subject to [[syntax:expansion:wordsplit | word-splitting]] or [[syntax:expansion:globs | pathname expansion]] (almost never desirable for arithmetic). For this reason, **the [[syntax:ccmd:arithmetic_eval | arithmetic compound command]] should generally be preferred over ''let''**.
|
''let'' is very similar to [[syntax:ccmd:arithmetic_eval | ((]] - the only difference being ''let'' is a builtin (simple command), and ''<nowiki>((</nowiki>'' 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 ''<nowiki>((</nowiki>'' aren't subject to [[syntax:expansion:wordsplit | word-splitting]] or [[syntax:expansion:globs | pathname expansion]] (almost never desirable for arithmetic). For this reason, **the [[syntax:ccmd:arithmetic_eval | arithmetic compound command]] should generally be preferred over ''let''**.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
$ let 'b = a' "(a += 3) + $((a = 1)), b++"
|
$ let 'b = a' "(a += 3) + $((a = 1)), b++"
|
||||||
$ echo "$a - $b - $?"
|
$ echo "$a - $b - $?"
|
||||||
4 - 2 - 0
|
4 - 2 - 0
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Is equivalent to the [[syntax:ccmd:arithmetic_eval | arithmetic evaluation compound command]]:
|
Is equivalent to the [[syntax:ccmd:arithmetic_eval | arithmetic evaluation compound command]]:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
$ (( b = a, (a += 3) + $((a = 1)), b++ ))
|
$ (( b = a, (a += 3) + $((a = 1)), b++ ))
|
||||||
$ echo "$a - $b - $?"
|
$ echo "$a - $b - $?"
|
||||||
4 - 2 - 0
|
4 - 2 - 0
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
<WRAP info>
|
<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.
|
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>
|
</WRAP>
|
||||||
|
|
||||||
Unlike ''<nowiki>((</nowiki>'', 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.
|
Unlike ''<nowiki>((</nowiki>'', 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.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
~ $ ( y=1+1 let x=y; declare -p x y )
|
~ $ ( y=1+1 let x=y; declare -p x y )
|
||||||
declare -- x="2"
|
declare -- x="2"
|
||||||
bash: declare: y: not found
|
bash: declare: y: not found
|
||||||
@ -43,18 +43,18 @@ bash: declare: y: not found
|
|||||||
~ $ ( y=1+1 let x=y++; declare -p x y )
|
~ $ ( y=1+1 let x=y++; declare -p x y )
|
||||||
declare -- x="2"
|
declare -- x="2"
|
||||||
declare -- y="3"
|
declare -- y="3"
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
This can be useful in certain situations where a temporary variable is needed.
|
This can be useful in certain situations where a temporary variable is needed.
|
||||||
|
|
||||||
===== Portability considerations =====
|
===== Portability considerations =====
|
||||||
|
|
||||||
* the ''let'' command is not specified by POSIX(r). The portable alternative is: <code>[ "$(( <EXPRESSION> ))" -ne 0 ]</code>. To make portable scripts simpler and cleaner, ''let'' can be defined as: <code>
|
* the ''let'' command is not specified by POSIX(r). The portable alternative is: <code>[ "$(( <EXPRESSION> ))" -ne 0 ]</code>. To make portable scripts simpler and cleaner, ''let'' can be defined as: <code>
|
||||||
# POSIX
|
# POSIX
|
||||||
let() {
|
let() {
|
||||||
IFS=, command eval test '$(($*))' -ne 0
|
IFS=, command eval test '$(($*))' -ne 0
|
||||||
}
|
}
|
||||||
</code> Aside from differences in supported arithmetic features, this should be identical to the Bash/Ksh ''let''.
|
</code> 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 [[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.
|
* It seems to be a common misunderstanding that ''let'' has some legacy purpose. Both ''let'' and [[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.
|
||||||
* [[http://pubs.opengroup.org/onlinepubs/9699919799/utilities/expr.html#tag_20_42 | expr(1)]] 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 [[syntax:arith_expr | arithmetic expansion]]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.
|
* [[http://pubs.opengroup.org/onlinepubs/9699919799/utilities/expr.html#tag_20_42 | expr(1)]] 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 [[syntax:arith_expr | arithmetic expansion]]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: [[commands/builtin/eval#portability_considerations | eval]] 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.
|
* For unknown reasons, ''let'' is one of the Bash commands with special parsing for arguments formatted like compound array assignments. See: [[commands/builtin/eval#portability_considerations | eval]] 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.
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
====== The local builtin command ======
|
====== The local builtin command ======
|
||||||
|
|
||||||
===== Synopsis =====
|
===== Synopsis =====
|
||||||
<code>
|
<code>
|
||||||
local [option] name[=value] ...
|
local [option] name[=value] ...
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Description =====
|
===== Description =====
|
||||||
|
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
====== The mapfile builtin command ======
|
====== The mapfile builtin command ======
|
||||||
|
|
||||||
===== Synopsis =====
|
===== Synopsis =====
|
||||||
<code>
|
<code>
|
||||||
mapfile [-n COUNT] [-O ORIGIN] [-s COUNT] [-t] [-u FD] [-C CALLBACK] [-c QUANTUM] [ARRAY]
|
mapfile [-n COUNT] [-O ORIGIN] [-s COUNT] [-t] [-u FD] [-C CALLBACK] [-c QUANTUM] [ARRAY]
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
readarray [-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]
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Description =====
|
===== Description =====
|
||||||
|
|
||||||
@ -32,7 +32,7 @@ While ''mapfile'' isn't a common or portable shell feature, it's functionality w
|
|||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
<code># eix --only-names -IC x11-drivers | { mapfile -t args; emerge -av1 "${args[@]}" <&1; }</code>
|
<code># eix --only-names -IC x11-drivers | { mapfile -t args; emerge -av1 "${args[@]}" <&1; }</code>
|
||||||
|
|
||||||
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
|
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
|
||||||
|
|
||||||
@ -42,20 +42,20 @@ This is one of the more unusual features of a Bash builtin. As far as I'm able t
|
|||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
<code>$ printf '%s\n' {1..5} | mapfile -c 1 -C 'printf . \#' )
|
<code>$ printf '%s\n' {1..5} | mapfile -c 1 -C 'printf . \#' )
|
||||||
.....</code>
|
.....</code>
|
||||||
|
|
||||||
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".
|
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".
|
||||||
<code>
|
<code>
|
||||||
$ foo() { echo "|$1|"; }; mapfile -n 11 -c 2 -C 'foo' <file
|
$ foo() { echo "|$1|"; }; mapfile -n 11 -c 2 -C 'foo' <file
|
||||||
|2|
|
|2|
|
||||||
|4|
|
|4|
|
||||||
etc..
|
etc..
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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:
|
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:
|
||||||
|
|
||||||
<code>$ { printf 'input%s\n' {1..10} | mapfile -c 1 -C '>&$(( (${#x[@]} % 2) + 3 )) printf -- "%.sprefix %s"' x; } 3>outfile0 4>outfile1
|
<code>$ { printf 'input%s\n' {1..10} | mapfile -c 1 -C '>&$(( (${#x[@]} % 2) + 3 )) printf -- "%.sprefix %s"' x; } 3>outfile0 4>outfile1
|
||||||
$ cat outfile{0,1}
|
$ cat outfile{0,1}
|
||||||
prefix input1
|
prefix input1
|
||||||
prefix input3
|
prefix input3
|
||||||
@ -67,13 +67,13 @@ prefix input4
|
|||||||
prefix input6
|
prefix input6
|
||||||
prefix input8
|
prefix input8
|
||||||
prefix input10
|
prefix input10
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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.
|
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).
|
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).
|
||||||
|
|
||||||
<code>$ y=( 'odd[j]' 'even[j++]' ); printf 'input%s\n' {1..10} | { mapfile -tc 1 -C 'printf -v "${y[${#x[@]} % 2]}" -- "%.sprefix %s"' x; printf '%s\n' "${odd[@]}" '' "${even[@]}"; }
|
<code>$ y=( 'odd[j]' 'even[j++]' ); printf 'input%s\n' {1..10} | { mapfile -tc 1 -C 'printf -v "${y[${#x[@]} % 2]}" -- "%.sprefix %s"' x; printf '%s\n' "${odd[@]}" '' "${even[@]}"; }
|
||||||
prefix input1
|
prefix input1
|
||||||
prefix input3
|
prefix input3
|
||||||
prefix input5
|
prefix input5
|
||||||
@ -85,11 +85,11 @@ prefix input4
|
|||||||
prefix input6
|
prefix input6
|
||||||
prefix input8
|
prefix input8
|
||||||
prefix input10
|
prefix input10
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
showRecord() {
|
showRecord() {
|
||||||
@ -116,7 +116,7 @@ main() {
|
|||||||
showRecord "$1"
|
showRecord "$1"
|
||||||
}
|
}
|
||||||
|
|
||||||
main "$1" <<-"EOF"
|
main "$1" <<-"EOF"
|
||||||
fabric.domain:123
|
fabric.domain:123
|
||||||
routex:1
|
routex:1
|
||||||
routey:2
|
routey:2
|
||||||
@ -124,7 +124,7 @@ fabric.domain:321
|
|||||||
routex:6
|
routex:6
|
||||||
routey:4
|
routey:4
|
||||||
EOF
|
EOF
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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.
|
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 =====
|
===== Bugs =====
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
====== The printf command ======
|
====== The printf command ======
|
||||||
|
|
||||||
<div center round todo box 70%>
|
<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!
|
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>
|
||||||
<div center round tip 70%>
|
<div center round tip 70%>
|
||||||
__**Attention:**__ This is about the Bash-builtin command ''printf'' - however, the description should be nearly identical for an external command that follows POSIX(r).
|
__**Attention:**__ This is about the Bash-builtin command ''printf'' - however, the description should be nearly identical for an external command that follows POSIX(r).
|
||||||
|
|
||||||
[[http://www.gnu.org/software/gawk/manual/gawk.html#Printf|GNU Awk]] expects a comma after the format string and between each of the arguments of a **printf** command. For examples, see: [[printf?&#using_printf_inside_of_awk|code snippet]].
|
[[http://www.gnu.org/software/gawk/manual/gawk.html#Printf|GNU Awk]] expects a comma after the format string and between each of the arguments of a **printf** command. For examples, see: [[printf?&#using_printf_inside_of_awk|code snippet]].
|
||||||
</div>
|
</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.
|
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.
|
||||||
|
|
||||||
@ -21,16 +21,16 @@ Beside other reasons, POSIX(r) has a very good argument to recommend it: Both hi
|
|||||||
|
|
||||||
===== Syntax =====
|
===== Syntax =====
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
printf <FORMAT> <ARGUMENTS...>
|
printf <FORMAT> <ARGUMENTS...>
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
The text format is given in ''<FORMAT>'', while all arguments the formatstring may point to are given after that, here, indicated by ''<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:
|
Thus, a typical ''printf''-call looks like:
|
||||||
<code>
|
<code>
|
||||||
printf "Surname: %s\nName: %s\n" "$SURNAME" "$FIRSTNAME"
|
printf "Surname: %s\nName: %s\n" "$SURNAME" "$FIRSTNAME"
|
||||||
</code>
|
</code>
|
||||||
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!).
|
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 ====
|
==== Options ====
|
||||||
@ -39,15 +39,15 @@ where ''"Surname: %s\nName: %s\n"'' is the format specification, and t
|
|||||||
|
|
||||||
The ''-v'' Option can't assign directly to array indexes in Bash versions older than Bash 4.1.
|
The ''-v'' Option can't assign directly to array indexes in Bash versions older than Bash 4.1.
|
||||||
|
|
||||||
<note warning>
|
<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.
|
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.
|
||||||
<code>
|
<code>
|
||||||
$ var='-vx[$(echo hi >&2)]'; printf "$var" hi; declare -p x
|
$ var='-vx[$(echo hi >&2)]'; printf "$var" hi; declare -p x
|
||||||
hi
|
hi
|
||||||
declare -a x='([0]="hi")'
|
declare -a x='([0]="hi")'
|
||||||
</code>
|
</code>
|
||||||
...where the echo can of course be replaced with any arbitrary command. If you must, either specify a hard-coded format string or use <nowiki>--</nowiki> to signal the end of options. The exact same issue also applies to [[commands/builtin/read | read]], and a similar one to [[commands/builtin/mapfile | mapfile]], though performing expansions into their arguments is less common.
|
...where the echo can of course be replaced with any arbitrary command. If you must, either specify a hard-coded format string or use <nowiki>--</nowiki> to signal the end of options. The exact same issue also applies to [[commands/builtin/read | read]], and a similar one to [[commands/builtin/mapfile | mapfile]], though performing expansions into their arguments is less common.
|
||||||
</note>
|
</note>
|
||||||
|
|
||||||
==== Arguments ====
|
==== Arguments ====
|
||||||
|
|
||||||
@ -59,24 +59,24 @@ Of course in shell-meaning the arguments are just strings, however, the common C
|
|||||||
|''0xN''|A hexadecimal number|
|
|''0xN''|A hexadecimal 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 double-quote infront of a character): interpreted as number (underlying codeset) **don't forget escaping**|
|
||||||
|''<nowiki>'X</nowiki>''|(a literal single-quote infront of a character): interpreted as number (underlying codeset) **don't forget escaping**|
|
|''<nowiki>'X</nowiki>''|(a literal single-quote infront of a character): interpreted as number (underlying codeset) **don't forget escaping**|
|
||||||
|
|
||||||
__**If more arguments than format specifiers**__ 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).
|
__**If more arguments than format specifiers**__ 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 [[syntax:expansion:wordsplit | word splitting]], as accidentally passing the wrong number of arguments can produce wildly different and unexpected results. See [[syntax:words | this article]].
|
Take care to avoid [[syntax:expansion:wordsplit | word splitting]], as accidentally passing the wrong number of arguments can produce wildly different and unexpected results. See [[syntax:words | this article]].
|
||||||
|
|
||||||
<note warning>
|
<note warning>
|
||||||
__**Again, attention:**__ 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''!):
|
__**Again, attention:**__ 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''!):
|
||||||
<code>
|
<code>
|
||||||
printf '%d\n' 08
|
printf '%d\n' 08
|
||||||
</code>
|
</code>
|
||||||
</note>
|
</note>
|
||||||
|
|
||||||
==== Format strings ====
|
==== 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.
|
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 ''<nowiki>%%</nowiki>'' in the format string.
|
To print a literal ''%'' (percent-sign), use ''<nowiki>%%</nowiki>'' in the format string.
|
||||||
|
|
||||||
__**Again:**__ Every format specifier expects an associated argument provided!
|
__**Again:**__ Every format specifier expects an associated argument provided!
|
||||||
|
|
||||||
@ -96,7 +96,7 @@ These specifiers have different names, depending who you ask. But they all mean
|
|||||||
|''%x''|Print the associated argument as **unsigned hexadecimal** number with lower-case hex-digits (a-f)|
|
|''%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)|
|
|''%X''|Same as ''%x'', but with upper-case hex-digits (A-F)|
|
||||||
|''%f''|Interpret and print the associated argument as **floating point** number|
|
|''%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''|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|
|
|''%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''|Interprets the associated argument as **double**, but prints it like ''%f'' or ''%e''|
|
||||||
|''%G''|Same as ''%g'', but print it like ''%E''|
|
|''%G''|Same as ''%g'', but print it like ''%E''|
|
||||||
@ -112,20 +112,20 @@ Some of the mentioned format specifiers can modify their behaviour by getting a
|
|||||||
==== Modifiers ====
|
==== 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:
|
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:
|
||||||
<code>printf "%50s\n" "This field is 50 characters wide..."</code>
|
<code>printf "%50s\n" "This field is 50 characters wide..."</code>
|
||||||
|
|
||||||
=== Field and printing modifiers ===
|
=== Field and printing modifiers ===
|
||||||
|
|
||||||
^Field output format^^
|
^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|
|
|''<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 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"''|
|
|''*''|**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|
|
|''#''|"Alternative format" for numbers: see table below|
|
||||||
|''-''|**Left-bound** text printing in the field (standard is **right-bound**)|
|
|''-''|**Left-bound** text printing in the field (standard is **right-bound**)|
|
||||||
|''0''|Pads numbers with zeros, not spaces|
|
|''0''|Pads numbers with zeros, not spaces|
|
||||||
|''<space>''|Pad a positive number with a space, where a minus (''-'') is for negative numbers|
|
|''<space>''|Pad a positive number with a space, where a minus (''-'') is for negative numbers|
|
||||||
|''+''|Prints all numbers **signed** (''+'' for positive, ''-'' for negative)|
|
|''+''|Prints all numbers **signed** (''+'' for positive, ''-'' for negative)|
|
||||||
|''<nowiki>'</nowiki>''|For decimal conversions, the thousands grouping separator is applied to the integer portion of the output according to the current LC_NUMERIC|
|
|''<nowiki>'</nowiki>''|For decimal conversions, the thousands grouping separator is applied to the integer portion of the output according to the current LC_NUMERIC|
|
||||||
|
|
||||||
__**The "alternative format" modifier ''#'':**__
|
__**The "alternative format" modifier ''#'':**__
|
||||||
|
|
||||||
@ -137,8 +137,8 @@ __**The "alternative format" modifier ''#'':**__
|
|||||||
|
|
||||||
=== Precision ===
|
=== 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):
|
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):
|
||||||
<code>printf "%.*f\n" 10 4,3</code>
|
<code>printf "%.*f\n" 10 4,3</code>
|
||||||
The format ''.*N'' to specify the N'th argument for precision does not work in Bash.
|
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!).
|
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!).
|
||||||
@ -156,19 +156,19 @@ These are interpreted if used anywhere in the format string, or in an argument c
|
|||||||
|''\r''|Prints a carriage-return|
|
|''\r''|Prints a carriage-return|
|
||||||
|''\t''|Prints a horizontal tabulator|
|
|''\t''|Prints a horizontal tabulator|
|
||||||
|''\v''|Prints a vertical tabulator|
|
|''\v''|Prints a vertical tabulator|
|
||||||
|''\"''|Prints a ''<nowiki>'</nowiki>''|
|
|''\"''|Prints a ''<nowiki>'</nowiki>''|
|
||||||
|''\?''|Prints a ''?''|
|
|''\?''|Prints a ''?''|
|
||||||
|''\<NNN>''|Interprets ''<NNN>'' as **octal** number and prints the corresponding character from the character set|
|
|''\<NNN>''|Interprets ''<NNN>'' as **octal** number and prints the corresponding character from the character set|
|
||||||
|''\0<NNN>''|same as ''\<NNN>''|
|
|''\0<NNN>''|same as ''\<NNN>''|
|
||||||
|''\x<NNN>''|Interprets ''<NNN>'' as **hexadecimal** number and prints the corresponding character from the character set (**3 digits**)|
|
|''\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<NNNN>''|same as ''\x<NNN>'', but **4 digits**|
|
||||||
|''\U<NNNNNNNN>''|same as ''\x<NNN>'', but **8 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:
|
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.|
|
|''\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: ''\<nowiki>'</nowiki>'', ''\"'', and ''\?'' are not removed.
|
* Backslashes in the escapes: ''\<nowiki>'</nowiki>'', ''\"'', and ''\?'' are not removed.
|
||||||
* Octal escapes beginning with ''\0'' may contain up to four digits. (POSIX specifies up to three).
|
* 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.
|
These are also respects in which ''%b'' differs from the escapes used by [[syntax/quoting#ansi_c_like_strings | $'...']] style quoting.
|
||||||
@ -200,17 +200,17 @@ This small loop prints all numbers from 0 to 127 in
|
|||||||
* decimal
|
* decimal
|
||||||
* octal
|
* octal
|
||||||
* hex
|
* hex
|
||||||
<code>
|
<code>
|
||||||
for ((x=0; x <= 127; x++)); do
|
for ((x=0; x <= 127; x++)); do
|
||||||
printf '%3d | %04o | 0x%02x\n' "$x" "$x" "$x"
|
printf '%3d | %04o | 0x%02x\n' "$x" "$x" "$x"
|
||||||
done
|
done
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== Ensure well-formatted MAC address ====
|
==== 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, ...):
|
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, ...):
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
the_mac="0:13:ce:7:7a:ad"
|
the_mac="0:13:ce:7:7a:ad"
|
||||||
|
|
||||||
# lowercase hex digits
|
# lowercase hex digits
|
||||||
@ -218,7 +218,7 @@ the_mac="$(printf "%02x:%02x:%02x:%02x:%02x:%02x" 0x${the_mac//:/
|
|||||||
|
|
||||||
# or the uppercase-digits variant
|
# or the uppercase-digits variant
|
||||||
the_mac="$(printf "%02X:%02X:%02X:%02X:%02X:%02X" 0x${the_mac//:/ 0x})"
|
the_mac="$(printf "%02X:%02X:%02X:%02X:%02X:%02X" 0x${the_mac//:/ 0x})"
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== Replacement echo ====
|
==== Replacement echo ====
|
||||||
|
|
||||||
@ -226,13 +226,13 @@ This code was found in Solaris manpage for echo(1).
|
|||||||
|
|
||||||
Solaris version of ''/usr/bin/echo'' is equivalent to:
|
Solaris version of ''/usr/bin/echo'' is equivalent to:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
printf "%b\n" "$*"
|
printf "%b\n" "$*"
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Solaris ''/usr/ucb/echo'' is equivalent to:
|
Solaris ''/usr/ucb/echo'' is equivalent to:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
if [ "X$1" = "X-n" ]
|
if [ "X$1" = "X-n" ]
|
||||||
then
|
then
|
||||||
shift
|
shift
|
||||||
@ -240,39 +240,39 @@ then
|
|||||||
else
|
else
|
||||||
printf "%s\n" "$*"
|
printf "%s\n" "$*"
|
||||||
fi
|
fi
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== prargs Implementation ====
|
==== prargs Implementation ====
|
||||||
|
|
||||||
Working off the replacement echo, here is a terse implementation of prargs:
|
Working off the replacement echo, here is a terse implementation of prargs:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
printf '"%b"\n' "$0" "$@" | nl -v0 -s": "
|
printf '"%b"\n' "$0" "$@" | nl -v0 -s": "
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== repeating a character (for example to print a line) ====
|
==== repeating a character (for example to print a line) ====
|
||||||
|
|
||||||
A small trick: Combining printf and parameter expansion to draw a line
|
A small trick: Combining printf and parameter expansion to draw a line
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
length=40
|
length=40
|
||||||
printf -v line '%*s' "$length"
|
printf -v line '%*s' "$length"
|
||||||
echo ${line// /-}
|
echo ${line// /-}
|
||||||
</code>
|
</code>
|
||||||
or:
|
or:
|
||||||
<code>
|
<code>
|
||||||
length=40
|
length=40
|
||||||
eval printf -v line '%.0s-' {1..$length}
|
eval printf -v line '%.0s-' {1..$length}
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== Replacement for some calls to date(1) ====
|
==== Replacement for some calls to date(1) ====
|
||||||
|
|
||||||
The ''%(...)T'' format string is a direct interface to ''strftime(3)''.
|
The ''%(...)T'' format string is a direct interface to ''strftime(3)''.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
$ printf 'This is week %(%U/%Y)T.\n' -1
|
$ printf 'This is week %(%U/%Y)T.\n' -1
|
||||||
This is week 52/2010.
|
This is week 52/2010.
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Please read the manpage of ''strftime(3)'' to get more information about the supported formats.
|
Please read the manpage of ''strftime(3)'' to get more information about the supported formats.
|
||||||
|
|
||||||
@ -282,7 +282,7 @@ Awk also derives its //printf()// function from C, and therefore has similar for
|
|||||||
|
|
||||||
In the following example, the two strings are concatenated by the intervening space so that no argument remains to fill the format.
|
In the following example, the two strings are concatenated by the intervening space so that no argument remains to fill the format.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
|
|
||||||
$ echo "Foo" | awk '{ printf "%s\n" $1 }'
|
$ echo "Foo" | awk '{ printf "%s\n" $1 }'
|
||||||
awk: (FILENAME=- FNR=1) fatal: not enough arguments to satisfy format string
|
awk: (FILENAME=- FNR=1) fatal: not enough arguments to satisfy format string
|
||||||
@ -290,21 +290,21 @@ awk: (FILENAME=- FNR=1) fatal: not enough arguments to satisfy format string
|
|||||||
Foo'
|
Foo'
|
||||||
^ ran out for this one
|
^ ran out for this one
|
||||||
|
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Simply replacing the space with a comma and adding parentheses yields correct awk syntax.
|
Simply replacing the space with a comma and adding parentheses yields correct awk syntax.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
$ echo "Foo" | awk '{ printf( "%s\n", $1 ) }'
|
$ echo "Foo" | awk '{ printf( "%s\n", $1 ) }'
|
||||||
Foo
|
Foo
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
echo "Foo" | awk '{ system( "printf \"%s\\n \" \"" $1 "\"" ) }'
|
echo "Foo" | awk '{ system( "printf \"%s\\n \" \"" $1 "\"" ) }'
|
||||||
Foo
|
Foo
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Differences from C, and portability considerations =====
|
===== Differences from C, and portability considerations =====
|
||||||
|
|
||||||
@ -313,19 +313,19 @@ Foo
|
|||||||
* 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.
|
* 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.
|
* Bash recognizes and skips over any characters present in the length modifiers specified by POSIX during format string parsing.
|
||||||
<code c|builtins/printf.def>
|
<code c|builtins/printf.def>
|
||||||
#define LENMODS "hjlLtz"
|
#define LENMODS "hjlLtz"
|
||||||
...
|
...
|
||||||
/* skip possible format modifiers */
|
/* skip possible format modifiers */
|
||||||
modstart = fmt;
|
modstart = fmt;
|
||||||
while (*fmt && strchr (LENMODS, *fmt))
|
while (*fmt && strchr (LENMODS, *fmt))
|
||||||
fmt++;
|
fmt++;
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
* 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.
|
* 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.
|
* 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.
|
||||||
<code>
|
<code>
|
||||||
# Illustrates Bash-like behavior. Redefining printf is usually unnecessary / not recommended.
|
# Illustrates Bash-like behavior. Redefining printf is usually unnecessary / not recommended.
|
||||||
function printf {
|
function printf {
|
||||||
case $1 in
|
case $1 in
|
||||||
@ -345,7 +345,7 @@ printf -v 'foo[2]' '%d\n' "$(cut -d ' ' -f 1 /proc/self/stat)"
|
|||||||
typeset -p foo
|
typeset -p foo
|
||||||
# 22461
|
# 22461
|
||||||
# typeset -a foo=([2]=22461)
|
# typeset -a foo=([2]=22461)
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
* The optional Bash loadable ''print'' may be useful for ksh compatibility and to overcome some of [[commands/builtin/echo | 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.
|
* The optional Bash loadable ''print'' may be useful for ksh compatibility and to overcome some of [[commands/builtin/echo | 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.
|
||||||
|
|
||||||
|
@ -2,25 +2,25 @@
|
|||||||
//read something about read here!//
|
//read something about read here!//
|
||||||
|
|
||||||
===== Synopsis =====
|
===== Synopsis =====
|
||||||
<code>
|
<code>
|
||||||
read [-ers] [-u <FD>] [-t <TIMEOUT>] [-p <PROMPT>] [-a <ARRAY>] [-n <NCHARS>] [-N <NCHARS>] [-d <DELIM>] [-i <TEXT>] [<NAME...>]
|
read [-ers] [-u <FD>] [-t <TIMEOUT>] [-p <PROMPT>] [-a <ARRAY>] [-n <NCHARS>] [-N <NCHARS>] [-d <DELIM>] [-i <TEXT>] [<NAME...>]
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Description =====
|
===== 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...>''.
|
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.
|
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 [[syntax:shellvars#IFS|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.
|
If ''<NAME...>'' is given, the line is word-split using [[syntax:shellvars#IFS|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%>
|
<WRAP center round info 90%>
|
||||||
If no ''<NAME>'' is given, the whole line read (without performing word-splitting!) is assigned to the shell variable [[syntax:shellvars#REPLY|REPLY]]. Then, ''REPLY'' really contains the line as it was read, without stripping pre- and postfix spaces and other things!
|
If no ''<NAME>'' is given, the whole line read (without performing word-splitting!) is assigned to the shell variable [[syntax:shellvars#REPLY|REPLY]]. Then, ''REPLY'' really contains the line as it was read, without stripping pre- and postfix spaces and other things!
|
||||||
<code>
|
<code>
|
||||||
while read -r; do
|
while read -r; do
|
||||||
printf '"%s"\n' "$REPLY"
|
printf '"%s"\n' "$REPLY"
|
||||||
done <<<" a line with prefix and postfix space "
|
done <<<" a line with prefix and postfix space "
|
||||||
</code>
|
</code>
|
||||||
</WRAP>
|
</WRAP>
|
||||||
|
|
||||||
If a timeout is given, or if the shell variable [[syntax:shellvars#TMOUT|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.
|
If a timeout is given, or if the shell variable [[syntax:shellvars#TMOUT|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.
|
||||||
|
|
||||||
@ -29,43 +29,43 @@ If a timeout is given, or if the shell variable [[syntax:shellvars#TMOUT|TMOUT]]
|
|||||||
==== Options ====
|
==== Options ====
|
||||||
|
|
||||||
^Option^Description^
|
^Option^Description^
|
||||||
|''-a <ARRAY>''|read the data word-wise into the specified array ''<ARRAY>'' instead of normal variables|
|
|''-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>''|
|
|''-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''|
|
|''-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|
|
|''-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, then quits|
|
||||||
|''-N <NCHARS>''|reads ''<NCHARS>'' characters of input, //ignoring any delimiter//, 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|
|
|''-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|
|
|''-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!)|
|
|''-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.|
|
|''-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)|
|
|''-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.
|
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'':
|
Of course it's valid to set individual array elements without using ''-a'':
|
||||||
<code>
|
<code>
|
||||||
read MYARRAY[5]
|
read MYARRAY[5]
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
<WRAP center round important 90%>
|
<WRAP center round important 90%>
|
||||||
|
|
||||||
Reading into array elements using the syntax above **may cause [[syntax:expansion:globs | pathname expansion]] to occur**.
|
Reading into array elements using the syntax above **may cause [[syntax:expansion:globs | pathname expansion]] 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
|
Example: You are in a directory with a file named ''x1'', and you want to read into an array ''x'', index ''1'' with
|
||||||
<code>
|
<code>
|
||||||
read x[1]
|
read x[1]
|
||||||
</code>
|
</code>
|
||||||
then pathname expansion will expand to the filename ''x1'' and break your processing!
|
then pathname expansion will expand to the filename ''x1'' and break your processing!
|
||||||
|
|
||||||
Even worse, if ''nullglob'' is set, your array/index will disappear.
|
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:
|
To avoid this, either **disable pathname expansion** or **quote** the array name and index:
|
||||||
<code>
|
<code>
|
||||||
read 'x[1]'
|
read 'x[1]'
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
</WRAP>
|
</WRAP>
|
||||||
|
|
||||||
==== Return status ====
|
==== Return status ====
|
||||||
|
|
||||||
@ -73,7 +73,7 @@ read 'x[1]'
|
|||||||
|0 |no error |
|
|0 |no error |
|
||||||
|0 |error when assigning to a read-only variable ((fixed in 4.2-rc1)) |
|
|0 |error when assigning to a read-only variable ((fixed in 4.2-rc1)) |
|
||||||
|2 |invalid option |
|
|2 |invalid option |
|
||||||
|>128 |timeout (see ''-t'') |
|
|>128 |timeout (see ''-t'') |
|
||||||
|!=0 |invalid filedescriptor supplied to ''-u'' |
|
|!=0 |invalid filedescriptor supplied to ''-u'' |
|
||||||
|!=0 |end-of-file reached |
|
|!=0 |end-of-file reached |
|
||||||
|
|
||||||
@ -81,36 +81,36 @@ read 'x[1]'
|
|||||||
|
|
||||||
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 [[http://pubs.opengroup.org/onlinepubs/9699919799/utilities/read.html#tag_20_109 | POSIX read]] spec.
|
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 [[http://pubs.opengroup.org/onlinepubs/9699919799/utilities/read.html#tag_20_109 | POSIX read]] spec.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
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: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: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: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: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 "<$x><$y>"
|
2012-05-23 13:49:38 geirha # read x y <<< 'foo\ bar baz'; echo "<$x><$y>"
|
||||||
2012-05-23 13:49:40 shbot geirha: <foo bar><baz>
|
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: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: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: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:51:47 geirha ormaaj: you mostly get the same effect as using \ at the prompt
|
||||||
2012-05-23 13:52:04 geirha echo \" outputs a " , read x <<< '\"' reads a "
|
2012-05-23 13:52:04 geirha echo \" outputs a " , read x <<< '\"' reads a "
|
||||||
2012-05-23 13:52:32 ormaaj oh weird
|
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: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:01 geirha ormaaj: ask Bourne :P
|
||||||
2012-05-23 13:53:20 geirha (not Jason)
|
2012-05-23 13:53:20 geirha (not Jason)
|
||||||
2012-05-23 13:53:56 ormaaj hm thanks anyway :)
|
2012-05-23 13:53:56 ormaaj hm thanks anyway :)
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Examples =====
|
===== Examples =====
|
||||||
==== Rudimentary cat replacement ====
|
==== Rudimentary cat replacement ====
|
||||||
|
|
||||||
A rudimentary replacement for the ''cat'' command: read lines of input from a file and print them on the terminal.
|
A rudimentary replacement for the ''cat'' command: read lines of input from a file and print them on the terminal.
|
||||||
<code>
|
<code>
|
||||||
opossum() {
|
opossum() {
|
||||||
while read -r; do
|
while read -r; do
|
||||||
printf "%s\n" "$REPLY"
|
printf "%s\n" "$REPLY"
|
||||||
done <"$1"
|
done <"$1"
|
||||||
}
|
}
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
__**Note:**__ 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'').
|
__**Note:**__ 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'').
|
||||||
|
|
||||||
@ -119,12 +119,12 @@ __**Note:**__ Here, ''read -r'' and the default ''REPLY'' is used, because we wa
|
|||||||
==== Press any key... ====
|
==== Press any key... ====
|
||||||
|
|
||||||
Remember the MSDOS ''pause'' command? Here's something similar:
|
Remember the MSDOS ''pause'' command? Here's something similar:
|
||||||
<code>
|
<code>
|
||||||
pause() {
|
pause() {
|
||||||
local dummy
|
local dummy
|
||||||
read -s -r -p "Press any key to continue..." -n 1 dummy
|
read -s -r -p "Press any key to continue..." -n 1 dummy
|
||||||
}
|
}
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
* ''-s'' to suppress terminal echo (printing)
|
* ''-s'' to suppress terminal echo (printing)
|
||||||
@ -135,61 +135,61 @@ Notes:
|
|||||||
=== Simple Split ===
|
=== Simple Split ===
|
||||||
|
|
||||||
Read can be used to split a string:
|
Read can be used to split a string:
|
||||||
<code>
|
<code>
|
||||||
var="one two three"
|
var="one two three"
|
||||||
read -r col1 col2 col3 <<< "$var"
|
read -r col1 col2 col3 <<< "$var"
|
||||||
printf "col1: %s col2: %s col3 %s\n" "$col1" "$col2" "$col3"
|
printf "col1: %s col2: %s col3 %s\n" "$col1" "$col2" "$col3"
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Take care that you cannot use a pipe:
|
Take care that you cannot use a pipe:
|
||||||
<code>
|
<code>
|
||||||
echo "$var" | read col1 col2 col3 # does not work!
|
echo "$var" | read col1 col2 col3 # does not work!
|
||||||
printf "col1: %s col2: %s col3 %s\n" "$col1" "$col2" "$col3"
|
printf "col1: %s col2: %s col3 %s\n" "$col1" "$col2" "$col3"
|
||||||
</code>
|
</code>
|
||||||
Why? because the commands of the pipe run in subshells that cannot modify the parent shell. As a result, the variables
|
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: [[scripting:processtree]]).
|
''col1'', ''col2'' and ''col3'' of the parent shell are not modified (see article: [[scripting:processtree]]).
|
||||||
|
|
||||||
If the variable has more fields than there are variables, the last variable get the remaining of the line:
|
If the variable has more fields than there are variables, the last variable get the remaining of the line:
|
||||||
<code>
|
<code>
|
||||||
read col1 col2 col3 <<< "one two three four"
|
read col1 col2 col3 <<< "one two three four"
|
||||||
printf "%s\n" "$col3" #prints three four
|
printf "%s\n" "$col3" #prints three four
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
|
|
||||||
=== Changing The Separator ===
|
=== Changing The Separator ===
|
||||||
|
|
||||||
By default reads separates the line in fields using spaces or tabs. You can modify this using the //special variable// [[syntax:shellvars#IFS | IFS]],
|
By default reads separates the line in fields using spaces or tabs. You can modify this using the //special variable// [[syntax:shellvars#IFS | IFS]],
|
||||||
the Internal Field Separator.
|
the Internal Field Separator.
|
||||||
<code>
|
<code>
|
||||||
IFS=":" read -r col1 col2 <<< "hello:world"
|
IFS=":" read -r col1 col2 <<< "hello:world"
|
||||||
printf "col1: %s col2: %s\n" "$col1" "$col2"
|
printf "col1: %s col2: %s\n" "$col1" "$col2"
|
||||||
</code>
|
</code>
|
||||||
Here we use the ''var=value command'' syntax to set the environment of ''read'' temporarily. We could have set ''IFS'' normally,
|
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'').
|
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
|
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:
|
fields are separated by **exactly** one character:
|
||||||
<code>
|
<code>
|
||||||
IFS=":" read -r col1 col2 col3 <<< "hello::world"
|
IFS=":" read -r col1 col2 col3 <<< "hello::world"
|
||||||
printf "col1: %s col2: %s col3 %s\n" "$col1" "$col2" "$col3"
|
printf "col1: %s col2: %s col3 %s\n" "$col1" "$col2" "$col3"
|
||||||
</code>
|
</code>
|
||||||
See how the ''<nowiki>::</nowiki>'' in the middle infact defines an additional //empty field//.
|
See how the ''<nowiki>::</nowiki>'' 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:
|
The fields are separated by exactly one character, but the character can be different between each field:
|
||||||
<code>
|
<code>
|
||||||
IFS=":|@" read -r col1 col2 col3 col4 <<< "hello:world|in@bash"
|
IFS=":|@" read -r col1 col2 col3 col4 <<< "hello:world|in@bash"
|
||||||
printf "col1: %s col2: %s col3 %s col4 %s\n" "$col1" "$col2" "$col3" "$col4"
|
printf "col1: %s col2: %s col3 %s col4 %s\n" "$col1" "$col2" "$col3" "$col4"
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
|
|
||||||
==== Are you sure? ====
|
==== Are you sure? ====
|
||||||
<code>
|
<code>
|
||||||
asksure() {
|
asksure() {
|
||||||
echo -n "Are you sure (Y/N)? "
|
echo -n "Are you sure (Y/N)? "
|
||||||
while read -r -n 1 -s answer; do
|
while read -r -n 1 -s answer; do
|
||||||
if [[ $answer = [YyNn] ]]; then
|
if [[ $answer = [YyNn] ]]; then
|
||||||
[[ $answer = [Yy] ]] && retval=0
|
[[ $answer = [Yy] ]] && retval=0
|
||||||
[[ $answer = [Nn] ]] && retval=1
|
[[ $answer = [Nn] ]] && retval=1
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
@ -205,15 +205,15 @@ if asksure; then
|
|||||||
else
|
else
|
||||||
echo "Pfff..."
|
echo "Pfff..."
|
||||||
fi
|
fi
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== Ask for a path with a default value ====
|
==== Ask for a path with a default value ====
|
||||||
|
|
||||||
__**Note:**__ The ''-i'' option was introduced with Bash 4
|
__**Note:**__ The ''-i'' option was introduced with Bash 4
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
read -e -p "Enter the path to the file: " -i "/usr/local/etc/" FILEPATH
|
read -e -p "Enter the path to the file: " -i "/usr/local/etc/" FILEPATH
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
The user will be prompted, he can just accept the default, or edit it.
|
The user will be prompted, he can just accept the default, or edit it.
|
||||||
|
|
||||||
@ -221,19 +221,19 @@ The user will be prompted, he can just accept the default, or edit it.
|
|||||||
|
|
||||||
Here, ''IFS'' contains both, a colon and a space. The fields of the date/time string are recognized correctly.
|
Here, ''IFS'' contains both, a colon and a space. The fields of the date/time string are recognized correctly.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
datetime="2008:07:04 00:34:45"
|
datetime="2008:07:04 00:34:45"
|
||||||
IFS=": " read -r year month day hour minute second <<< "$datetime"
|
IFS=": " read -r year month day hour minute second <<< "$datetime"
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Portability considerations =====
|
===== 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) 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
|
* 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''. <code>
|
* ''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''. <code>
|
||||||
while IFS= read -r line; do
|
while IFS= read -r line; do
|
||||||
...
|
...
|
||||||
done < text.txt
|
done < text.txt
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
===== Synopsis =====
|
===== Synopsis =====
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
readonly [-p] [-a] [-A] [-f] [NAME[=VALUE] ...]
|
readonly [-p] [-a] [-A] [-f] [NAME[=VALUE] ...]
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Description =====
|
===== Description =====
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
====== The return builtin command ======
|
====== The return builtin command ======
|
||||||
|
|
||||||
===== Synopsis =====
|
===== Synopsis =====
|
||||||
<code>return [N]</code>
|
<code>return [N]</code>
|
||||||
|
|
||||||
===== Description =====
|
===== Description =====
|
||||||
The ''return'' command returns from a shell function.
|
The ''return'' command returns from a shell function.
|
||||||
|
@ -4,14 +4,14 @@ FIXME incomplete - text, examples, maybe extended description
|
|||||||
|
|
||||||
===== Synopsis =====
|
===== Synopsis =====
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
set [--abefhkmnptuvxBCHP] <-o OPTIONNAME> [-][--] <POSPARAMS>
|
set [--abefhkmnptuvxBCHP] <-o OPTIONNAME> [-][--] <POSPARAMS>
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Description =====
|
===== Description =====
|
||||||
|
|
||||||
''set'' is primarily made to
|
''set'' is primarily made to
|
||||||
* set the positional parameters (see [[scripting:posparams | handling positional parameters]]) to ''<POSPARAMS>''
|
* set the positional parameters (see [[scripting:posparams | handling positional parameters]]) to ''<POSPARAMS>''
|
||||||
* set shell attributes with short options (see below)
|
* set shell attributes with short options (see below)
|
||||||
* set shell attributes with long option names (see below)
|
* set shell attributes with long option names (see below)
|
||||||
|
|
||||||
@ -24,7 +24,7 @@ All attributes below can be switched on using ''-X'' and switched off using ''+X
|
|||||||
^Flag^Optionname^Description^
|
^Flag^Optionname^Description^
|
||||||
|''-a''|''allexport''|Automatically mark new and altered variables to be exported to subsequent environments.|
|
|''-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)|
|
|''-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'', ''<nowiki>||</nowiki>'', ''<nowiki>&&</nowiki>'')|
|
|''-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'', ''<nowiki>||</nowiki>'', ''<nowiki>&&</nowiki>'')|
|
||||||
|''-f''|''noglob''|Disable [[syntax:expansion:globs | pathname expansion]] (globbing)|
|
|''-f''|''noglob''|Disable [[syntax:expansion:globs | pathname expansion]] (globbing)|
|
||||||
|''-h''|''hashall''|Remembers the location of commands when they're called (hashing). Enabled by default.|
|
|''-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.|
|
|''-k''|''keyword''|Allows to place environment-assignments everywhere in the commandline, not only infront of the called command.|
|
||||||
@ -37,13 +37,13 @@ All attributes below can be switched on using ''-X'' and switched off using ''+X
|
|||||||
|''-v''|''verbose''|Print shell input lines as they are read - useful for debugging.|
|
|''-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.|
|
|''-x''|''xtrace''|Print commands just before execution - with all expansions and substitutions done, and words marked - useful for debugging.|
|
||||||
|''-B''|''braceexpand''|The shell performs [[syntax:expansion:brace | brace expansion]] This is on by default.|
|
|''-B''|''braceexpand''|The shell performs [[syntax:expansion:brace | brace expansion]] 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 [[syntax:redirection | redirection]]|
|
|''-C''|<BOOKMARK:tag_noclobber>''noclobber''|Don't overwrite files on redirection operations. You can override that by specifying the ''>|'' redirection operator when needed. See [[syntax:redirection | redirection]]|
|
||||||
|''-E''|''errtrace''|''ERR''-traps are inherited by by shell functions, command substitutions, and commands executed in a subshell environment.|
|
|''-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.|
|
|''-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.|
|
|''-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.|
|
|''-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 ''<nowiki>--</nowiki>''!) when no further arguments are given.|
|
|''-''| |"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 ''<nowiki>--</nowiki>''!) when no further arguments are given.|
|
||||||
|''<nowiki>--</nowiki>''| |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.|
|
|''<nowiki>--</nowiki>''| |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 ^^
|
^^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.|
|
| |''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)|
|
| |''history''|If set, command historization is done (enabled by default on interactive shells)|
|
||||||
@ -56,14 +56,14 @@ All attributes below can be switched on using ''-X'' and switched off using ''+X
|
|||||||
===== Examples =====
|
===== Examples =====
|
||||||
|
|
||||||
Tag a part of a shell script to output debugging information (''-x''):
|
Tag a part of a shell script to output debugging information (''-x''):
|
||||||
<code bash>
|
<code bash>
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
...
|
...
|
||||||
set -x # on
|
set -x # on
|
||||||
...
|
...
|
||||||
set +x # off
|
set +x # off
|
||||||
...
|
...
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Portability considerations =====
|
===== Portability considerations =====
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
====== The shift builtin command ======
|
====== The shift builtin command ======
|
||||||
|
|
||||||
===== Synopsis =====
|
===== Synopsis =====
|
||||||
<code>shift [n]</code>
|
<code>shift [n]</code>
|
||||||
|
|
||||||
===== Description =====
|
===== Description =====
|
||||||
|
|
||||||
@ -46,20 +46,20 @@ There are no options.
|
|||||||
===== Portability considerations =====
|
===== Portability considerations =====
|
||||||
|
|
||||||
* The ''shift'' builtin command is specified by POSIX(r).
|
* 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 [[internals/shell_options#shift_verbose | shift_verbose]] [[commands/builtin/shopt | shopt]] option is enabled. Ksh93, pdksh, posh, mksh, and dash, all throw useless fatal shell errors.<code>
|
* 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 [[internals/shell_options#shift_verbose | shift_verbose]] [[commands/builtin/shopt | shopt]] option is enabled. Ksh93, pdksh, posh, mksh, and dash, all throw useless fatal shell errors.<code>
|
||||||
$ dash -c 'f() { if shift; then echo "$1"; else echo "no args"; fi; }; f'
|
$ dash -c 'f() { if shift; then echo "$1"; else echo "no args"; fi; }; f'
|
||||||
dash: 1: shift: can't shift that many
|
dash: 1: shift: can't shift that many
|
||||||
</code> In most shells, you can work around this problem using the [[commands:builtin:command | 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'
|
</code> In most shells, you can work around this problem using the [[commands:builtin:command | 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
|
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> 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'
|
<code> $ mksh -c 'f() { if ! ${1+false} && shift; then echo "$1"; else echo "no args"; fi; }; f'
|
||||||
no args
|
no args
|
||||||
</code>
|
</code>
|
||||||
<del>The mksh maintainer refuses to change either the ''shift'' or ''command'' builtins.</del> [[https://github.com/MirBSD/mksh/commit/996e05548ab82f7ef2dea61f109cc7b6d13837fa | Fixed]]. (Thanks!)
|
<del>The mksh maintainer refuses to change either the ''shift'' or ''command'' builtins.</del> [[https://github.com/MirBSD/mksh/commit/996e05548ab82f7ef2dea61f109cc7b6d13837fa | Fixed]]. (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'
|
* 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)
|
(no output)
|
||||||
</code> The above mksh workaround will work in this case too.
|
</code> The above mksh workaround will work in this case too.
|
||||||
|
|
||||||
===== See also =====
|
===== See also =====
|
||||||
|
|
||||||
|
@ -4,9 +4,9 @@ The ''shopt'' builtin manages [[internals:shell_options | shell options]], a set
|
|||||||
|
|
||||||
===== Synopsis =====
|
===== Synopsis =====
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
shopt [-pqsu] [-o] <OPTNAME...>
|
shopt [-pqsu] [-o] <OPTNAME...>
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Description =====
|
===== Description =====
|
||||||
|
|
||||||
@ -15,11 +15,11 @@ Note: Some of these options and other shell options can also be set with [[comma
|
|||||||
==== Options ====
|
==== Options ====
|
||||||
|
|
||||||
^Option^Description^
|
^Option^Description^
|
||||||
|''-o''|Restrict the values of ''<OPTNAME...>'' to only those also known by [[commands:builtin:set | the set builtin]]|
|
|''-o''|Restrict the values of ''<OPTNAME...>'' to only those also known by [[commands:builtin:set | the set builtin]]|
|
||||||
|''-p''|Print all shell options and their current value. **Default**.|
|
|''-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|
|
|''-q''|Quiet mode. Set exit code if named option is set. For multiple options: ''TRUE'' if all options are set, ''FALSE'' otherwise|
|
||||||
|''-s''|Enable (__s__et) the shell options named by ''<OPTNAME...>'' or list all //enabled// options if no names are given|
|
|''-s''|Enable (__s__et) the shell options named by ''<OPTNAME...>'' or list all //enabled// options if no names are given|
|
||||||
|''-u''|Disabe (__u__nset) the shell options named by ''<OPTNAME...>'' or list all //disabled// options if no names are given|
|
|''-u''|Disabe (__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.
|
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.
|
||||||
|
|
||||||
@ -32,9 +32,9 @@ When setting/unsetting an option, the exit code is ''TRUE'' unless the named opt
|
|||||||
===== Examples ======
|
===== Examples ======
|
||||||
|
|
||||||
Enable the ''nullglob'' option:
|
Enable the ''nullglob'' option:
|
||||||
<code>
|
<code>
|
||||||
shopt -s nullglob
|
shopt -s nullglob
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Portability considerations ======
|
===== Portability considerations ======
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<label class="block control-label" for="focus__this"><span>Username</span> <input type="text" id="focus__this" name="u" class="edit form-control"/></label><br/>
|
<label class="block control-label" for="focus__this"><span>Username</span> <input type="text" id="focus__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="block control-label"><span>Password</span> <input type="password" name="p" class="edit form-control"/></label><br/>
|
||||||
<label class="simple control-label" for="remember__me"><input type="checkbox" id="remember__me" name="r" value="1" class="checkbox-inline"/> <span>Remember me</span></label>
|
<label class="simple control-label" for="remember__me"><input type="checkbox" id="remember__me" name="r" value="1" class="checkbox-inline"/> <span>Remember me</span></label>
|
||||||
<div id="plugin__captcha_wrapper"><input type="hidden" name="80d13662bc018ff4d7809a8d517a0cfb" value="42FOZdS2ttGgeyJAq6xhPpjk5crSblSEQXmyW2Yg3W8="/><label for="plugin__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>
|
<div id="plugin__captcha_wrapper"><input type="hidden" name="80d13662bc018ff4d7809a8d517a0cfb" value="42FOZdS2ttGgeyJAq6xhPpjk5crSblSEQXmyW2Yg3W8="/><label for="plugin__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>
|
</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>
|
<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>
|
||||||
@ -89,7 +89,7 @@
|
|||||||
<span class="visible-lg-block"></span>
|
<span class="visible-lg-block"></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<img src="/web/20221209023650im_/https://wiki.bash-hackers.org/lib/exe/indexer.php?id=commands%3Abuiltin%3Asource&1670553440" width="2" height="1" alt=""/>
|
<img src="/web/20221209023650im_/https://wiki.bash-hackers.org/lib/exe/indexer.php?id=commands%3Abuiltin%3Asource&1670553440" width="2" height="1" alt=""/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<label class="block control-label" for="focus__this"><span>Username</span> <input type="text" id="focus__this" name="u" class="edit form-control"/></label><br/>
|
<label class="block control-label" for="focus__this"><span>Username</span> <input type="text" id="focus__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="block control-label"><span>Password</span> <input type="password" name="p" class="edit form-control"/></label><br/>
|
||||||
<label class="simple control-label" for="remember__me"><input type="checkbox" id="remember__me" name="r" value="1" class="checkbox-inline"/> <span>Remember me</span></label>
|
<label class="simple control-label" for="remember__me"><input type="checkbox" id="remember__me" name="r" value="1" class="checkbox-inline"/> <span>Remember me</span></label>
|
||||||
<div id="plugin__captcha_wrapper"><input type="hidden" name="cf98d82c03000fa22bcf7e63f85de09a" value="E7+c/5cYp8oDlzqwHkwFoVVx8H0vjRftuE9fGEqBYWM="/><label for="plugin__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>
|
<div id="plugin__captcha_wrapper"><input type="hidden" name="cf98d82c03000fa22bcf7e63f85de09a" value="E7+c/5cYp8oDlzqwHkwFoVVx8H0vjRftuE9fGEqBYWM="/><label for="plugin__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>
|
</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>
|
<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>
|
||||||
@ -89,7 +89,7 @@
|
|||||||
<span class="visible-lg-block"></span>
|
<span class="visible-lg-block"></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<img src="/web/20221209024037im_/https://wiki.bash-hackers.org/lib/exe/indexer.php?id=commands%3Abuiltin%3Atimes&1670553667" width="2" height="1" alt=""/>
|
<img src="/web/20221209024037im_/https://wiki.bash-hackers.org/lib/exe/indexer.php?id=commands%3Abuiltin%3Atimes&1670553667" width="2" height="1" alt=""/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
====== The trap builtin command ======
|
====== The trap builtin command ======
|
||||||
|
|
||||||
===== Synopsis =====
|
===== Synopsis =====
|
||||||
<code>trap [-lp] [[ARGUMENT] SIGNAL]</code>
|
<code>trap [-lp] [[ARGUMENT] SIGNAL]</code>
|
||||||
|
|
||||||
===== Description =====
|
===== Description =====
|
||||||
The ''trap'' command is used to "trap" signals and other events. In this context, "trapping" means to install handler code.
|
The ''trap'' command is used to "trap" signals and other events. In this context, "trapping" means to install handler code.
|
||||||
@ -42,15 +42,15 @@ Special events
|
|||||||
|
|
||||||
==== List installed traps ====
|
==== List installed traps ====
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
trap
|
trap
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== Ignore terminal interrupt (Ctrl-C, SIGINT) ====
|
==== Ignore terminal interrupt (Ctrl-C, SIGINT) ====
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
trap '' INT
|
trap '' INT
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Portability considerations =====
|
===== Portability considerations =====
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<label class="block control-label" for="focus__this"><span>Username</span> <input type="text" id="focus__this" name="u" class="edit form-control"/></label><br/>
|
<label class="block control-label" for="focus__this"><span>Username</span> <input type="text" id="focus__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="block control-label"><span>Password</span> <input type="password" name="p" class="edit form-control"/></label><br/>
|
||||||
<label class="simple control-label" for="remember__me"><input type="checkbox" id="remember__me" name="r" value="1" class="checkbox-inline"/> <span>Remember me</span></label>
|
<label class="simple control-label" for="remember__me"><input type="checkbox" id="remember__me" name="r" value="1" class="checkbox-inline"/> <span>Remember me</span></label>
|
||||||
<div id="plugin__captcha_wrapper"><input type="hidden" name="b838780b201e25c64701dd6d96c9c690" value="vWqvEC1F2pR6Zk6YCMXV6ZcWI9LCKDF1yaEDdcP/PjA="/><label for="plugin__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>
|
<div id="plugin__captcha_wrapper"><input type="hidden" name="b838780b201e25c64701dd6d96c9c690" value="vWqvEC1F2pR6Zk6YCMXV6ZcWI9LCKDF1yaEDdcP/PjA="/><label for="plugin__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>
|
</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>
|
<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>
|
||||||
@ -89,7 +89,7 @@
|
|||||||
<span class="visible-lg-block"></span>
|
<span class="visible-lg-block"></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<img src="/web/20220811050800im_/https://wiki.bash-hackers.org/lib/exe/indexer.php?id=commands%3Abuiltin%3Atrue&1660194486" width="2" height="1" alt=""/>
|
<img src="/web/20220811050800im_/https://wiki.bash-hackers.org/lib/exe/indexer.php?id=commands%3Abuiltin%3Atrue&1660194486" width="2" height="1" alt=""/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
===== Synopsis =====
|
===== Synopsis =====
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
unset [-f|v] [-n] [NAME ...]
|
unset [-f|v] [-n] [NAME ...]
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Description =====
|
===== Description =====
|
||||||
|
|
||||||
@ -28,13 +28,13 @@ Without any option, ''unset'' tries to unset a variable first, then a function.
|
|||||||
|
|
||||||
===== Examples =====
|
===== Examples =====
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
unset -v EDITOR
|
unset -v EDITOR
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
unset -f myfunc1 myfunc2
|
unset -f myfunc1 myfunc2
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== Scope ====
|
==== Scope ====
|
||||||
|
|
||||||
@ -43,7 +43,7 @@ In bash, unset has some interesting properties due to its unique dynamic scope.
|
|||||||
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.
|
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.
|
Here's a demonstration of this behavior.
|
||||||
<code>
|
<code>
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
FUNCNEST=10
|
FUNCNEST=10
|
||||||
@ -53,7 +53,7 @@ FUNCNEST=10
|
|||||||
callDepth() {
|
callDepth() {
|
||||||
# Strip "main" off the end of FUNCNAME[@] if current function is named "main" and
|
# Strip "main" off the end of FUNCNAME[@] if current function is named "main" and
|
||||||
# Bash added an extra "main" for non-interactive scripts.
|
# Bash added an extra "main" for non-interactive scripts.
|
||||||
if [[ main == !(!("${FUNCNAME[1]}")|!("${FUNCNAME[-1]}")) && $- != *i* ]]; then
|
if [[ main == !(!("${FUNCNAME[1]}")|!("${FUNCNAME[-1]}")) && $- != *i* ]]; then
|
||||||
local -a 'fnames=("${FUNCNAME[@]:1:${#FUNCNAME[@]}-2}")'
|
local -a 'fnames=("${FUNCNAME[@]:1:${#FUNCNAME[@]}-2}")'
|
||||||
else
|
else
|
||||||
local -a 'fnames=("${FUNCNAME[@]:1}")'
|
local -a 'fnames=("${FUNCNAME[@]:1}")'
|
||||||
@ -79,9 +79,9 @@ unset2() {
|
|||||||
|
|
||||||
f() {
|
f() {
|
||||||
local a
|
local a
|
||||||
if (( (a=$(callDepth)) <= 4 )); then
|
if (( (a=$(callDepth)) <= 4 )); then
|
||||||
(( a == 1 )) && unset a
|
(( a == 1 )) && unset a
|
||||||
(( a == 2 )) && declare -g a='global scope yo'
|
(( a == 2 )) && declare -g a='global scope yo'
|
||||||
f
|
f
|
||||||
else
|
else
|
||||||
trap 'declare -p a' DEBUG
|
trap 'declare -p a' DEBUG
|
||||||
@ -97,15 +97,15 @@ a='global scope'
|
|||||||
f
|
f
|
||||||
|
|
||||||
# vim: set fenc=utf-8 ff=unix ts=4 sts=4 sw=4 ft=sh nowrap et:
|
# vim: set fenc=utf-8 ff=unix ts=4 sts=4 sw=4 ft=sh nowrap et:
|
||||||
</code>
|
</code>
|
||||||
output:
|
output:
|
||||||
<code>
|
<code>
|
||||||
declare -- a="5"
|
declare -- a="5"
|
||||||
declare -- a="4"
|
declare -- a="4"
|
||||||
declare -- a="2"
|
declare -- a="2"
|
||||||
./unset-tests: line 44: declare: a: not found
|
./unset-tests: line 44: declare: a: not found
|
||||||
declare -- a="global scope yo"
|
declare -- a="global scope yo"
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Some things to observe:
|
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.
|
* ''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.
|
||||||
@ -115,32 +115,32 @@ Some things to observe:
|
|||||||
|
|
||||||
==== Args ====
|
==== Args ====
|
||||||
Like several other Bash builtins that take parameter names, unset expands its arguments.
|
Like several other Bash builtins that take parameter names, unset expands its arguments.
|
||||||
<code>
|
<code>
|
||||||
~ $ ( a=({a..d}); unset 'a[2]'; declare -p a )
|
~ $ ( a=({a..d}); unset 'a[2]'; declare -p a )
|
||||||
declare -a a='([0]="a" [1]="b" [3]="d")'
|
declare -a a='([0]="a" [1]="b" [3]="d")'
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
As usual in such cases, it's important to quote the args to avoid accidental results such as globbing.
|
As usual in such cases, it's important to quote the args to avoid accidental results such as globbing.
|
||||||
<code>
|
<code>
|
||||||
~ $ ( a=({a..d}) b=a c=d d=1; set -x; unset "${b}["{2..3}-c\]; declare -p a )
|
~ $ ( a=({a..d}) b=a c=d d=1; set -x; unset "${b}["{2..3}-c\]; declare -p a )
|
||||||
+ unset 'a[2-1]' 'a[3-1]'
|
+ unset 'a[2-1]' 'a[3-1]'
|
||||||
+ declare -p a
|
+ declare -p a
|
||||||
declare -a a='([0]="a" [3]="d")'
|
declare -a a='([0]="a" [3]="d")'
|
||||||
</code>
|
</code>
|
||||||
Of course hard to follow indirection is still possible whenever arithmetic is involved, also as shown above, even without extra expansions.
|
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.
|
In Bash, the ''unset'' builtin only evaluates array subscripts if the array itself is set.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
~ $ ( unset -v 'a[$(echo a was set >&2)0]' )
|
~ $ ( unset -v 'a[$(echo a was set >&2)0]' )
|
||||||
~ $ ( a=(); unset -v 'a[$(echo a was set >&2)0]' )
|
~ $ ( a=(); unset -v 'a[$(echo a was set >&2)0]' )
|
||||||
a was set
|
a was set
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Portability considerations =====
|
===== Portability considerations =====
|
||||||
|
|
||||||
Quoting POSIX:
|
Quoting POSIX:
|
||||||
<code>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.</code>
|
<code>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.</code>
|
||||||
Therefore, it is recommended to explicitly specify ''-f'' or ''-v'' when using ''unset''. Also, I prefer it as a matter of style.
|
Therefore, it is recommended to explicitly specify ''-f'' or ''-v'' when using ''unset''. Also, I prefer it as a matter of style.
|
||||||
|
|
||||||
===== See also =====
|
===== See also =====
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
====== The wait builtin command ======
|
====== The wait builtin command ======
|
||||||
|
|
||||||
===== Synopsis =====
|
===== Synopsis =====
|
||||||
<code>wait [-f] [-n] [-p VARNAME] [ID...]</code>
|
<code>wait [-f] [-n] [-p VARNAME] [ID...]</code>
|
||||||
|
|
||||||
===== Description =====
|
===== Description =====
|
||||||
The ''wait'' builtin command is used to wait for job completion and return exit status.
|
The ''wait'' builtin command is used to wait for job completion and return exit status.
|
||||||
|
@ -1,64 +1,64 @@
|
|||||||
====== The classic test command ======
|
====== The classic test command ======
|
||||||
|
|
||||||
''test <EXPRESSION>''
|
''test <EXPRESSION>''
|
||||||
|
|
||||||
''[ <EXPRESSION> ]''
|
''[ <EXPRESSION> ]''
|
||||||
|
|
||||||
===== General syntax =====
|
===== 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:
|
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:
|
||||||
<code>
|
<code>
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# test if /etc/passwd exists
|
# test if /etc/passwd exists
|
||||||
|
|
||||||
if test -e /etc/passwd; then
|
if test -e /etc/passwd; then
|
||||||
echo "Alright man..." >&2
|
echo "Alright man..." >&2
|
||||||
else
|
else
|
||||||
echo "Yuck! Where is it??" >&2
|
echo "Yuck! Where is it??" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
|
|
||||||
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''").
|
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> ]**''"
|
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:
|
Let's rewrite the above example to use it:
|
||||||
<code>
|
<code>
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# test if /etc/passwd exists
|
# test if /etc/passwd exists
|
||||||
|
|
||||||
if [ -e /etc/passwd ]; then
|
if [ -e /etc/passwd ]; then
|
||||||
echo "Alright man..." >&2
|
echo "Alright man..." >&2
|
||||||
else
|
else
|
||||||
echo "Yuck! Where is it??" >&2
|
echo "Yuck! Where is it??" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
</code>
|
</code>
|
||||||
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!**
|
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:
|
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:
|
||||||
<code>
|
<code>
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
mymusic="/data/music/Van Halen/Van Halen - Right Now.mp3"
|
mymusic="/data/music/Van Halen/Van Halen - Right Now.mp3"
|
||||||
|
|
||||||
if [ -e "$mymusic" ]; then
|
if [ -e "$mymusic" ]; then
|
||||||
echo "Let's rock" >&2
|
echo "Let's rock" >&2
|
||||||
else
|
else
|
||||||
echo "No music today, sorry..." >&2
|
echo "No music today, sorry..." >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
</code>
|
</code>
|
||||||
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 [[syntax:words | article about words...]]
|
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 [[syntax:words | article about 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!**
|
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!**
|
||||||
|
|
||||||
__**Another common mistake**__ is to provide too **few** arguments:
|
__**Another common mistake**__ is to provide too **few** arguments:
|
||||||
<code>
|
<code>
|
||||||
[ "$mystring"!="test" ]
|
[ "$mystring"!="test" ]
|
||||||
</code>
|
</code>
|
||||||
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''!
|
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**.
|
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**.
|
||||||
@ -68,56 +68,56 @@ Well, I addressed several basic rules, now let's see what the test-command can d
|
|||||||
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.
|
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 | |
|
^ Operator syntax ^ Description | |
|
||||||
| **-a** <FILE> | True if <FILE> exists. :!: (not recommended, may collide with ''-a'' for ''AND'', see below) | |
|
| **-a** <FILE> | True if <FILE> exists. :!: (not recommended, may collide with ''-a'' for ''AND'', see below) | |
|
||||||
| **-e** <FILE> | True if <FILE> exists. | |
|
| **-e** <FILE> | True if <FILE> exists. | |
|
||||||
| **-f** <FILE> | True, if <FILE> exists and is a **regular** file. | |
|
| **-f** <FILE> | True, if <FILE> exists and is a **regular** file. | |
|
||||||
| **-d** <FILE> | True, if <FILE> exists and is a **directory**. ||
|
| **-d** <FILE> | True, if <FILE> exists and is a **directory**. ||
|
||||||
| **-c** <FILE> | True, if <FILE> exists and is a **character special** file. | |
|
| **-c** <FILE> | True, if <FILE> exists and is a **character special** file. | |
|
||||||
| **-b** <FILE> | True, if <FILE> exists and is a **block 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). | |
|
| **-p** <FILE> | True, if <FILE> exists and is a **named pipe** (FIFO). | |
|
||||||
| **-S** <FILE> | True, if <FILE> exists and is a **socket** file. | |
|
| **-S** <FILE> | True, if <FILE> exists and is a **socket** file. | |
|
||||||
| **-L** <FILE> | True, if <FILE> exists and is a **symbolic link**. | |
|
| **-L** <FILE> | True, if <FILE> exists and is a **symbolic link**. | |
|
||||||
| **-h** <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. | |
|
| **-g** <FILE> | True, if <FILE> exists and has **sgid bit** set. | |
|
||||||
| **-u** <FILE> | True, if <FILE> exists and has **suid bit** set. | |
|
| **-u** <FILE> | True, if <FILE> exists and has **suid bit** set. | |
|
||||||
| **-r** <FILE> | True, if <FILE> exists and is **readable**. | |
|
| **-r** <FILE> | True, if <FILE> exists and is **readable**. | |
|
||||||
| **-w** <FILE> | True, if <FILE> exists and is **writable**. | |
|
| **-w** <FILE> | True, if <FILE> exists and is **writable**. | |
|
||||||
| **-x** <FILE> | True, if <FILE> exists and is **executable**. | |
|
| **-x** <FILE> | True, if <FILE> exists and is **executable**. | |
|
||||||
| **-s** <FILE> | True, if <FILE> exists and has size bigger than 0 (**not empty**). | |
|
| **-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. | |
|
| **-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> **-nt** <FILE2> | True, if <FILE1> is **newer than** <FILE2> (mtime). :!: | |
|
||||||
| <FILE1> **-ot** <FILE2> | True, if <FILE1> is **older 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**. :!: | |
|
| <FILE1> **-ef** <FILE2> | True, if <FILE1> and <FILE2> refer to the **same device and inode numbers**. :!: | |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
===== String tests =====
|
===== String tests =====
|
||||||
^Operator syntax^Description^
|
^Operator syntax^Description^
|
||||||
|**-z** <STRING>|True, if <STRING> is **empty**.|
|
|**-z** <STRING>|True, if <STRING> is **empty**.|
|
||||||
|**-n** <STRING>|True, if <STRING> is **not empty** (this is the default operation).|
|
|**-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 **equal**.|
|
||||||
|<STRING1> **!=** <STRING2>|True, if the strings are **not 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 **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 ''\>''|
|
|<STRING1> **>** <STRING2>|True if <STRING1> sorts **after** <STRING2> lexicographically (pure ASCII, not current locale!). Remember to escape! Use ''\>''|
|
||||||
|
|
||||||
===== Arithmetic tests =====
|
===== Arithmetic tests =====
|
||||||
^Operator syntax^Description^
|
^Operator syntax^Description^
|
||||||
|<INTEGER1> **-eq** <INTEGER2>|True, if the integers are **equal**.|
|
|<INTEGER1> **-eq** <INTEGER2>|True, if the integers are **equal**.|
|
||||||
|<INTEGER1> **-ne** <INTEGER2>|True, if the integers are **NOT 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> **-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> **-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> **-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.|
|
|<INTEGER1> **-gt** <INTEGER2>|True, if the first integer is **greater than** second one.|
|
||||||
|
|
||||||
===== Misc syntax =====
|
===== Misc syntax =====
|
||||||
^ Operator syntax ^ Description ^
|
^ 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> **-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). |
|
| <TEST1> **-o** <TEST2> | True, if either <TEST1> **or** <TEST2> is true (OR). |
|
||||||
| **!** <TEST> | True, if <TEST> is **false** (NOT). |
|
| **!** <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 "\)"! |
|
| **(** <TEST> **)** | Group a test (for precedence). **Attention:** In normal shell-usage, the "(" and ")" must be escaped; use "\(" and "\)"! |
|
||||||
| **-o** <OPTION_NAME> | True, if the [[internals:shell_options| shell option]] <OPTION_NAME> is set. |
|
| **-o** <OPTION_NAME> | True, if the [[internals:shell_options| shell option]] <OPTION_NAME> is set. |
|
||||||
| **-v** <VARIABLENAME> | True if the variable <VARIABLENAME> has been set. Use ''var[n]'' for array elements. |
|
| **-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) |
|
| **-R** <VARIABLENAME> | True if the variable <VARIABLENAME> has been set and is a nameref variable (since 4.3-alpha) |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -147,10 +147,10 @@ Here are the rules taken from the manual (__**Note:**__ This is for the command
|
|||||||
|
|
||||||
|
|
||||||
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:
|
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:
|
||||||
<code>
|
<code>
|
||||||
var=""
|
var=""
|
||||||
if [ -n $var ]; then echo "var is not empty"; fi
|
if [ -n $var ]; then echo "var is not empty"; fi
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
This code prints "var is not empty", even though ''-n something'' is supposed to be true if ''$var'' is not empty - **why?**
|
This code prints "var is not empty", even though ''-n something'' is supposed to be true if ''$var'' is not empty - **why?**
|
||||||
|
|
||||||
@ -165,17 +165,17 @@ These rules also explain why, for instance, -a and -o can have several meanings.
|
|||||||
|
|
||||||
==== The Prefered Way ====
|
==== 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**.
|
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:
|
See this:
|
||||||
<code>
|
<code>
|
||||||
if [ -n "$var"] && [ -e "$var"]; then
|
if [ -n "$var"] && [ -e "$var"]; then
|
||||||
echo "\$var is not null and a file named $var exists!"
|
echo "\$var is not null and a file named $var exists!"
|
||||||
fi
|
fi
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
The return status of AND and OR lists is the exit status of the last command executed in the list
|
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 an exit status of zero (true)
|
||||||
* With ''command1 ││ command2'', ''command2'' is executed if, and only if, ''command1'' returns a non-zero exit status (false)
|
* With ''command1 ││ command2'', ''command2'' is executed if, and only if, ''command1'' returns a non-zero exit status (false)
|
||||||
|
|
||||||
|
|
||||||
@ -183,19 +183,19 @@ The return status of AND and OR lists is the exit status of the last command exe
|
|||||||
==== The other way: -a and -o ====
|
==== The other way: -a and -o ====
|
||||||
|
|
||||||
The logical operators AND and OR for the test-command itself are ''-a'' and ''-o'', thus:
|
The logical operators AND and OR for the test-command itself are ''-a'' and ''-o'', thus:
|
||||||
<code>
|
<code>
|
||||||
if [ -n "$var" -a -e "$var" ] ; then
|
if [ -n "$var" -a -e "$var" ] ; then
|
||||||
echo "\$var is not null and a file named $var exists"
|
echo "\$var is not null and a file named $var exists"
|
||||||
fi
|
fi
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
They are **not** ''&&'' or ''||'':
|
They are **not** ''&&'' or ''||'':
|
||||||
<code>
|
<code>
|
||||||
$ if [ -n "/tmp" && -d "/tmp"]; then echo true; fi # DOES NOT WORK
|
$ if [ -n "/tmp" && -d "/tmp"]; then echo true; fi # DOES NOT WORK
|
||||||
bash: [: missing `]'
|
bash: [: missing `]'
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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**:
|
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"''
|
* ''[ -n "/tmp"''
|
||||||
* ''-d "/tmp" ]''
|
* ''-d "/tmp" ]''
|
||||||
...which **must** fail.
|
...which **must** fail.
|
||||||
@ -207,7 +207,7 @@ You might find the error message confusing, ''['' does not find the required fin
|
|||||||
|
|
||||||
=== If portability is a concern ===
|
=== 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! ((<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>))
|
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! ((<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>))
|
||||||
|
|
||||||
=== If you want the cut behaviour ===
|
=== If you want the cut behaviour ===
|
||||||
|
|
||||||
@ -215,49 +215,49 @@ Let's say, we want to check the following two things (AND):
|
|||||||
- if a string is null (empty)
|
- if a string is null (empty)
|
||||||
- if a command produced an output
|
- if a command produced an output
|
||||||
Let's see:
|
Let's see:
|
||||||
<code>
|
<code>
|
||||||
if [ -z "false" -a -z "$(echo I am executed >&2)" ] ; then ...
|
if [ -z "false" -a -z "$(echo I am executed >&2)" ] ; then ...
|
||||||
</code>
|
</code>
|
||||||
=> The arguments are all expanded **before** ''test'' runs, thus the echo-command **is executed**.
|
=> The arguments are all expanded **before** ''test'' runs, thus the echo-command **is executed**.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
if [ -z "false" ] && [ -z "$(echo I am not executed >&2)" ]; then...
|
if [ -z "false" ] && [ -z "$(echo I am not executed >&2)" ]; then...
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
=> 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**.
|
=> 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**.
|
||||||
|
|
||||||
__**Note:**__ In my opinion, ''-a'' and ''-o'' are also less readable ''[pgas]''
|
__**Note:**__ In my opinion, ''-a'' and ''-o'' are also less readable ''[pgas]''
|
||||||
|
|
||||||
==== Precedence and Parenthesis ====
|
==== Precedence and Parenthesis ====
|
||||||
|
|
||||||
Take care if you convert your scripts from using ''-a'' and ''-o'' to use the list way (''&&'' and ''||''):
|
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 test-command rules, ''-a'' has **precedence over** ''-o''
|
||||||
* in the shell grammar rules, ''&&'' and ''||'' have **equal precedence**
|
* in the shell grammar rules, ''&&'' and ''||'' have **equal precedence**
|
||||||
That means, **you can get different results**, depending on the manner of use:
|
That means, **you can get different results**, depending on the manner of use:
|
||||||
<code>
|
<code>
|
||||||
$ if [ "true" ] || [ -e /does/not/exist ] && [ -e /does/not/exist ]; then echo true; else echo false; fi
|
$ if [ "true" ] || [ -e /does/not/exist ] && [ -e /does/not/exist ]; then echo true; else echo false; fi
|
||||||
false
|
false
|
||||||
|
|
||||||
$ if [ "true" -o -e /does/not/exist -a -e /does/not/exist ]; then echo true; else echo false;fi
|
$ if [ "true" -o -e /does/not/exist -a -e /does/not/exist ]; then echo true; else echo false;fi
|
||||||
true
|
true
|
||||||
</code>
|
</code>
|
||||||
As a result you have to think about it a little or add precedence control (parenthesis).
|
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:
|
For ''&&'' and ''||'' parenthesis means (shell-ly) grouping the commands, and since ''( ... )'' introduces a subshell we will use ''{ ... }'' instead:
|
||||||
<code>
|
<code>
|
||||||
$ if [ "true" ] || { [ -e /does/not/exist ] && [ -e /does/not/exist ] ;} ; then echo true; else echo false; fi
|
$ if [ "true" ] || { [ -e /does/not/exist ] && [ -e /does/not/exist ] ;} ; then echo true; else echo false; fi
|
||||||
true
|
true
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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:
|
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:
|
||||||
<code>
|
<code>
|
||||||
$ if [ \( "true" -o -e /does/not/exist \) -a -e /does/not/exist ]; then echo true; else echo false; fi
|
$ if [ \( "true" -o -e /does/not/exist \) -a -e /does/not/exist ]; then echo true; else echo false; fi
|
||||||
false
|
false
|
||||||
|
|
||||||
# equivalent, but less readable IMHO:
|
# equivalent, but less readable IMHO:
|
||||||
$ if [ '(' "true" -o -e /does/not/exist ')' -a -e /does/not/exist ]; then echo true; else echo false; fi
|
$ if [ '(' "true" -o -e /does/not/exist ')' -a -e /does/not/exist ]; then echo true; else echo false; fi
|
||||||
false
|
false
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -267,14 +267,14 @@ As for AND and OR, there are 2 ways to negate a test with the shell keyword ''!'
|
|||||||
|
|
||||||
|
|
||||||
Here ''!'' negates the exit status of the command ''test'' which is 0 (true), and the else part is executed:
|
Here ''!'' negates the exit status of the command ''test'' which is 0 (true), and the else part is executed:
|
||||||
<code>
|
<code>
|
||||||
if ! [ -d '/tmp' ]; then echo "/tmp doesn't exists"; else echo "/tmp exists"; fi
|
if ! [ -d '/tmp' ]; then echo "/tmp doesn't exists"; else echo "/tmp exists"; fi
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Here the ''test'' command itself exits with status 1 (false) and the else is also executed:
|
Here the ''test'' command itself exits with status 1 (false) and the else is also executed:
|
||||||
<code>
|
<code>
|
||||||
if [ ! -d '/tmp' ]; then echo "/tmp doesn't exists"; else echo "/tmp exists"; fi
|
if [ ! -d '/tmp' ]; then echo "/tmp doesn't exists"; else echo "/tmp exists"; fi
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Unlike for AND and OR, both methods for NOT have an identical behaviour, at least for doing one single test.
|
Unlike for AND and OR, both methods for NOT have an identical behaviour, at least for doing one single test.
|
||||||
|
|
||||||
@ -285,7 +285,7 @@ In this section you will get all the mentioned (and maybe more) possible pitfall
|
|||||||
==== General ====
|
==== 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**:
|
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**:
|
||||||
<code>
|
<code>
|
||||||
From: (PROTECTED)
|
From: (PROTECTED)
|
||||||
Subject: -d option not working. . .?
|
Subject: -d option not working. . .?
|
||||||
Date: Tue, 11 Sep 2007 21:51:59 -0400
|
Date: Tue, 11 Sep 2007 21:51:59 -0400
|
||||||
@ -317,21 +317,21 @@ done
|
|||||||
|
|
||||||
|
|
||||||
Regards
|
Regards
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
See the problem regarding the used test-command (the other potential problems are not of interest here)?
|
See the problem regarding the used test-command (the other potential problems are not of interest here)?
|
||||||
<code>
|
<code>
|
||||||
[-d $i]
|
[-d $i]
|
||||||
</code>
|
</code>
|
||||||
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:
|
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:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
From: Bob Proulx (EMAIL PROTECTED)
|
From: Bob Proulx (EMAIL PROTECTED)
|
||||||
Subject: Re: -d option not working. . .?
|
Subject: Re: -d option not working. . .?
|
||||||
Date: Wed, 12 Sep 2007 10:32:35 -0600
|
Date: Wed, 12 Sep 2007 10:32:35 -0600
|
||||||
To: bug-bash@gnu.org
|
To: bug-bash@gnu.org
|
||||||
|
|
||||||
> (QUOTED TEXT WAS REMOVED)
|
> (QUOTED TEXT WAS REMOVED)
|
||||||
|
|
||||||
The shell is first and foremost a way to launch other commands. The
|
The shell is first and foremost a way to launch other commands. The
|
||||||
syntax is simply "if" followed by a command-list, (e.g. if /some/foo;
|
syntax is simply "if" followed by a command-list, (e.g. if /some/foo;
|
||||||
@ -424,7 +424,7 @@ Fortran using -gt, -eq, etc. looked very normal.
|
|||||||
|
|
||||||
Incorrect use generating unlikely to be intended results:
|
Incorrect use generating unlikely to be intended results:
|
||||||
|
|
||||||
if test 5 > 2 # true, "5" is non-zero length, creates file named "2"
|
if test 5 > 2 # true, "5" is non-zero length, creates file named "2"
|
||||||
|
|
||||||
Intended use:
|
Intended use:
|
||||||
|
|
||||||
@ -439,13 +439,13 @@ shell processes [[ internally all arguments are known and do not need
|
|||||||
to be quoted.
|
to be quoted.
|
||||||
|
|
||||||
if [[ -d $file ]] # okay
|
if [[ -d $file ]] # okay
|
||||||
if [[ 5 > 2 ]] # okay
|
if [[ 5 > 2 ]] # okay
|
||||||
|
|
||||||
I am sure that I am remembering a detail wrong but hopefully this is
|
I am sure that I am remembering a detail wrong but hopefully this is
|
||||||
useful as a gentle introduction and interesting anyway.
|
useful as a gentle introduction and interesting anyway.
|
||||||
|
|
||||||
Bob
|
Bob
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
I hope this text protects you a bit from stepping from one pitfall into the next.
|
I hope this text protects you a bit from stepping from one pitfall into the next.
|
||||||
|
|
||||||
@ -462,12 +462,12 @@ Some code snipplets follow, different ways of shell reaction is used.
|
|||||||
* ''[ "$MYVAR" ]''
|
* ''[ "$MYVAR" ]''
|
||||||
* **Note:** There are possibilities to make a difference if a variable is //undefined// or //NULL// - see [[syntax:pe#use_an_alternate_value|Parameter Expansion - Using an alternate value]]
|
* **Note:** There are possibilities to make a difference if a variable is //undefined// or //NULL// - see [[syntax:pe#use_an_alternate_value|Parameter Expansion - Using an alternate value]]
|
||||||
* **check if a directory exists, if not, create it**
|
* **check if a directory exists, if not, create it**
|
||||||
* ''test ! -d /home/user/foo && mkdir /home/user/foo''
|
* ''test ! -d /home/user/foo && mkdir /home/user/foo''
|
||||||
* ''[ ! -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''
|
* ''if [ ! -d /home/user/foo ]; then mkdir /home/user/foo; fi''
|
||||||
* **check if minimum one parameter was given, and that one is "Hello"**
|
* **check if minimum one parameter was given, and that one is "Hello"**
|
||||||
* ''test $# -ge 1 -a "$1" = "Hello" || exit 1''
|
* ''test $# -ge 1 -a "$1" = "Hello" || exit 1''
|
||||||
* ''[ $# -ge 1 ] && [ "$1" = "Hello" ] || exit 1'' (see [[syntax:basicgrammar#lists | lists description]])
|
* ''[ $# -ge 1 ] && [ "$1" = "Hello" ] || exit 1'' (see [[syntax:basicgrammar#lists | lists description]])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -475,11 +475,11 @@ Some code snipplets follow, different ways of shell reaction is used.
|
|||||||
|
|
||||||
Using a [[syntax:ccmd:classic_for | for-loop]] to iterate through all entries of a directory, if an entry is a directory (''[ -d "$fn" ]''), print its name:
|
Using a [[syntax:ccmd:classic_for | for-loop]] to iterate through all entries of a directory, if an entry is a directory (''[ -d "$fn" ]''), print its name:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
for fn in *; do
|
for fn in *; do
|
||||||
[ -d "$fn" ] && echo "$fn"
|
[ -d "$fn" ] && echo "$fn"
|
||||||
done
|
done
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
|
|
||||||
===== See also =====
|
===== See also =====
|
||||||
|
@ -7,4 +7,4 @@ This small dictionary is not meant as complete super online computer and UNIX(r)
|
|||||||
To get a full description, you should query a real UNIX(r) dictionary or FAQ, this is just for the most important concepts.
|
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}}
|
{{pagequery>@terms *;sort=a,name;group;proper;cols=3}}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
====== Calculating with dc ======
|
====== Calculating with dc ======
|
||||||
|
|
||||||
{{keywords>bash shell scripting arithmetic calculate}}
|
{{keywords>bash shell scripting arithmetic calculate}}
|
||||||
|
|
||||||
===== Introduction =====
|
===== Introduction =====
|
||||||
|
|
||||||
@ -21,10 +21,10 @@ By default ''dc'', unlike ''bc'', doesn't print anything, the result is pushed o
|
|||||||
You have to use the "p" command to print the element at the top of 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:
|
Thus a simple operation looks like:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
$ dc <<< '1 1+pq'
|
$ dc <<< '1 1+pq'
|
||||||
2
|
2
|
||||||
</code>
|
</code>
|
||||||
I used a "here string" present in bash 3.x, ksh93 and zsh. if your
|
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'''.
|
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'''.
|
||||||
|
|
||||||
@ -41,40 +41,40 @@ The classic operations are:
|
|||||||
GNU ''dc'' adds a couple more.
|
GNU ''dc'' adds a couple more.
|
||||||
|
|
||||||
To input a negative number you need to use the ''_'' (underscore) character:
|
To input a negative number you need to use the ''_'' (underscore) character:
|
||||||
<code>
|
<code>
|
||||||
$ dc <<< '1_1-p'
|
$ dc <<< '1_1-p'
|
||||||
2
|
2
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
You can use the //digits// ''0'' to ''9'' and the //letters// ''A'' to ''F'' as numbers, and a dot (''.'') as a decimal point.
|
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.
|
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:
|
A number with a letter is considered hexadecimal:
|
||||||
<code>
|
<code>
|
||||||
dc <<< 'Ap'
|
dc <<< 'Ap'
|
||||||
10
|
10
|
||||||
</code>
|
</code>
|
||||||
The **output** is converted to **base 10** by default
|
The **output** is converted to **base 10** by default
|
||||||
|
|
||||||
===== Scale And Base =====
|
===== Scale And Base =====
|
||||||
|
|
||||||
''dc'' is a calulator with abitrary precision, by default this precision is 0.
|
''dc'' is a calulator with abitrary precision, by default this precision is 0.
|
||||||
thus ''<nowiki>dc <<< "5 4/p"</nowiki>'' prints "1".
|
thus ''<nowiki>dc <<< "5 4/p"</nowiki>'' prints "1".
|
||||||
|
|
||||||
We can increase the precision using the ''k'' command. It pops the value at the top of the stack
|
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:
|
and uses it as the precision argument:
|
||||||
<code>
|
<code>
|
||||||
dc <<< '2k5 4/p' # prints 1.25
|
dc <<< '2k5 4/p' # prints 1.25
|
||||||
dc <<< '4k5 4/p' # prints 1.2500
|
dc <<< '4k5 4/p' # prints 1.2500
|
||||||
dc <<< '100k 2vp'
|
dc <<< '100k 2vp'
|
||||||
1.4142135623730950488016887242096980785696718753769480731766797379907\
|
1.4142135623730950488016887242096980785696718753769480731766797379907\
|
||||||
324784621070388503875343276415727
|
324784621070388503875343276415727
|
||||||
</code>
|
</code>
|
||||||
dc supports //large// precision arguments.
|
dc supports //large// precision arguments.
|
||||||
|
|
||||||
You can change the base used to output (//print//) the numbers with ''o'' and the base used to
|
You can change the base used to output (//print//) the numbers with ''o'' and the base used to
|
||||||
input (//type//) the numbers with ''i'':
|
input (//type//) the numbers with ''i'':
|
||||||
<code>
|
<code>
|
||||||
dc << EOF
|
dc << EOF
|
||||||
20 p# prints 20, output is in base 10
|
20 p# prints 20, output is in base 10
|
||||||
16o # the output is now in base 2 16
|
16o # the output is now in base 2 16
|
||||||
20p # prints 14, in hex
|
20p # prints 14, in hex
|
||||||
@ -82,29 +82,29 @@ dc << EOF
|
|||||||
p # prints 14 this doesn't modify the number in the stack
|
p # prints 14 this doesn't modify the number in the stack
|
||||||
10p # prints 10 the output is done in base 16
|
10p # prints 10 the output is done in base 16
|
||||||
EOF
|
EOF
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Note: when the input value is modified, the base is modified for all commands, including ''i'':
|
Note: when the input value is modified, the base is modified for all commands, including ''i'':
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
dc << EOF
|
dc << EOF
|
||||||
16i 16o # base is 16 for input and output
|
16i 16o # base is 16 for input and output
|
||||||
10p # prints 10
|
10p # prints 10
|
||||||
10i # ! set the base to 10 i.e. to 16 decimal
|
10i # ! set the base to 10 i.e. to 16 decimal
|
||||||
17p # prints 17
|
17p # prints 17
|
||||||
EOF
|
EOF
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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.
|
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.
|
The problem is 10 was typed while the input base 16, thus the base was set to 10 hexadecimal, i.e. 16 decimal.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
dc << EOF
|
dc << EOF
|
||||||
16o16o10p #prints 10
|
16o16o10p #prints 10
|
||||||
Ai # set the base to A in hex i.e. 10
|
Ai # set the base to A in hex i.e. 10
|
||||||
17p # prints 11 in base 16
|
17p # prints 11 in base 16
|
||||||
EOF
|
EOF
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Stack =====
|
===== Stack =====
|
||||||
|
|
||||||
@ -112,8 +112,8 @@ There are two basic commands to manipulate the stack:
|
|||||||
* ''d'' duplicates the top of the stack
|
* ''d'' duplicates the top of the stack
|
||||||
* ''c'' clears the stack
|
* ''c'' clears the stack
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
$ dc << EOF
|
$ dc << EOF
|
||||||
2 # put 2 on the stack
|
2 # put 2 on the stack
|
||||||
d # duplicate i.e. put another 2 on the stack
|
d # duplicate i.e. put another 2 on the stack
|
||||||
*p # multiply and print
|
*p # multiply and print
|
||||||
@ -121,7 +121,7 @@ c p # clear and print
|
|||||||
EOF
|
EOF
|
||||||
4
|
4
|
||||||
dc: stack empty
|
dc: stack empty
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
''c p'' results in an error, as we would expect, as c removes everything
|
''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.//
|
on the stack. //Note: we can use ''#'' to put comments in the script.//
|
||||||
@ -129,12 +129,12 @@ 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
|
If you are lost, you can inspect (i.e. print) the stack using the command
|
||||||
''f''. The stack remains unchanged:
|
''f''. The stack remains unchanged:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
dc <<< '1 2 d 4+f'
|
dc <<< '1 2 d 4+f'
|
||||||
6
|
6
|
||||||
2
|
2
|
||||||
1
|
1
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Note how the first element that will be popped from the stack is printed first, if you are
|
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.
|
used to an HP calculator, it's the reverse.
|
||||||
@ -149,15 +149,15 @@ the range of unsigned char. I'm not sure how you are supposed to use
|
|||||||
the NUL byte.
|
the NUL byte.
|
||||||
Using a register is easy:
|
Using a register is easy:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
dc <<EOF
|
dc <<EOF
|
||||||
12 # put 12 on the stack
|
12 # put 12 on the stack
|
||||||
sa # remove it from the stack (s), and put it in register 'a'
|
sa # remove it from the stack (s), and put it in register 'a'
|
||||||
10 # put 10 on the stack
|
10 # put 10 on the stack
|
||||||
la # read (l) the value of register 'a' and push it on the stack
|
la # read (l) the value of register 'a' and push it on the stack
|
||||||
+p # add the 2 values and print
|
+p # add the 2 values and print
|
||||||
EOF
|
EOF
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
The above snippet uses newlines to embed comments, but it doesn't
|
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.
|
really matter, you can use ''echo '12sa10la+p'| dc'', with the same results.
|
||||||
@ -165,8 +165,8 @@ 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
|
The register can contain more than just a value, **each register is a
|
||||||
stack on its own**.
|
stack on its own**.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
dc <<EOF
|
dc <<EOF
|
||||||
12sa #store 12 in 'a'
|
12sa #store 12 in 'a'
|
||||||
6Sa # with a capital S the 6 is removed
|
6Sa # with a capital S the 6 is removed
|
||||||
# from the main stack and pushed on the 'a' stack
|
# from the main stack and pushed on the 'a' stack
|
||||||
@ -176,30 +176,30 @@ 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
|
# to the main stack and pulls it from the 'a' stack
|
||||||
lap # prints 12, which is now at the top of the stack
|
lap # prints 12, which is now at the top of the stack
|
||||||
EOF
|
EOF
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Macros =====
|
===== Macros =====
|
||||||
|
|
||||||
''dc'' lets you push arbitrary strings on the stack when the strings are enclosed in ''[]''.
|
''dc'' lets you push arbitrary strings on the stack when the strings are enclosed in ''[]''.
|
||||||
You can print it with ''p'': ''<nowiki>dc <<< '[Hello World!]p'</nowiki>'' and you can
|
You can print it with ''p'': ''<nowiki>dc <<< '[Hello World!]p'</nowiki>'' and you can
|
||||||
evalute it with x: ''<nowiki>dc <<< '[1 2+]xp'</nowiki>''.
|
evalute it with x: ''<nowiki>dc <<< '[1 2+]xp'</nowiki>''.
|
||||||
|
|
||||||
This is not that interesting until combined with registers.
|
This is not that interesting until combined with registers.
|
||||||
First, let's say we want to calculate the square of a number
|
First, let's say we want to calculate the square of a number
|
||||||
(don't forget to include ''f'' if you get lost!):
|
(don't forget to include ''f'' if you get lost!):
|
||||||
<code>
|
<code>
|
||||||
dc << EOF
|
dc << EOF
|
||||||
3 # push our number on the stack
|
3 # push our number on the stack
|
||||||
d # duplicate it i.e. push 3 on the stack again
|
d # duplicate it i.e. push 3 on the stack again
|
||||||
d**p # duplicate again and calculate the product and print
|
d**p # duplicate again and calculate the product and print
|
||||||
EOF
|
EOF
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Now we have several cubes to calculate, we could use ''<nowiki>dd**</nowiki>'' several times, or
|
Now we have several cubes to calculate, we could use ''<nowiki>dd**</nowiki>'' several times, or
|
||||||
use a macro.
|
use a macro.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
dc << EOF
|
dc << EOF
|
||||||
[dd**] # push a string
|
[dd**] # push a string
|
||||||
sa # save it in register a
|
sa # save it in register a
|
||||||
3 # push 3 on the stack
|
3 # push 3 on the stack
|
||||||
@ -207,65 +207,65 @@ lax # push the string "dd**" on the stack and execute it
|
|||||||
p # print the result
|
p # print the result
|
||||||
4laxp # same operation for 4, in one line
|
4laxp # same operation for 4, in one line
|
||||||
EOF
|
EOF
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Conditionals and Loops =====
|
===== Conditionals and Loops =====
|
||||||
|
|
||||||
''dc'' can execute a macro stored in a register using the ''lR x'' combo, but
|
''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
|
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
|
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.
|
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:
|
When written, it appears as the reverse of what we are used to reading:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
dc << EOF
|
dc << EOF
|
||||||
[[Hello World]p] sR # store in 'R' a macro that prints Hello World
|
[[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
|
2 1 >R # do nothing 1 is at the top 2 is the second element
|
||||||
1 2 >R # prints Hello World
|
1 2 >R # prints Hello World
|
||||||
EOF
|
EOF
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Some ''dc'' have ''>R <R =R'', GNU ''dc'' had some more, check your manual. Note
|
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
|
that the test "consumes" its operands: the 2 first elements are popped
|
||||||
off the stack (you can verify that ''<nowiki>dc <<< "[f]sR 2 1 >R 1 2 >R f"</nowiki>''
|
off the stack (you can verify that ''<nowiki>dc <<< "[f]sR 2 1 >R 1 2 >R f"</nowiki>''
|
||||||
doesn't print anything)
|
doesn't print anything)
|
||||||
|
|
||||||
Have you noticed how we can //include// a macro (string) in a macro? and as ''dc''
|
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
|
relies on a stack we can, in fact, use the macro recursively (have your
|
||||||
favorite control-c key combo ready ;)) :
|
favorite control-c key combo ready ;)) :
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
dc << EOF
|
dc << EOF
|
||||||
[ [Hello World] p # our macro starts by printing Hello World
|
[ [Hello World] p # our macro starts by printing Hello World
|
||||||
lRx ] # and then executes the macro in R
|
lRx ] # and then executes the macro in R
|
||||||
sR # we store it in the register R
|
sR # we store it in the register R
|
||||||
lRx # and finally executes it.
|
lRx # and finally executes it.
|
||||||
EOF
|
EOF
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
We have recursivity, we have test, we have loops:
|
We have recursivity, we have test, we have loops:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
dc << EOF
|
dc << EOF
|
||||||
[ li # put our index i on the stack
|
[ li # put our index i on the stack
|
||||||
p # print it, to see what's going on
|
p # print it, to see what's going on
|
||||||
1 - # we decrement the index by one
|
1 - # we decrement the index by one
|
||||||
si # store decremented index (i=i-1)
|
si # store decremented index (i=i-1)
|
||||||
0 li >L # if i > 0 then execute L
|
0 li >L # if i > 0 then execute L
|
||||||
] sL # store our macro with the name L
|
] sL # store our macro with the name L
|
||||||
|
|
||||||
10 si # let's give to our index the value 10
|
10 si # let's give to our index the value 10
|
||||||
lLx # and start our loop
|
lLx # and start our loop
|
||||||
EOF
|
EOF
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Of course code written this way is far too easy to read! Make sure to
|
Of course code written this way is far too easy to read! Make sure to
|
||||||
remove all those extra spaces newlines and comments:
|
remove all those extra spaces newlines and comments:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
dc <<< '[lip1-si0li>L]sL10silLx'
|
dc <<< '[lip1-si0li>L]sL10silLx'
|
||||||
dc <<< '[p1-d0<L]sL10lLx' # use the stack instead of a register
|
dc <<< '[p1-d0<L]sL10lLx' # use the stack instead of a register
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
I'll let you figure out the second example, it's not hard, it uses the stack
|
I'll let you figure out the second example, it's not hard, it uses the stack
|
||||||
instead of a register for the index.
|
instead of a register for the index.
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
====== Collapsing Functions ======
|
====== Collapsing Functions ======
|
||||||
|
|
||||||
{{keywords>bash shell scripting example function collapse}}
|
{{keywords>bash shell scripting example function collapse}}
|
||||||
|
|
||||||
===== What is a "Collapsing Function"? =====
|
===== 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.
|
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? =====
|
===== 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.
|
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.
|
||||||
<code>
|
<code>
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
[[ $1 = -v || $1 = --verbose ]] && verbose=1
|
[[ $1 = -v || $1 = --verbose ]] && verbose=1
|
||||||
|
|
||||||
chatter() {
|
chatter() {
|
||||||
if [[ $verbose ]]; then
|
if [[ $verbose ]]; then
|
||||||
@ -30,13 +30,13 @@ for i in {1..10}; do
|
|||||||
chatter "$i"
|
chatter "$i"
|
||||||
sleep 1
|
sleep 1
|
||||||
done
|
done
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== How does it work? =====
|
===== 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.
|
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 =====
|
===== More examples =====
|
||||||
FIXME Add more examples!
|
FIXME Add more examples!
|
||||||
<code>
|
<code>
|
||||||
# Somewhat more portable find -executable
|
# Somewhat more portable find -executable
|
||||||
# FIXME/UNTESTED (I don't have access to all of the different versions of find.)
|
# 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
|
# Usage: find PATH ARGS -- use find like normal, except use -executable instead of
|
||||||
@ -44,21 +44,21 @@ FIXME Add more examples!
|
|||||||
find() {
|
find() {
|
||||||
hash find || { echo 'find not found!'; exit 1; }
|
hash find || { echo 'find not found!'; exit 1; }
|
||||||
# We can be pretty sure "$0" should be executable.
|
# We can be pretty sure "$0" should be executable.
|
||||||
if [[ $(command find "$0" -executable 2> /dev/null) ]]; then
|
if [[ $(command find "$0" -executable 2> /dev/null) ]]; then
|
||||||
unset -f find # We can just use the command find
|
unset -f find # We can just use the command find
|
||||||
elif [[ $(command find "$0" -perm /u+x 2> /dev/null) ]]; then
|
elif [[ $(command find "$0" -perm /u+x 2> /dev/null) ]]; then
|
||||||
find() {
|
find() {
|
||||||
typeset arg args
|
typeset arg args
|
||||||
for arg do
|
for arg do
|
||||||
[[ $arg = -executable ]] && args+=(-perm /u+x) || args+=("$arg")
|
[[ $arg = -executable ]] && args+=(-perm /u+x) || args+=("$arg")
|
||||||
done
|
done
|
||||||
command find "${args[@]}"
|
command find "${args[@]}"
|
||||||
}
|
}
|
||||||
elif [[ $(command find "$0" -perm +u+x 2> /dev/null) ]]; then
|
elif [[ $(command find "$0" -perm +u+x 2> /dev/null) ]]; then
|
||||||
find() {
|
find() {
|
||||||
typeset arg args
|
typeset arg args
|
||||||
for arg do
|
for arg do
|
||||||
[[ $arg = -executable ]] && args+=(-perm +u+x) || args+=("$arg")
|
[[ $arg = -executable ]] && args+=(-perm +u+x) || args+=("$arg")
|
||||||
done
|
done
|
||||||
command find "${args[@]}"
|
command find "${args[@]}"
|
||||||
}
|
}
|
||||||
@ -66,20 +66,20 @@ find() {
|
|||||||
find() {
|
find() {
|
||||||
typeset arg args
|
typeset arg args
|
||||||
for arg do
|
for arg do
|
||||||
[[ $arg = -executable ]] && args+=(-exec test -x {} \; -print) || args+=("$arg")
|
[[ $arg = -executable ]] && args+=(-exec test -x {} \; -print) || args+=("$arg")
|
||||||
done
|
done
|
||||||
command find "${args[@]}"
|
command find "${args[@]}"
|
||||||
}
|
}
|
||||||
fi
|
fi
|
||||||
find "$@"
|
find "$@"
|
||||||
}
|
}
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# Using collapsing functions to turn debug messages on/off
|
# Using collapsing functions to turn debug messages on/off
|
||||||
|
|
||||||
[ "--debug" = "$1" ] && dbg=echo || dbg=:
|
[ "--debug" = "$1" ] && dbg=echo || dbg=:
|
||||||
|
|
||||||
|
|
||||||
# From now on if you use $dbg instead of echo, you can select if messages will be shown
|
# From now on if you use $dbg instead of echo, you can select if messages will be shown
|
||||||
|
@ -1,52 +1,52 @@
|
|||||||
====== Config files for your script ======
|
====== Config files for your script ======
|
||||||
|
|
||||||
{{keywords>bash shell scripting config files include configuration}}
|
{{keywords>bash shell scripting config files include configuration}}
|
||||||
|
|
||||||
===== General =====
|
===== 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:
|
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:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
echo "Reading config...." >&2
|
echo "Reading config...." >&2
|
||||||
source /etc/cool.cfg
|
source /etc/cool.cfg
|
||||||
echo "Config for the username: $cool_username" >&2
|
echo "Config for the username: $cool_username" >&2
|
||||||
echo "Config for the target host: $cool_host" >&2
|
echo "Config for the target host: $cool_host" >&2
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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!
|
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:
|
The sourced file most likely contains something like:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
cool_username="guest"
|
cool_username="guest"
|
||||||
cool_host="foo.example.com"
|
cool_host="foo.example.com"
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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!
|
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:
|
The ''source'' command also is available under the name ''.'' (dot). The usage of the dot is identical:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
echo "Reading config...." >&2
|
echo "Reading config...." >&2
|
||||||
. /etc/cool.cfg #note the space between the dot and the leading slash of /etc.cfg
|
. /etc/cool.cfg #note the space between the dot and the leading slash of /etc.cfg
|
||||||
echo "Config for the username: $cool_username" >&2
|
echo "Config for the username: $cool_username" >&2
|
||||||
echo "Config for the target host: $cool_host" >&2
|
echo "Config for the target host: $cool_host" >&2
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Per-user configs =====
|
===== 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:
|
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:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
echo "Reading system-wide config...." >&2
|
echo "Reading system-wide config...." >&2
|
||||||
. /etc/cool.cfg
|
. /etc/cool.cfg
|
||||||
if [ -r ~/.coolrc ]; then
|
if [ -r ~/.coolrc ]; then
|
||||||
echo "Reading user config...." >&2
|
echo "Reading user config...." >&2
|
||||||
. ~/.coolrc
|
. ~/.coolrc
|
||||||
fi
|
fi
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -56,35 +56,35 @@ As mentioned earlier, the sourced file can contain anything a Bash script can. E
|
|||||||
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).
|
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:
|
Imagine the following "config file", containing some malicious code:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
# cool config file for my even cooler script
|
# cool config file for my even cooler script
|
||||||
username=god_only_knows
|
username=god_only_knows
|
||||||
hostname=www.example.com
|
hostname=www.example.com
|
||||||
password=secret ; echo rm -rf ~/*
|
password=secret ; echo rm -rf ~/*
|
||||||
parameter=foobar && echo "You've bene pwned!";
|
parameter=foobar && echo "You've bene pwned!";
|
||||||
# hey look, weird code follows...
|
# hey look, weird code follows...
|
||||||
echo "I am the skull virus..."
|
echo "I am the skull virus..."
|
||||||
echo rm -fr ~/*
|
echo rm -fr ~/*
|
||||||
mailto=netadmin@example.com
|
mailto=netadmin@example.com
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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:
|
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:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
configfile='/etc/cool.cfg'
|
configfile='/etc/cool.cfg'
|
||||||
configfile_secured='/tmp/cool.cfg'
|
configfile_secured='/tmp/cool.cfg'
|
||||||
|
|
||||||
# check if the file contains something we don't want
|
# check if the file contains something we don't want
|
||||||
if egrep -q -v '^#|^[^ ]*=[^;]*' "$configfile"; then
|
if egrep -q -v '^#|^[^ ]*=[^;]*' "$configfile"; then
|
||||||
echo "Config file is unclean, cleaning it..." >&2
|
echo "Config file is unclean, cleaning it..." >&2
|
||||||
# filter the original to a new file
|
# filter the original to a new file
|
||||||
egrep '^#|^[^ ]*=[^;&]*' "$configfile" > "$configfile_secured"
|
egrep '^#|^[^ ]*=[^;&]*' "$configfile" > "$configfile_secured"
|
||||||
configfile="$configfile_secured"
|
configfile="$configfile_secured"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# now source it, either the original or the filtered variant
|
# now source it, either the original or the filtered variant
|
||||||
source "$configfile"
|
source "$configfile"
|
||||||
</code>
|
</code>
|
||||||
**__To make clear what it does:__** 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.
|
**__To make clear what it does:__** 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.
|
||||||
|
|
||||||
|
@ -1,58 +1,58 @@
|
|||||||
====== Dissect a bad oneliner ======
|
====== Dissect a bad oneliner ======
|
||||||
|
|
||||||
|
|
||||||
<code bash>$ ls *.zip | while read i; do j=`echo $i | sed 's/.zip//g'`; mkdir $j; cd $j; unzip ../$i; cd ..; done</code>
|
<code bash>$ ls *.zip | while read i; do j=`echo $i | sed 's/.zip//g'`; mkdir $j; cd $j; unzip ../$i; cd ..; done</code>
|
||||||
|
|
||||||
This is an actual one-liner someone asked about in ''#bash''. **There are several things wrong with it. Let's break it down!**
|
This is an actual one-liner someone asked about in ''#bash''. **There are several things wrong with it. Let's break it down!**
|
||||||
|
|
||||||
<code bash>$ ls *.zip | while read i; do ...; done</code>
|
<code bash>$ ls *.zip | while read i; do ...; done</code>
|
||||||
|
|
||||||
(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:
|
(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:
|
||||||
|
|
||||||
<code bash>$ for i in *.zip; do j=`echo $i | sed 's/.zip//g'`; mkdir $j; cd $j; unzip ../$i; cd ..; done</code>
|
<code bash>$ for i in *.zip; do j=`echo $i | sed 's/.zip//g'`; mkdir $j; cd $j; unzip ../$i; cd ..; done</code>
|
||||||
|
|
||||||
Let's break it down some more!
|
Let's break it down some more!
|
||||||
|
|
||||||
<code bash>j=`echo $i | sed 's/.zip//g'` # where $i is some name ending in '.zip'</code>
|
<code bash>j=`echo $i | sed 's/.zip//g'` # where $i is some name ending in '.zip'</code>
|
||||||
|
|
||||||
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 [[syntax:expansion:wordsplit | wordsplitting]] 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, [[syntax:expansion:cmdsubst | modern]] ''$()'' construct instead of the old style backticks:
|
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 [[syntax:expansion:wordsplit | wordsplitting]] 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, [[syntax:expansion:cmdsubst | modern]] ''$()'' construct instead of the old style backticks:
|
||||||
|
|
||||||
<code bash>sh $ for i in *.zip; do j=$(basename "$i" ".zip"); mkdir $j; cd $j; unzip ../$i; cd ..; done</code>
|
<code bash>sh $ for i in *.zip; do j=$(basename "$i" ".zip"); mkdir $j; cd $j; unzip ../$i; cd ..; done</code>
|
||||||
|
|
||||||
In Bash we don't need the subshell or the external basename command. See [[syntax:pe#substring_removal | Substring removal with parameter expansion]]:
|
In Bash we don't need the subshell or the external basename command. See [[syntax:pe#substring_removal | Substring removal with parameter expansion]]:
|
||||||
|
|
||||||
<code bash>bash $ for i in *.zip; do j="${i%.zip}"; mkdir $j; cd $j; unzip ../$i; cd ..; done</code>
|
<code bash>bash $ for i in *.zip; do j="${i%.zip}"; mkdir $j; cd $j; unzip ../$i; cd ..; done</code>
|
||||||
|
|
||||||
Let's keep going:
|
Let's keep going:
|
||||||
|
|
||||||
<code bash>$ mkdir $j; cd $j; ...; cd ..</code>
|
<code bash>$ mkdir $j; cd $j; ...; cd ..</code>
|
||||||
|
|
||||||
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 [[syntax:expansion:wordsplit | wordsplitting]] in the previous step? Well, if you don't quote ''$j'', wordsplitting can happen again.
|
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 [[syntax:expansion:wordsplit | wordsplitting]] in the previous step? Well, if you don't quote ''$j'', wordsplitting can happen again.
|
||||||
|
|
||||||
<code bash>$ mkdir "$j" && cd "$j" && ... && cd ..</code>
|
<code bash>$ mkdir "$j" && cd "$j" && ... && cd ..</code>
|
||||||
|
|
||||||
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:
|
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:
|
||||||
|
|
||||||
<code bash>$ mkdir "$j" && cd "$j" && ... && cd -</code>
|
<code bash>$ mkdir "$j" && cd "$j" && ... && cd -</code>
|
||||||
|
|
||||||
(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.)
|
(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:
|
So now we have:
|
||||||
|
|
||||||
<code bash>sh $ for i in *.zip; do j=$(basename "$i" ".zip"); mkdir "$j" && cd "$j" && unzip ../$i && cd -; done</code>
|
<code bash>sh $ for i in *.zip; do j=$(basename "$i" ".zip"); mkdir "$j" && cd "$j" && unzip ../$i && cd -; done</code>
|
||||||
|
|
||||||
<code bash>bash $ for i in *.zip; do j="${i%.zip}"; mkdir "$j" && cd "$j" && unzip ../$i && cd -; done</code>
|
<code bash>bash $ for i in *.zip; do j="${i%.zip}"; mkdir "$j" && cd "$j" && unzip ../$i && cd -; done</code>
|
||||||
|
|
||||||
Let's throw the ''unzip'' command back in the mix:
|
Let's throw the ''unzip'' command back in the mix:
|
||||||
|
|
||||||
<code bash>mkdir "$j" && cd "$j" && unzip ../$i && cd -</code>
|
<code bash>mkdir "$j" && cd "$j" && unzip ../$i && cd -</code>
|
||||||
|
|
||||||
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:
|
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:
|
||||||
|
|
||||||
<code bash>$ mkdir "$j" && unzip -d "$j" "$i"</code>
|
<code bash>$ mkdir "$j" && unzip -d "$j" "$i"</code>
|
||||||
|
|
||||||
<code bash>sh $ for i in *.zip; do j=$(basename "$i" ".zip"); mkdir "$j" && unzip -d "$j" "$i"; done</code>
|
<code bash>sh $ for i in *.zip; do j=$(basename "$i" ".zip"); mkdir "$j" && unzip -d "$j" "$i"; done</code>
|
||||||
|
|
||||||
<code bash>bash $ for i in *.zip; do j="${i%.zip}"; mkdir "$j" && unzip -d "$j" "$i"; done</code>
|
<code bash>bash $ for i in *.zip; do j="${i%.zip}"; mkdir "$j" && unzip -d "$j" "$i"; done</code>
|
||||||
|
|
||||||
There! That's as good as it gets.
|
There! That's as good as it gets.
|
||||||
|
186
howto/edit-ed.md
186
howto/edit-ed.md
@ -1,6 +1,6 @@
|
|||||||
====== Editing files via scripts with ed ======
|
====== Editing files via scripts with ed ======
|
||||||
|
|
||||||
{{keywords>bash shell scripting arguments file editor edit ed sed}}
|
{{keywords>bash shell scripting arguments file editor edit ed sed}}
|
||||||
|
|
||||||
===== Why ed? =====
|
===== Why ed? =====
|
||||||
|
|
||||||
@ -22,37 +22,37 @@ Don't get me wrong, this is **not** meant as anti-''sed'' article! It's just mea
|
|||||||
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:
|
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:
|
||||||
|
|
||||||
**__Pipelines__**
|
**__Pipelines__**
|
||||||
<code>
|
<code>
|
||||||
echo '<ED-COMMANDS>' | ed <FILE>
|
echo '<ED-COMMANDS>' | ed <FILE>
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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:
|
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:
|
||||||
<code>
|
<code>
|
||||||
|
|
||||||
# insertHead "$text" "$file"
|
# insertHead "$text" "$file"
|
||||||
|
|
||||||
insertHead() {
|
insertHead() {
|
||||||
printf '%s\n' H 1i "$1" . w | ed -s "$2"
|
printf '%s\n' H 1i "$1" . w | ed -s "$2"
|
||||||
}
|
}
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
**__Here-strings__**
|
**__Here-strings__**
|
||||||
<code>
|
<code>
|
||||||
ed <FILE> <<< '<ED-COMMANDS>'
|
ed <FILE> <<< '<ED-COMMANDS>'
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
**__Here-documents__**
|
**__Here-documents__**
|
||||||
<code>
|
<code>
|
||||||
ed <FILE> <<EOF
|
ed <FILE> <<EOF
|
||||||
<ED-COMMANDS>
|
<ED-COMMANDS>
|
||||||
EOF
|
EOF
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Which one you prefer is your choice. I will use the here-strings, since it looks best here IMHO.
|
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.
|
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 ''<nowiki>$'TEXT'</nowiki>'', as it can interpret a set of various escape sequences and special characters. I'll use the ''-s'' option to make it less verbose.
|
Since ''ed'' wants commands separated by newlines, I'll use a special Bash quoting method, the C-like strings ''<nowiki>$'TEXT'</nowiki>'', as it can interpret a set of various escape sequences and special characters. I'll use the ''-s'' option to make it less verbose.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -74,30 +74,30 @@ Line addresses or commands using //regular expressions// interpret POSIX Basic R
|
|||||||
|
|
||||||
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:
|
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:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
bash > ed -s file <<< $'H\n,df'
|
bash > ed -s file <<< $'H\n,df'
|
||||||
?
|
?
|
||||||
script, line 2: Invalid command suffix
|
script, line 2: Invalid command suffix
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
While working on your script, you might make errors and destroy your file, you might be tempted to try your script doing something like:
|
While working on your script, you might make errors and destroy your file, you might be tempted to try your script doing something like:
|
||||||
<code>
|
<code>
|
||||||
# Works, but there is better
|
# Works, but there is better
|
||||||
|
|
||||||
# copy my original file
|
# copy my original file
|
||||||
cp file file.test
|
cp file file.test
|
||||||
|
|
||||||
# try my script on the file
|
# try my script on the file
|
||||||
ed -s file.test <<< $'H\n<ed commands>\nw'
|
ed -s file.test <<< $'H\n<ed commands>\nw'
|
||||||
|
|
||||||
# see the results
|
# see the results
|
||||||
cat file.test
|
cat file.test
|
||||||
</code>
|
</code>
|
||||||
There is a much better way though, you can use the ed command ''p'' to print the file, now your testing would look like:
|
There is a much better way though, you can use the ed command ''p'' to print the file, now your testing would look like:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
ed -s file <<< $'H\n<ed commands>\n,p'
|
ed -s file <<< $'H\n<ed commands>\n,p'
|
||||||
</code>
|
</code>
|
||||||
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''.
|
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!**
|
Of course, even if the file is not modified by the ''p'' command, **it's always a good idea to have a backup copy!**
|
||||||
@ -118,74 +118,74 @@ Like ''sed'', ''ed'' also knows the common ''s/FROM/TO/'' command, and it can al
|
|||||||
|
|
||||||
=== Substitutions through the whole file ===
|
=== Substitutions through the whole file ===
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
ed -s test.txt <<< $',s/Windows(R)-compatible/POSIX-conform/g\nw'
|
ed -s test.txt <<< $',s/Windows(R)-compatible/POSIX-conform/g\nw'
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
__Note:__ The comma as single address operator is an alias for ''1,$'' ("all lines").
|
__Note:__ The comma as single address operator is an alias for ''1,$'' ("all lines").
|
||||||
|
|
||||||
=== Substitutions in specific lines ===
|
=== Substitutions in specific lines ===
|
||||||
|
|
||||||
On a line containing ''fruits'', do the substitution:
|
On a line containing ''fruits'', do the substitution:
|
||||||
<code>
|
<code>
|
||||||
ed -s test.txt <<< $'/fruits/s/apple/banana/g\nw'
|
ed -s test.txt <<< $'/fruits/s/apple/banana/g\nw'
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
On the 5th line after the line containing ''fruits'', do the substitution:
|
On the 5th line after the line containing ''fruits'', do the substitution:
|
||||||
<code>
|
<code>
|
||||||
ed -s test.txt <<< $'/fruits/+5s/apple/banana/g\nw'
|
ed -s test.txt <<< $'/fruits/+5s/apple/banana/g\nw'
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== Block operations ====
|
==== Block operations ====
|
||||||
|
|
||||||
=== Delete a block of text ===
|
=== Delete a block of text ===
|
||||||
|
|
||||||
The simple one is a well-known (by position) block of text:
|
The simple one is a well-known (by position) block of text:
|
||||||
<code>
|
<code>
|
||||||
# delete lines number 2 to 4 (2, 3, 4)
|
# delete lines number 2 to 4 (2, 3, 4)
|
||||||
ed -s test.txt <<< $'2,5d\nw'
|
ed -s test.txt <<< $'2,5d\nw'
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
This deletes all lines matching a specific regular expression:
|
This deletes all lines matching a specific regular expression:
|
||||||
<code>
|
<code>
|
||||||
# delete all lines matching foobar
|
# delete all lines matching foobar
|
||||||
ed -s test.txt <<< $'g/foobar/d\nw'
|
ed -s test.txt <<< $'g/foobar/d\nw'
|
||||||
</code>
|
</code>
|
||||||
g/regexp/ applies the command following it to all the lines matching the regexp
|
g/regexp/ applies the command following it to all the lines matching the regexp
|
||||||
|
|
||||||
|
|
||||||
=== Move a block of text ===
|
=== Move a block of text ===
|
||||||
...using the ''m'' command: ''<ADDRESS> m <TARGET-ADDRESS>''
|
...using the ''m'' command: ''<ADDRESS> m <TARGET-ADDRESS>''
|
||||||
|
|
||||||
This is definitely something that can't be done easily with sed.
|
This is definitely something that can't be done easily with sed.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
# moving lines 5-9 to the end of the file
|
# moving lines 5-9 to the end of the file
|
||||||
ed -s test.txt <<< $'5,9m$\nw'
|
ed -s test.txt <<< $'5,9m$\nw'
|
||||||
|
|
||||||
# moving lines 5-9 to line 3
|
# moving lines 5-9 to line 3
|
||||||
ed -s test.txt <<< $'5,9m3\nw'
|
ed -s test.txt <<< $'5,9m3\nw'
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
=== Copy a block of text ===
|
=== Copy a block of text ===
|
||||||
...using the ''t'' command: ''<ADDRESS> t <TARGET-ADDRESS>''
|
...using the ''t'' command: ''<ADDRESS> t <TARGET-ADDRESS>''
|
||||||
|
|
||||||
You use the ''t'' command just like you use the ''m'' (move) command.
|
You use the ''t'' command just like you use the ''m'' (move) command.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
# make a copy of lines 5-9 and place it at the end of the file
|
# make a copy of lines 5-9 and place it at the end of the file
|
||||||
ed -s test.txt <<< $'5,9t$\nw'
|
ed -s test.txt <<< $'5,9t$\nw'
|
||||||
|
|
||||||
# make a copy of lines 5-9 and place it at line 3
|
# make a copy of lines 5-9 and place it at line 3
|
||||||
ed -s test.txt <<< $'5,9t3\nw'
|
ed -s test.txt <<< $'5,9t3\nw'
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
=== Join all lines ===
|
=== Join all lines ===
|
||||||
...but leave the final newline intact. This is done by an extra command: ''j'' (join).
|
...but leave the final newline intact. This is done by an extra command: ''j'' (join).
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
ed -s file <<< $'1,$j\nw'
|
ed -s file <<< $'1,$j\nw'
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Compared with two other methods (using ''tr'' or ''sed''), you don't have to delete all newlines and manually add one at the end.
|
Compared with two other methods (using ''tr'' or ''sed''), you don't have to delete all newlines and manually add one at the end.
|
||||||
|
|
||||||
@ -195,25 +195,25 @@ Compared with two other methods (using ''tr'' or ''sed''), you don't have to del
|
|||||||
=== Insert another file ===
|
=== 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''):
|
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''):
|
||||||
<code>
|
<code>
|
||||||
ed -s FILE1 <<< $'$-1 r FILE2\n,p'
|
ed -s FILE1 <<< $'$-1 r FILE2\n,p'
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
To compare, here's a possible ''sed'' solution which must use Bash arithmetic and the external program ''wc'':
|
To compare, here's a possible ''sed'' solution which must use Bash arithmetic and the external program ''wc'':
|
||||||
<code>
|
<code>
|
||||||
sed "$(($(wc -l < FILE1)-1))r FILE2" FILE1
|
sed "$(($(wc -l < FILE1)-1))r FILE2" FILE1
|
||||||
|
|
||||||
# UPDATE here's one which uses GNU sed's "e" parameter for the s-command
|
# UPDATE here's one which uses GNU sed's "e" parameter for the s-command
|
||||||
# it executes the commands found in pattern space. I'll take that as a
|
# it executes the commands found in pattern space. I'll take that as a
|
||||||
# security risk, but well, sometimes GNU > security, you know...
|
# security risk, but well, sometimes GNU > security, you know...
|
||||||
sed '${h;s/.*/cat FILE2/e;G}' FILE1
|
sed '${h;s/.*/cat FILE2/e;G}' FILE1
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Another approach, in two invocations of sed, that avoids the use of external commands completely:
|
Another approach, in two invocations of sed, that avoids the use of external commands completely:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
sed $'${s/$/\\n-||-/;r FILE2\n}' FILE1 | sed '0,/-||-/{//!h;N;//D};$G'
|
sed $'${s/$/\\n-||-/;r FILE2\n}' FILE1 | sed '0,/-||-/{//!h;N;//D};$G'
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Pitfalls =====
|
===== Pitfalls =====
|
||||||
|
|
||||||
@ -230,16 +230,16 @@ on each line so this command will search the next line matching foo and delete i
|
|||||||
If you want to delete all lines matching foo, or do a subsitution on all lines matching foo
|
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:
|
you have to tell ed about it with the g (global) command:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
echo $'1\n1\n3' > file
|
echo $'1\n1\n3' > file
|
||||||
|
|
||||||
#replace all lines matching 1 by "replacement"
|
#replace all lines matching 1 by "replacement"
|
||||||
ed -s file <<< $'g/1/s/1/replacement/\n,p'
|
ed -s file <<< $'g/1/s/1/replacement/\n,p'
|
||||||
|
|
||||||
#replace the first line matching 1 by "replacement"
|
#replace the first line matching 1 by "replacement"
|
||||||
#(because it starts searching from the last line)
|
#(because it starts searching from the last line)
|
||||||
ed -s file <<< $'s/1/replacement/\n,p'
|
ed -s file <<< $'s/1/replacement/\n,p'
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
**__ an error stops the script __**
|
**__ an error stops the script __**
|
||||||
|
|
||||||
@ -248,21 +248,21 @@ does not find a pattern it's an error, while sed just continues with the next li
|
|||||||
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,
|
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.
|
ed will stop if it cannot find foo on the first line, sed will continue.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
#Gnu sed version
|
#Gnu sed version
|
||||||
sed -e '1s/foo/bar/' -e '$a\something' file
|
sed -e '1s/foo/bar/' -e '$a\something' file
|
||||||
|
|
||||||
#First ed version, does nothing if foo is not found on the first line:
|
#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'
|
ed -s file <<< $'H\n1s/foo/bar/\na\nsomething\n.\nw'
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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,
|
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:
|
thus the substitution will succeed and ed will not produce an error when foo is not found:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
#Second version will add the line with "something" even if foo is not found
|
#Second version will add the line with "something" even if foo is not found
|
||||||
ed -s file <<< $'H\n1g/foo/s/foo/bar/\na\nsomething\n.\nw'
|
ed -s file <<< $'H\n1g/foo/s/foo/bar/\na\nsomething\n.\nw'
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
In fact, even a substitution that fails after a g/ / command does not seem to cause an error, i.e. you can
|
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
|
use a trick like g/./s/foo/bar/ to attempt the substitution on all non blank lines
|
||||||
@ -273,9 +273,9 @@ use a trick like g/./s/foo/bar/ to attempt the substitution on all non blank lin
|
|||||||
|
|
||||||
If you don't quote the delimiter, $ has a special meaning. This sounds obvious but it's
|
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:
|
easy to forget this fact when you use addresses like $-1 or commands like $a. Either quote the $ or the delimiter:
|
||||||
<code>
|
<code>
|
||||||
#fails
|
#fails
|
||||||
ed -s file << EOF
|
ed -s file << EOF
|
||||||
$a
|
$a
|
||||||
last line
|
last line
|
||||||
.
|
.
|
||||||
@ -283,7 +283,7 @@ w
|
|||||||
EOF
|
EOF
|
||||||
|
|
||||||
#ok
|
#ok
|
||||||
ed -s file << EOF
|
ed -s file << EOF
|
||||||
\$a
|
\$a
|
||||||
last line
|
last line
|
||||||
.
|
.
|
||||||
@ -291,28 +291,28 @@ w
|
|||||||
EOF
|
EOF
|
||||||
|
|
||||||
#ok again
|
#ok again
|
||||||
ed -s file << 'EOF'
|
ed -s file << 'EOF'
|
||||||
$a
|
$a
|
||||||
last line
|
last line
|
||||||
.
|
.
|
||||||
w
|
w
|
||||||
EOF
|
EOF
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
**__ "." is not a command __**
|
**__ "." is not a command __**
|
||||||
|
|
||||||
The . used to terminate the command "a" must be the only thing on the line.
|
The . used to terminate the command "a" must be the only thing on the line.
|
||||||
take care if you indent the commands:
|
take care if you indent the commands:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
#ed doesn't care about the spaces before the commands, but the . must be the only thing on the line:
|
#ed doesn't care about the spaces before the commands, but the . must be the only thing on the line:
|
||||||
ed -s file << EOF
|
ed -s file << EOF
|
||||||
a
|
a
|
||||||
my content
|
my content
|
||||||
.
|
.
|
||||||
w
|
w
|
||||||
EOF
|
EOF
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Simulate other commands =====
|
===== Simulate other commands =====
|
||||||
|
|
||||||
@ -320,14 +320,14 @@ Keep in mind that in all the examples below, the entire file will be read into m
|
|||||||
|
|
||||||
==== A simple grep ====
|
==== A simple grep ====
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
ed -s file <<< 'g/foo/p'
|
ed -s file <<< 'g/foo/p'
|
||||||
|
|
||||||
# equivalent
|
# equivalent
|
||||||
ed -s file <<< 'g/foo/'
|
ed -s file <<< 'g/foo/'
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
The name ''grep'' is derived from the notaion ''g/RE/p'' (global => regular expression => print).
|
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
|
ref http://www.catb.org/~esr/jargon/html/G/grep.html
|
||||||
|
|
||||||
|
|
||||||
@ -336,23 +336,23 @@ ref http://www.catb.org/~esr/jargon/html/G/grep.html
|
|||||||
|
|
||||||
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:
|
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:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
ed -s file <<< '='
|
ed -s file <<< '='
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
|
|
||||||
==== cat ====
|
==== cat ====
|
||||||
Yea, it's a joke...
|
Yea, it's a joke...
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
ed -s file <<< $',p'
|
ed -s file <<< $',p'
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
...but a similar thing to ''cat'' showing line-endings and escapes can be done with the ''list'' command (l):
|
...but a similar thing to ''cat'' showing line-endings and escapes can be done with the ''list'' command (l):
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
ed -s file <<< $',l'
|
ed -s file <<< $',l'
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
|
|
||||||
FIXME to be continued
|
FIXME to be continued
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
====== Small getopts tutorial ======
|
====== Small getopts tutorial ======
|
||||||
|
|
||||||
{{keywords>bash shell scripting arguments positional parameters options getopt getopts}}
|
{{keywords>bash shell scripting arguments positional parameters options getopt getopts}}
|
||||||
|
|
||||||
===== Description =====
|
===== Description =====
|
||||||
**Note that** ''getopts'' is neither able to parse GNU-style long options (''<nowiki>--</nowiki>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:
|
**Note that** ''getopts'' is neither able to parse GNU-style long options (''<nowiki>--</nowiki>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.
|
* 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!)
|
* 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, ...)
|
* There's no need to argue with several ''getopt'' implementations which had buggy concepts in the past (whitespace, ...)
|
||||||
@ -17,9 +17,9 @@ Some other methods to parse positional parameters - using neither **getopt** nor
|
|||||||
==== Terminology ====
|
==== Terminology ====
|
||||||
|
|
||||||
It's useful to know what we're talking about here, so let's see... Consider the following command line:
|
It's useful to know what we're talking about here, so let's see... Consider the following command line:
|
||||||
<code>
|
<code>
|
||||||
mybackup -x -f /etc/mybackup.conf -r ./foo.txt ./bar.txt
|
mybackup -x -f /etc/mybackup.conf -r ./foo.txt ./bar.txt
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
These are all positional parameters, but they can be divided into several logical groups:
|
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.
|
* ''-x'' is an **option** (aka **flag** or **switch**). It consists of a dash (''-'') followed by **one** character.
|
||||||
@ -28,9 +28,9 @@ These are all positional parameters, but they can be divided into several logica
|
|||||||
* ''./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**.
|
* ''./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:
|
To give you an idea about why ''getopts'' is useful, The above command line is equivalent to:
|
||||||
<code>
|
<code>
|
||||||
mybackup -xrf /etc/mybackup.conf ./foo.txt ./bar.txt
|
mybackup -xrf /etc/mybackup.conf ./foo.txt ./bar.txt
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
which is complex to parse without the help of ''getopts''.
|
which is complex to parse without the help of ''getopts''.
|
||||||
|
|
||||||
@ -40,20 +40,20 @@ The option flags can be **upper- and lowercase** characters, or **digits**. It m
|
|||||||
|
|
||||||
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:
|
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:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
shift $((OPTIND-1))
|
shift $((OPTIND-1))
|
||||||
# now do something with $@
|
# now do something with $@
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Since ''getopts'' sets an exit status of //FALSE// when there's nothing left to parse, it's easy to use in a while-loop:
|
Since ''getopts'' sets an exit status of //FALSE// when there's nothing left to parse, it's easy to use in a while-loop:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
while getopts ...; do
|
while getopts ...; do
|
||||||
...
|
...
|
||||||
done
|
done
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
''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 ''<nowiki>--</nowiki>'' (double-hyphen), which means [[dict:terms:end_of_options | end of options]].
|
''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 ''<nowiki>--</nowiki>'' (double-hyphen), which means [[dict:terms:end_of_options | end of options]].
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -69,9 +69,9 @@ done
|
|||||||
==== Specify what you want ====
|
==== Specify what you want ====
|
||||||
|
|
||||||
The base-syntax for ''getopts'' is:
|
The base-syntax for ''getopts'' is:
|
||||||
<code>
|
<code>
|
||||||
getopts OPTSTRING VARNAME [ARGS...]
|
getopts OPTSTRING VARNAME [ARGS...]
|
||||||
</code>
|
</code>
|
||||||
where:
|
where:
|
||||||
^''OPTSTRING''|tells ''getopts'' which options to expect and where to expect arguments (see below)|
|
^''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|
|
^''VARNAME''|tells ''getopts'' which shell-variable to use for option reporting|
|
||||||
@ -80,10 +80,10 @@ where:
|
|||||||
=== The option-string ===
|
=== 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'':
|
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'':
|
||||||
<code>getopts fAx VARNAME</code>
|
<code>getopts fAx VARNAME</code>
|
||||||
|
|
||||||
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:
|
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:
|
||||||
<code>getopts fA:x VARNAME</code>
|
<code>getopts fA:x VARNAME</code>
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
@ -95,16 +95,16 @@ You can give your own set of arguments to the utility to parse. Whenever additio
|
|||||||
|
|
||||||
This way, you are able to parse any option set you like, here for example from an array:
|
This way, you are able to parse any option set you like, here for example from an array:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
while getopts :f:h opt "${MY_OWN_SET[@]}"; do
|
while getopts :f:h opt "${MY_OWN_SET[@]}"; do
|
||||||
...
|
...
|
||||||
done
|
done
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
A call to ''getopts'' **without** these additional arguments is **equivalent** to explicitly calling it with ''"$@"'':
|
A call to ''getopts'' **without** these additional arguments is **equivalent** to explicitly calling it with ''"$@"'':
|
||||||
<code>
|
<code>
|
||||||
getopts ... "$@"
|
getopts ... "$@"
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== Error Reporting ====
|
==== Error Reporting ====
|
||||||
|
|
||||||
@ -132,20 +132,20 @@ For productive scripts I recommend to use the silent mode, since everything look
|
|||||||
Enough said - action!
|
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 ('':''):
|
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 ('':''):
|
||||||
<code bash>
|
<code bash>
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
while getopts ":a" opt; do
|
while getopts ":a" opt; do
|
||||||
case $opt in
|
case $opt in
|
||||||
a)
|
a)
|
||||||
echo "-a was triggered!" >&2
|
echo "-a was triggered!" >&2
|
||||||
;;
|
;;
|
||||||
\?)
|
\?)
|
||||||
echo "Invalid option: -$OPTARG" >&2
|
echo "Invalid option: -$OPTARG" >&2
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
I put that into a file named ''go_test.sh'', which is the name you'll see below in the examples.
|
I put that into a file named ''go_test.sh'', which is the name you'll see below in the examples.
|
||||||
|
|
||||||
@ -153,18 +153,18 @@ Let's do some tests:
|
|||||||
|
|
||||||
=== Calling it without any arguments ===
|
=== Calling it without any arguments ===
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
$ ./go_test.sh
|
$ ./go_test.sh
|
||||||
$
|
$
|
||||||
</code>
|
</code>
|
||||||
Nothing happened? Right. ''getopts'' didn't see any valid or invalid options (letters preceded by a dash), so it wasn't triggered.
|
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 ===
|
=== Calling it with non-option arguments ===
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
$ ./go_test.sh /etc/passwd
|
$ ./go_test.sh /etc/passwd
|
||||||
$
|
$
|
||||||
</code>
|
</code>
|
||||||
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.
|
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}''.
|
The arguments given to your script are of course accessible as ''$1'' - ''${N}''.
|
||||||
@ -174,40 +174,40 @@ The arguments given to your script are of course accessible as ''$1'' - ''${N}''
|
|||||||
Now let's trigger ''getopts'': Provide options.
|
Now let's trigger ''getopts'': Provide options.
|
||||||
|
|
||||||
First, an **invalid** one:
|
First, an **invalid** one:
|
||||||
<code>
|
<code>
|
||||||
$ ./go_test.sh -b
|
$ ./go_test.sh -b
|
||||||
Invalid option: -b
|
Invalid option: -b
|
||||||
$
|
$
|
||||||
</code>
|
</code>
|
||||||
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.
|
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''):
|
Now, a **valid** one (''-a''):
|
||||||
<code>
|
<code>
|
||||||
$ ./go_test.sh -a
|
$ ./go_test.sh -a
|
||||||
-a was triggered!
|
-a was triggered!
|
||||||
$
|
$
|
||||||
</code>
|
</code>
|
||||||
You see, the detection works perfectly. The ''a'' was put into the variable ''$opt'' for our case statement.
|
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:
|
Of course it's possible to **mix valid and invalid** options when calling:
|
||||||
<code>
|
<code>
|
||||||
$ ./go_test.sh -a -x -b -c
|
$ ./go_test.sh -a -x -b -c
|
||||||
-a was triggered!
|
-a was triggered!
|
||||||
Invalid option: -x
|
Invalid option: -x
|
||||||
Invalid option: -b
|
Invalid option: -b
|
||||||
Invalid option: -c
|
Invalid option: -c
|
||||||
$
|
$
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Finally, it's of course possible, to give our option **multiple times**:
|
Finally, it's of course possible, to give our option **multiple times**:
|
||||||
<code>
|
<code>
|
||||||
$ ./go_test.sh -a -a -a -a
|
$ ./go_test.sh -a -a -a -a
|
||||||
-a was triggered!
|
-a was triggered!
|
||||||
-a was triggered!
|
-a was triggered!
|
||||||
-a was triggered!
|
-a was triggered!
|
||||||
-a was triggered!
|
-a was triggered!
|
||||||
$
|
$
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
The last examples lead us to some points you may consider:
|
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)
|
* **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)
|
||||||
@ -219,43 +219,43 @@ Let's extend our example from above. Just a little bit:
|
|||||||
* ''-a'' now takes an argument
|
* ''-a'' now takes an argument
|
||||||
* on an error, the parsing exits with ''exit 1''
|
* on an error, the parsing exits with ''exit 1''
|
||||||
|
|
||||||
<code bash>
|
<code bash>
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
while getopts ":a:" opt; do
|
while getopts ":a:" opt; do
|
||||||
case $opt in
|
case $opt in
|
||||||
a)
|
a)
|
||||||
echo "-a was triggered, Parameter: $OPTARG" >&2
|
echo "-a was triggered, Parameter: $OPTARG" >&2
|
||||||
;;
|
;;
|
||||||
\?)
|
\?)
|
||||||
echo "Invalid option: -$OPTARG" >&2
|
echo "Invalid option: -$OPTARG" >&2
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
:)
|
:)
|
||||||
echo "Option -$OPTARG requires an argument." >&2
|
echo "Option -$OPTARG requires an argument." >&2
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Let's do the very same tests we did in the last example:
|
Let's do the very same tests we did in the last example:
|
||||||
|
|
||||||
=== Calling it without any arguments ===
|
=== Calling it without any arguments ===
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
$ ./go_test.sh
|
$ ./go_test.sh
|
||||||
$
|
$
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
As above, nothing happened. It wasn't triggered.
|
As above, nothing happened. It wasn't triggered.
|
||||||
|
|
||||||
=== Calling it with non-option arguments ===
|
=== Calling it with non-option arguments ===
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
$ ./go_test.sh /etc/passwd
|
$ ./go_test.sh /etc/passwd
|
||||||
$
|
$
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
The **very same** case: It wasn't triggered.
|
The **very same** case: It wasn't triggered.
|
||||||
|
|
||||||
@ -263,31 +263,31 @@ The **very same** case: It wasn't triggered.
|
|||||||
|
|
||||||
**Invalid** option:
|
**Invalid** option:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
$ ./go_test.sh -b
|
$ ./go_test.sh -b
|
||||||
Invalid option: -b
|
Invalid option: -b
|
||||||
$
|
$
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
As expected, as above, ''getopts'' didn't accept this option and acted like programmed.
|
As expected, as above, ''getopts'' didn't accept this option and acted like programmed.
|
||||||
|
|
||||||
**Valid** option, but without the mandatory **argument**:
|
**Valid** option, but without the mandatory **argument**:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
$ ./go_test.sh -a
|
$ ./go_test.sh -a
|
||||||
Option -a requires an argument.
|
Option -a requires an argument.
|
||||||
$
|
$
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
The option was okay, but there is an argument missing.
|
The option was okay, but there is an argument missing.
|
||||||
|
|
||||||
Let's provide **the argument**:
|
Let's provide **the argument**:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
$ ./go_test.sh -a /etc/passwd
|
$ ./go_test.sh -a /etc/passwd
|
||||||
-a was triggered, Parameter: /etc/passwd
|
-a was triggered, Parameter: /etc/passwd
|
||||||
$
|
$
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== See also =====
|
===== See also =====
|
||||||
* Internal: [[scripting:posparams]]
|
* Internal: [[scripting:posparams]]
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
====== Lock your script (against parallel execution) ======
|
====== Lock your script (against parallel execution) ======
|
||||||
|
|
||||||
{{keywords>bash shell scripting mutex locking run-control}}
|
{{keywords>bash shell scripting mutex locking run-control}}
|
||||||
|
|
||||||
===== Why lock? =====
|
===== Why lock? =====
|
||||||
|
|
||||||
@ -31,9 +31,9 @@ To create a file or set a file timestamp, usually the command touch is used. The
|
|||||||
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.
|
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.
|
Setting the timestamp is similar: One step to check the timespamp, a second step to set the timestamp.
|
||||||
|
|
||||||
<WRAP center round tip 60%>
|
<WRAP center round tip 60%>
|
||||||
__**Conclusion:**__ We need an operation that does the check and the locking in one step.
|
__**Conclusion:**__ We need an operation that does the check and the locking in one step.
|
||||||
</WRAP>
|
</WRAP>
|
||||||
|
|
||||||
A simple way to get that is to create a **lock directory** - with the mkdir command. It will:
|
A simple way to get that is to create a **lock directory** - with the mkdir command. It will:
|
||||||
|
|
||||||
@ -42,14 +42,14 @@ A simple way to get that is to create a **lock directory** - with the mkdir comm
|
|||||||
|
|
||||||
|
|
||||||
With mkdir it seems, we have our two steps in one simple operation. A (very!) simple locking code might look like this:
|
With mkdir it seems, we have our two steps in one simple operation. A (very!) simple locking code might look like this:
|
||||||
<code bash>
|
<code bash>
|
||||||
if mkdir /var/lock/mylock; then
|
if mkdir /var/lock/mylock; then
|
||||||
echo "Locking succeeded" >&2
|
echo "Locking succeeded" >&2
|
||||||
else
|
else
|
||||||
echo "Lock failed - exit" >&2
|
echo "Lock failed - exit" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
</code>
|
</code>
|
||||||
In case ''mkdir'' reports an error, the script will exit at this point - **the MUTEX did its job!**
|
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// --- //[[sunny_delhi18@yahoo.com|sn18]] 2009/12/19 08:24//
|
//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// --- //[[sunny_delhi18@yahoo.com|sn18]] 2009/12/19 08:24//
|
||||||
@ -58,18 +58,18 @@ In case ''mkdir'' reports an error, the script will exit at this point - **the M
|
|||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
<code bash>
|
<code bash>
|
||||||
|
|
||||||
if ( set -o noclobber; echo "locked" > "$lockfile") 2> /dev/null; then
|
if ( set -o noclobber; echo "locked" > "$lockfile") 2> /dev/null; then
|
||||||
trap 'rm -f "$lockfile"; exit $?' INT TERM EXIT
|
trap 'rm -f "$lockfile"; exit $?' INT TERM EXIT
|
||||||
echo "Locking succeeded" >&2
|
echo "Locking succeeded" >&2
|
||||||
rm -f "$lockfile"
|
rm -f "$lockfile"
|
||||||
else
|
else
|
||||||
echo "Lock failed - exit" >&2
|
echo "Lock failed - exit" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Another explanation of this basic pattern using ''set -C'' can be found [[http://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xcu_chap02.html#tag_23_02_07 | here]].
|
Another explanation of this basic pattern using ''set -C'' can be found [[http://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xcu_chap02.html#tag_23_02_07 | here]].
|
||||||
===== An example =====
|
===== An example =====
|
||||||
@ -83,7 +83,7 @@ There are some differences compared to the very simple example above:
|
|||||||
|
|
||||||
|
|
||||||
Details on how the script is killed aren't given, only code relevant to the locking process is shown:
|
Details on how the script is killed aren't given, only code relevant to the locking process is shown:
|
||||||
<code bash>
|
<code bash>
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# lock dirs/files
|
# lock dirs/files
|
||||||
@ -100,20 +100,20 @@ ENO_RECVSIG=3; ETXT[3]="ENO_RECVSIG"
|
|||||||
### start locking attempt
|
### start locking attempt
|
||||||
###
|
###
|
||||||
|
|
||||||
trap 'ECODE=$?; echo "[statsgen] Exit: ${ETXT[ECODE]}($ECODE)" >&2' 0
|
trap 'ECODE=$?; echo "[statsgen] Exit: ${ETXT[ECODE]}($ECODE)" >&2' 0
|
||||||
echo -n "[statsgen] Locking: " >&2
|
echo -n "[statsgen] Locking: " >&2
|
||||||
|
|
||||||
if mkdir "${LOCKDIR}" &>/dev/null; then
|
if mkdir "${LOCKDIR}" &>/dev/null; then
|
||||||
|
|
||||||
# lock succeeded, install signal handlers before storing the PID just in case
|
# lock succeeded, install signal handlers before storing the PID just in case
|
||||||
# storing the PID fails
|
# storing the PID fails
|
||||||
trap 'ECODE=$?;
|
trap 'ECODE=$?;
|
||||||
echo "[statsgen] Removing lock. Exit: ${ETXT[ECODE]}($ECODE)" >&2
|
echo "[statsgen] Removing lock. Exit: ${ETXT[ECODE]}($ECODE)" >&2
|
||||||
rm -rf "${LOCKDIR}"' 0
|
rm -rf "${LOCKDIR}"' 0
|
||||||
echo "$$" >"${PIDFILE}"
|
echo "$$" >"${PIDFILE}"
|
||||||
# the following handler will exit the script upon receiving these signals
|
# the following handler will exit the script upon receiving these signals
|
||||||
# the trap on "0" (EXIT) from above will be triggered by this trap's "exit" command!
|
# the trap on "0" (EXIT) from above will be triggered by this trap's "exit" command!
|
||||||
trap 'echo "[statsgen] Killed by a signal." >&2
|
trap 'echo "[statsgen] Killed by a signal." >&2
|
||||||
exit ${ENO_RECVSIG}' 1 2 3 15
|
exit ${ENO_RECVSIG}' 1 2 3 15
|
||||||
echo "success, installed signal handlers"
|
echo "success, installed signal handlers"
|
||||||
|
|
||||||
@ -127,24 +127,24 @@ else
|
|||||||
# Thanks to Grzegorz Wierzowiecki for pointing out this race condition on
|
# Thanks to Grzegorz Wierzowiecki for pointing out this race condition on
|
||||||
# http://wiki.grzegorz.wierzowiecki.pl/code:mutex-in-bash
|
# http://wiki.grzegorz.wierzowiecki.pl/code:mutex-in-bash
|
||||||
if [ $? != 0 ]; then
|
if [ $? != 0 ]; then
|
||||||
echo "lock failed, PID ${OTHERPID} is active" >&2
|
echo "lock failed, PID ${OTHERPID} is active" >&2
|
||||||
exit ${ENO_LOCKFAIL}
|
exit ${ENO_LOCKFAIL}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! kill -0 $OTHERPID &>/dev/null; then
|
if ! kill -0 $OTHERPID &>/dev/null; then
|
||||||
# lock is stale, remove it and restart
|
# lock is stale, remove it and restart
|
||||||
echo "removing stale lock of nonexistant PID ${OTHERPID}" >&2
|
echo "removing stale lock of nonexistant PID ${OTHERPID}" >&2
|
||||||
rm -rf "${LOCKDIR}"
|
rm -rf "${LOCKDIR}"
|
||||||
echo "[statsgen] restarting myself" >&2
|
echo "[statsgen] restarting myself" >&2
|
||||||
exec "$0" "$@"
|
exec "$0" "$@"
|
||||||
else
|
else
|
||||||
# lock is valid and OTHERPID is active - exit, we're locked!
|
# lock is valid and OTHERPID is active - exit, we're locked!
|
||||||
echo "lock failed, PID ${OTHERPID} is active" >&2
|
echo "lock failed, PID ${OTHERPID} is active" >&2
|
||||||
exit ${ENO_LOCKFAIL}
|
exit ${ENO_LOCKFAIL}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
fi
|
fi
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Related links =====
|
===== Related links =====
|
||||||
|
|
||||||
|
106
howto/pax.md
106
howto/pax.md
@ -1,6 +1,6 @@
|
|||||||
====== pax - the POSIX archiver ======
|
====== pax - the POSIX archiver ======
|
||||||
|
|
||||||
{{keywords>bash shell scripting POSIX archive tar packing zip}}
|
{{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!
|
pax can do a lot of fancy stuff, feel free to contribute more awesome pax tricks!
|
||||||
|
|
||||||
@ -53,7 +53,7 @@ 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''.
|
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
|
The write mode is the only mode where you need to specify the archive type
|
||||||
with ''-x <TYPE>'', e.g. ''-x ustar''.
|
with ''-x <TYPE>'', e.g. ''-x ustar''.
|
||||||
|
|
||||||
=== Copy ===
|
=== Copy ===
|
||||||
|
|
||||||
@ -74,7 +74,7 @@ This file will be used as input or output, depending on the operation
|
|||||||
|
|
||||||
When pax reads an archive, it tries to guess the archive type.
|
When pax reads an archive, it tries to guess the archive type.
|
||||||
However, in //write// mode, you must specify which type of archive
|
However, in //write// mode, you must specify which type of archive
|
||||||
to append using the ''-x <TYPE>'' switch. If you omit this switch,
|
to append using the ''-x <TYPE>'' switch. If you omit this switch,
|
||||||
a default archive will be created (POSIX says it's implementation defined,
|
a default archive will be created (POSIX says it's implementation defined,
|
||||||
Berkeley ''pax'' creates ''ustar'' if no options are specified).
|
Berkeley ''pax'' creates ''ustar'' if no options are specified).
|
||||||
|
|
||||||
@ -100,15 +100,15 @@ In //read// and //list// modes, you can specify patterns to determine which file
|
|||||||
|
|
||||||
=== Some assorted examples of patterns ===
|
=== Some assorted examples of patterns ===
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
pax -r <myarchive.tar 'data/sales/*.txt' 'data/products/*.png'
|
pax -r <myarchive.tar 'data/sales/*.txt' 'data/products/*.png'
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
pax -r <myarchive.tar 'data/sales/year_200[135].txt'
|
pax -r <myarchive.tar 'data/sales/year_200[135].txt'
|
||||||
# should be equivalent to
|
# should be equivalent to
|
||||||
pax -r <myarchive.tar 'data/sales/year_2001.txt' 'data/sales/year_2003.txt' 'data/sales/year_2005.txt'
|
pax -r <myarchive.tar 'data/sales/year_2001.txt' 'data/sales/year_2003.txt' 'data/sales/year_2005.txt'
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Using pax =====
|
===== Using pax =====
|
||||||
|
|
||||||
@ -119,13 +119,13 @@ system, like you would use ''tar''.
|
|||||||
==== Creating an archive ====
|
==== Creating an archive ====
|
||||||
|
|
||||||
This task is done with basic syntax
|
This task is done with basic syntax
|
||||||
<code>
|
<code>
|
||||||
# archive contents to stdout
|
# archive contents to stdout
|
||||||
pax -w >archive.tar README.txt *.png data/
|
pax -w >archive.tar README.txt *.png data/
|
||||||
|
|
||||||
# equivalent, extract archive contents directly to a file
|
# equivalent, extract archive contents directly to a file
|
||||||
pax -w -x ustar -f archive.tar README.txt *.png data/
|
pax -w -x ustar -f archive.tar README.txt *.png data/
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
''pax'' is in //write// mode, the given filenames are packed into an
|
''pax'' is in //write// mode, the given filenames are packed into an
|
||||||
archive:
|
archive:
|
||||||
@ -139,15 +139,15 @@ files inserted into the archive to ''STDERR''.
|
|||||||
When, and only when, no filename arguments are specified, ''pax'' attempts to
|
When, and only when, no filename arguments are specified, ''pax'' attempts to
|
||||||
read filenames from ''STDIN'', separated by newlines.
|
read filenames from ''STDIN'', separated by newlines.
|
||||||
This way you can easily combine ''find'' with ''pax'':
|
This way you can easily combine ''find'' with ''pax'':
|
||||||
<code>
|
<code>
|
||||||
find . -name '*.txt' | pax -wf textfiles.tar -x ustar
|
find . -name '*.txt' | pax -wf textfiles.tar -x ustar
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== Listing archive contents ====
|
==== Listing archive contents ====
|
||||||
|
|
||||||
The standard output format to list archive members simply is to print each
|
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
|
filename to a separate line. But the output format can be customized to include
|
||||||
permissions, timestamps, etc. with the ''-o listopt=<FORMAT>'' specification.
|
permissions, timestamps, etc. with the ''-o listopt=<FORMAT>'' specification.
|
||||||
The syntax of the format specification is strongly derived from the
|
The syntax of the format specification is strongly derived from the
|
||||||
''printf(3)'' format specification.
|
''printf(3)'' format specification.
|
||||||
|
|
||||||
@ -156,35 +156,35 @@ support these extended listing formats.
|
|||||||
|
|
||||||
However, ''pax'' lists archive members in a ''ls -l''-like format, when you
|
However, ''pax'' lists archive members in a ''ls -l''-like format, when you
|
||||||
give the ''-v'' option:
|
give the ''-v'' option:
|
||||||
<code>
|
<code>
|
||||||
pax -v <myarchive.tar
|
pax -v <myarchive.tar
|
||||||
# or, of course
|
# or, of course
|
||||||
pax -vf myarchive.tar
|
pax -vf myarchive.tar
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== Extracting from an archive ====
|
==== Extracting from an archive ====
|
||||||
|
|
||||||
You can extract all files, or files (not) matching specific patterns from an
|
You can extract all files, or files (not) matching specific patterns from an
|
||||||
archive using constructs like:
|
archive using constructs like:
|
||||||
<code>
|
<code>
|
||||||
# "normal" extraction
|
# "normal" extraction
|
||||||
pax -rf myarchive.tar '*.txt'
|
pax -rf myarchive.tar '*.txt'
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
# with inverted pattern
|
# with inverted pattern
|
||||||
pax -rf myarchive.tar -c '*.txt'
|
pax -rf myarchive.tar -c '*.txt'
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
|
|
||||||
==== Copying files ====
|
==== Copying files ====
|
||||||
|
|
||||||
To copy directory contents to another directory, similar to a
|
To copy directory contents to another directory, similar to a
|
||||||
''cp -a'' command, use:
|
''cp -a'' command, use:
|
||||||
<code>
|
<code>
|
||||||
mkdir destdir
|
mkdir destdir
|
||||||
pax -rw dir destdir #creates a copy of dir in destdir/, i.e. destdir/dir
|
pax -rw dir destdir #creates a copy of dir in destdir/, i.e. destdir/dir
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -192,10 +192,10 @@ pax -rw dir destdir #creates a copy of dir in destdir/, i.e. destdir/dir
|
|||||||
==== Copying files via ssh ====
|
==== Copying files via ssh ====
|
||||||
|
|
||||||
To copy directory contents to another directory on a remote system, use:
|
To copy directory contents to another directory on a remote system, use:
|
||||||
<code>
|
<code>
|
||||||
pax -w localdir | ssh user@host "cd distantdest && pax -r -v"
|
pax -w localdir | ssh user@host "cd distantdest && pax -r -v"
|
||||||
pax -w localdir | gzip | ssh user@host "cd distantdir && gunzip | pax -r -v" #compress the sent data
|
pax -w localdir | gzip | ssh user@host "cd distantdir && gunzip | pax -r -v" #compress the sent data
|
||||||
</code>
|
</code>
|
||||||
These commands create a copy of localdir in distandir (distantdir/dir) on the remote machine.
|
These commands create a copy of localdir in distandir (distantdir/dir) on the remote machine.
|
||||||
|
|
||||||
===== Advanced usage =====
|
===== Advanced usage =====
|
||||||
@ -208,10 +208,10 @@ __**Note:**__ ''-T'' is an extension and is not defined by POSIX.
|
|||||||
Say you have write-access to a fileserver mounted on your filesystem tree.
|
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
|
In //copy// mode, you can tell ''pax'' to copy only files that were
|
||||||
modified today:
|
modified today:
|
||||||
<code>
|
<code>
|
||||||
mkdir /n/mybackups/$(date +%A)/
|
mkdir /n/mybackups/$(date +%A)/
|
||||||
pax -rw -T 0000 data/ /n/mybackups/$(date +%A)/
|
pax -rw -T 0000 data/ /n/mybackups/$(date +%A)/
|
||||||
</code>
|
</code>
|
||||||
This is done using the ''-T'' switch, which normally allows you to specify 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".
|
time window, but in this case, only the start time which means "today at midnight".
|
||||||
|
|
||||||
@ -222,9 +222,9 @@ __**Note:**__ The ''%A'' format from ''date'' expands to the name of the
|
|||||||
current day, localized, e.g. "Friday" (en) or "Mittwoch" (de).
|
current day, localized, e.g. "Friday" (en) or "Mittwoch" (de).
|
||||||
|
|
||||||
The same, but with an archive, can be accomplished by:
|
The same, but with an archive, can be accomplished by:
|
||||||
<code>
|
<code>
|
||||||
pax -w -T 0000 -f /n/mybackups/$(date +%A)
|
pax -w -T 0000 -f /n/mybackups/$(date +%A)
|
||||||
</code>
|
</code>
|
||||||
In this case, the day-name is an archive-file (you don't need a filename
|
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).
|
extension like ''.tar'' but you can add one, if desired).
|
||||||
|
|
||||||
@ -233,11 +233,11 @@ extension like ''.tar'' but you can add one, if desired).
|
|||||||
==== Changing filenames while archiving ====
|
==== 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'' 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/'':
|
||||||
<code>
|
<code>
|
||||||
pax -x ustar -w -f holiday_pictures.tar -s '/^holiday_2007/holiday_pics/' holiday_2007/
|
pax -x ustar -w -f holiday_pictures.tar -s '/^holiday_2007/holiday_pics/' holiday_2007/
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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 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''.
|
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''.
|
||||||
|
|
||||||
@ -248,24 +248,24 @@ Multiple ''-s'' options can be specified on the command line. They are applied t
|
|||||||
The -s command seen above can be used to exclude a file. The substitution must result in a null string:
|
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.
|
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.
|
We are going to replace the names containing /CVS/ with nothing, note the .* they are needed because we need to match the entire pathname.
|
||||||
<code>
|
<code>
|
||||||
pax -w -x ustar -f release.tar -s',.*/CVS/.*,,' myapplication
|
pax -w -x ustar -f release.tar -s',.*/CVS/.*,,' myapplication
|
||||||
</code>
|
</code>
|
||||||
You can use several -s options, for instance, let's say you also want to remove files ending in ~:
|
You can use several -s options, for instance, let's say you also want to remove files ending in ~:
|
||||||
<code>
|
<code>
|
||||||
pax -w -x ustar -f release.tar -'s,.*/CVS/.*,,' -'s/.*~//' myapplication
|
pax -w -x ustar -f release.tar -'s,.*/CVS/.*,,' -'s/.*~//' myapplication
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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:
|
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:
|
||||||
<code>
|
<code>
|
||||||
pax -r -f archive.tar -s',^etc/.*,,' #the etc/ dir is not extracted
|
pax -r -f archive.tar -s',^etc/.*,,' #the etc/ dir is not extracted
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== Getting archive filenames from STDIN ====
|
==== 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:
|
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:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
# Back up config files changed less than 3 days ago
|
# Back up config files changed less than 3 days ago
|
||||||
find /etc -type f -mtime -3 | pax -x ustar -w -f /backups/etc.tar
|
find /etc -type f -mtime -3 | pax -x ustar -w -f /backups/etc.tar
|
||||||
|
|
||||||
@ -277,7 +277,7 @@ find . -type d -print | pax -r -w -d /target
|
|||||||
find . -newer /var/run/mylastbackup -print0 |
|
find . -newer /var/run/mylastbackup -print0 |
|
||||||
pax -0 -x ustar -w -d -f /backups/mybackup.tar
|
pax -0 -x ustar -w -d -f /backups/mybackup.tar
|
||||||
touch /var/run/mylastbackup
|
touch /var/run/mylastbackup
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
The ''-d'' option tells pax ''not'' to recurse into directories it reads (''cpio''-style). Without ''-d'', pax recurses into all directories (''tar''-style).
|
The ''-d'' option tells pax ''not'' to recurse into directories it reads (''cpio''-style). Without ''-d'', pax recurses into all directories (''tar''-style).
|
||||||
|
|
||||||
@ -286,24 +286,24 @@ The ''-d'' option tells pax ''not'' to recurse into directories it reads (''cpio
|
|||||||
===== From tar to pax =====
|
===== From tar to pax =====
|
||||||
|
|
||||||
''pax'' can handle the ''tar'' archive format, if you want to switch to the standard tool an alias like:
|
''pax'' can handle the ''tar'' archive format, if you want to switch to the standard tool an alias like:
|
||||||
<code>
|
<code>
|
||||||
alias tar='echo USE PAX, idiot. pax is the standard archiver!; # '
|
alias tar='echo USE PAX, idiot. pax is the standard archiver!; # '
|
||||||
</code>
|
</code>
|
||||||
in your ''~/.bashrc'' can be useful :-D.
|
in your ''~/.bashrc'' can be useful :-D.
|
||||||
|
|
||||||
Here is a quick table comparing (GNU) ''tar'' and ''pax'' to help you to make the switch:
|
Here is a quick table comparing (GNU) ''tar'' and ''pax'' to help you to make the switch:
|
||||||
^ TAR ^ PAX ^ Notes ^
|
^ 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 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 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 xjvf file.tar.bz2'' | ''bunzip2 <file.tar.bz2 %%|%% pax -rv'' | |
|
||||||
| ''tar cjvf archive.tar.bz2 path ...'' | ''pax -wv path %%|%% bzip2 > archive.tar.bz2'' | |
|
| ''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'' |
|
| ''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!
|
''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 =====
|
===== Implementations =====
|
||||||
|
|
||||||
* [[http://www2.research.att.com/sw/download/ | AT&T AST toolkit]] | [[http://www2.research.att.com/~gsf/man/man1/pax.html | manpage]]
|
* [[http://www2.research.att.com/sw/download/ | AT&T AST toolkit]] | [[http://www2.research.att.com/~gsf/man/man1/pax.html | manpage]]
|
||||||
* [[http://heirloom.sourceforge.net/index.html | Heirloom toolchest]] | [[http://heirloom.sourceforge.net/man/pax.1.html | manpage]]
|
* [[http://heirloom.sourceforge.net/index.html | Heirloom toolchest]] | [[http://heirloom.sourceforge.net/man/pax.1.html | manpage]]
|
||||||
* [[http://www.openbsd.org/cgi-bin/cvsweb/src/bin/pax/ | OpenBSD pax]] | [[http://www.openbsd.org/cgi-bin/man.cgi?query=pax&apropos=0&sektion=0&manpath=OpenBSD+Current&arch=i386&format=html | manpage]]
|
* [[http://www.openbsd.org/cgi-bin/cvsweb/src/bin/pax/ | OpenBSD pax]] | [[http://www.openbsd.org/cgi-bin/man.cgi?query=pax&apropos=0&sektion=0&manpath=OpenBSD+Current&arch=i386&format=html | manpage]]
|
||||||
* [[https://launchpad.net/paxmirabilis | MirBSD pax]] | [[https://www.mirbsd.org/htman/i386/man1/pax.htm | manpage]] - Debian bases their package upon this.
|
* [[https://launchpad.net/paxmirabilis | MirBSD pax]] | [[https://www.mirbsd.org/htman/i386/man1/pax.htm | manpage]] - Debian bases their package upon this.
|
||||||
|
@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
====== Illustrated Redirection Tutorial ======
|
====== Illustrated Redirection Tutorial ======
|
||||||
|
|
||||||
{{keywords>bash shell scripting tutorial redirection redirect file descriptor}}
|
{{keywords>bash shell scripting tutorial redirection redirect file descriptor}}
|
||||||
|
|
||||||
This tutorial is not a complete guide to redirection, it will not
|
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
|
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.
|
you to understand what things like ''3>&2'', ''2>&1'' or ''1>&3-'' do.
|
||||||
|
|
||||||
|
|
||||||
====== stdin, stdout, stderr ======
|
====== stdin, stdout, stderr ======
|
||||||
@ -17,116 +17,116 @@ standard error (''stderr'').
|
|||||||
|
|
||||||
For example, with Bash running in a Linux terminal emulator, you'll see:
|
For example, with Bash running in a Linux terminal emulator, you'll see:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
# lsof +f g -ap $BASHPID -d 0,1,2
|
# lsof +f g -ap $BASHPID -d 0,1,2
|
||||||
COMMAND PID USER FD TYPE FILE-FLAG DEVICE SIZE/OFF NODE NAME
|
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 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 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
|
bash 12135 root 2u CHR RW,LG 136,13 0t0 16 /dev/pts/5
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
This ''/dev/pts/5'' is a pseudo terminal used to emulate a real
|
This ''/dev/pts/5'' is a pseudo terminal used to emulate a real
|
||||||
terminal. Bash reads (''stdin'') from this terminal and prints
|
terminal. Bash reads (''stdin'') from this terminal and prints
|
||||||
via ''stdout'' and ''stderr'' to this terminal.
|
via ''stdout'' and ''stderr'' to this terminal.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
standard input ( 0 ) ---->| /dev/pts/5 |
|
standard input ( 0 ) ---->| /dev/pts/5 |
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
|
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
standard output ( 1 ) ---->| /dev/pts/5 |
|
standard output ( 1 ) ---->| /dev/pts/5 |
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
|
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
standard error ( 2 ) ---->| /dev/pts/5 |
|
standard error ( 2 ) ---->| /dev/pts/5 |
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
When a command, a compound command, a subshell etc. is executed, it inherits
|
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
|
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''.
|
descriptor ''1'' inherited from the shell, which is connected to ''/dev/pts/5''.
|
||||||
|
|
||||||
====== Simple Redirections ======
|
====== Simple Redirections ======
|
||||||
===== Output Redirection "n> file" =====
|
===== Output Redirection "n> file" =====
|
||||||
|
|
||||||
''>'' is probably the simplest redirection.
|
''>'' is probably the simplest redirection.
|
||||||
|
|
||||||
''echo foo > file''
|
''echo foo > file''
|
||||||
|
|
||||||
the ''> file'' after the command alters the file descriptors belonging to the
|
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
|
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:
|
''1>file'') so that it points to the file ''file''. They will look like:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
standard input ( 0 ) ---->| /dev/pts/5 |
|
standard input ( 0 ) ---->| /dev/pts/5 |
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
|
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
standard output ( 1 ) ---->| file |
|
standard output ( 1 ) ---->| file |
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
|
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
standard error ( 2 ) ---->| /dev/pts/5 |
|
standard error ( 2 ) ---->| /dev/pts/5 |
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Now characters written by our command, ''echo'', that are sent to the
|
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
|
standard output, i.e., the file descriptor ''1'', end up in the file
|
||||||
named ''file''.
|
named ''file''.
|
||||||
|
|
||||||
In the same way, command ''2> file'' will change the standard error and
|
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.
|
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
|
What will ''command 3> file'' do? It will open a new file descriptor
|
||||||
pointing to ''file''. The command will then start with:
|
pointing to ''file''. The command will then start with:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
standard input ( 0 ) ---->| /dev/pts/5 |
|
standard input ( 0 ) ---->| /dev/pts/5 |
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
|
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
standard output ( 1 ) ---->| /dev/pts/5 |
|
standard output ( 1 ) ---->| /dev/pts/5 |
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
|
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
standard error ( 2 ) ---->| /dev/pts/5 |
|
standard error ( 2 ) ---->| /dev/pts/5 |
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
|
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
new descriptor ( 3 ) ---->| file |
|
new descriptor ( 3 ) ---->| file |
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
What will the command do with this descriptor? It depends. Often nothing.
|
What will the command do with this descriptor? It depends. Often nothing.
|
||||||
We will see later why we might want other file descriptors.
|
We will see later why we might want other file descriptors.
|
||||||
|
|
||||||
===== Input Redirection "n< file" =====
|
===== Input Redirection "n< file" =====
|
||||||
|
|
||||||
When you run a commandusing ''command < file'', it changes the
|
When you run a commandusing ''command < file'', it changes the
|
||||||
file descriptor ''0'' so that it looks like:
|
file descriptor ''0'' so that it looks like:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
standard input ( 0 ) <----| file |
|
standard input ( 0 ) <----| file |
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
|
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
standard output ( 1 ) ---->| /dev/pts/5 |
|
standard output ( 1 ) ---->| /dev/pts/5 |
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
|
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
standard error ( 2 ) ---->| /dev/pts/5 |
|
standard error ( 2 ) ---->| /dev/pts/5 |
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
If the command reads from ''stdin'', it now will read from ''file'' and not
|
If the command reads from ''stdin'', it now will read from ''file'' and not
|
||||||
from the console.
|
from the console.
|
||||||
|
|
||||||
As with ''>'', ''<'' can be used to open a new file descriptor for reading,
|
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.
|
''command 3<file''. Later we will see how this can be useful.
|
||||||
|
|
||||||
|
|
||||||
===== Pipes | =====
|
===== Pipes | =====
|
||||||
@ -136,204 +136,204 @@ 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
|
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.
|
for the left command, and as a read source for the right command.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
echo foo | cat
|
echo foo | cat
|
||||||
|
|
||||||
--- +--------------+ --- +--------------+
|
--- +--------------+ --- +--------------+
|
||||||
( 0 ) ---->| /dev/pts/5 | ------> ( 0 ) ---->|pipe (read) |
|
( 0 ) ---->| /dev/pts/5 | ------> ( 0 ) ---->|pipe (read) |
|
||||||
--- +--------------+ / --- +--------------+
|
--- +--------------+ / --- +--------------+
|
||||||
/
|
/
|
||||||
--- +--------------+ / --- +--------------+
|
--- +--------------+ / --- +--------------+
|
||||||
( 1 ) ---->| pipe (write) | / ( 1 ) ---->| /dev/pts |
|
( 1 ) ---->| pipe (write) | / ( 1 ) ---->| /dev/pts |
|
||||||
--- +--------------+ --- +--------------+
|
--- +--------------+ --- +--------------+
|
||||||
|
|
||||||
--- +--------------+ --- +--------------+
|
--- +--------------+ --- +--------------+
|
||||||
( 2 ) ---->| /dev/pts/5 | ( 2 ) ---->| /dev/pts/ |
|
( 2 ) ---->| /dev/pts/5 | ( 2 ) ---->| /dev/pts/ |
|
||||||
--- +--------------+ --- +--------------+
|
--- +--------------+ --- +--------------+
|
||||||
|
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
This is possible because the redirections are set up by the shell
|
This is possible because the redirections are set up by the shell
|
||||||
**before** the commands are executed, and the commands inherit the file
|
**before** the commands are executed, and the commands inherit the file
|
||||||
descriptors.
|
descriptors.
|
||||||
|
|
||||||
====== More On File Descriptors ======
|
====== More On File Descriptors ======
|
||||||
===== Duplicating File Descriptor 2>&1 =====
|
===== Duplicating File Descriptor 2>&1 =====
|
||||||
|
|
||||||
We have seen how to open (or redirect) file descriptors. Let us see
|
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
|
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
|
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
|
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''
|
interesting example so we will use ''ls /tmp/ doesnotexist 2>&1 | less''
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
ls /tmp/ doesnotexist 2>&1 | less
|
ls /tmp/ doesnotexist 2>&1 | less
|
||||||
|
|
||||||
--- +--------------+ --- +--------------+
|
--- +--------------+ --- +--------------+
|
||||||
( 0 ) ---->| /dev/pts/5 | ------> ( 0 ) ---->|from the pipe |
|
( 0 ) ---->| /dev/pts/5 | ------> ( 0 ) ---->|from the pipe |
|
||||||
--- +--------------+ / ---> --- +--------------+
|
--- +--------------+ / ---> --- +--------------+
|
||||||
/ /
|
/ /
|
||||||
--- +--------------+ / / --- +--------------+
|
--- +--------------+ / / --- +--------------+
|
||||||
( 1 ) ---->| to the pipe | / / ( 1 ) ---->| /dev/pts |
|
( 1 ) ---->| to the pipe | / / ( 1 ) ---->| /dev/pts |
|
||||||
--- +--------------+ / --- +--------------+
|
--- +--------------+ / --- +--------------+
|
||||||
/
|
/
|
||||||
--- +--------------+ / --- +--------------+
|
--- +--------------+ / --- +--------------+
|
||||||
( 2 ) ---->| to the pipe | / ( 2 ) ---->| /dev/pts/ |
|
( 2 ) ---->| to the pipe | / ( 2 ) ---->| /dev/pts/ |
|
||||||
--- +--------------+ --- +--------------+
|
--- +--------------+ --- +--------------+
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Why is it called //duplicating//? Because after ''2>&1'', we have 2 file
|
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
|
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 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
|
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
|
often misunderstood by people wanting to redirect both standard input
|
||||||
and standard output to the file. Continue reading for more on this.
|
and standard output to the file. Continue reading for more on this.
|
||||||
|
|
||||||
So if you have two file descriptors ''s'' and ''t'' like:
|
So if you have two file descriptors ''s'' and ''t'' like:
|
||||||
<code>
|
<code>
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
a descriptor ( s ) ---->| /some/file |
|
a descriptor ( s ) ---->| /some/file |
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
a descriptor ( t ) ---->| /another/file |
|
a descriptor ( t ) ---->| /another/file |
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Using a ''t>&s'' (where ''t'' and ''s'' are numbers) it means:
|
Using a ''t>&s'' (where ''t'' and ''s'' are numbers) it means:
|
||||||
|
|
||||||
> Copy whatever file descriptor ''s'' contains into file descriptor ''t''
|
> Copy whatever file descriptor ''s'' contains into file descriptor ''t''
|
||||||
|
|
||||||
So you got a copy of this descriptor:
|
So you got a copy of this descriptor:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
a descriptor ( s ) ---->| /some/file |
|
a descriptor ( s ) ---->| /some/file |
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
a descriptor ( t ) ---->| /some/file |
|
a descriptor ( t ) ---->| /some/file |
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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'').
|
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
|
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
|
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.
|
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''.
|
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>
|
<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:
|
So, as a simple example (albeit slightly contrived), is the following:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
exec 3>&1 # Copy 1 into 3
|
exec 3>&1 # Copy 1 into 3
|
||||||
exec 1> logfile # Make 1 opened to write to logfile
|
exec 1> logfile # Make 1 opened to write to logfile
|
||||||
lotsa_stdout # Outputs to fd 1, which writes to logfile
|
lotsa_stdout # Outputs to fd 1, which writes to logfile
|
||||||
exec 1>&3 # Copy 3 back into 1
|
exec 1>&3 # Copy 3 back into 1
|
||||||
echo Done # Output to original stdout
|
echo Done # Output to original stdout
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
|
|
||||||
===== Order Of Redirection, i.e., "> file 2>&1" vs. "2>&1 >file" =====
|
===== Order Of Redirection, i.e., "> file 2>&1" vs. "2>&1 >file" =====
|
||||||
|
|
||||||
While it doesn't matter where the redirections appears on the command
|
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.
|
line, their order does matter. They are set up from left to right.
|
||||||
|
|
||||||
* ''2>&1 >file''
|
* ''2>&1 >file''
|
||||||
|
|
||||||
A common error, is to do ''command 2>&1 > file'' to redirect both ''stderr''
|
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
|
and ''stdout'' to ''file''. Let's see what's going on. First we type the
|
||||||
command in our terminal, the descriptors look like this:
|
command in our terminal, the descriptors look like this:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
standard input ( 0 ) ---->| /dev/pts/5 |
|
standard input ( 0 ) ---->| /dev/pts/5 |
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
|
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
standard output ( 1 ) ---->| /dev/pts/5 |
|
standard output ( 1 ) ---->| /dev/pts/5 |
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
|
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
standard error ( 2 ) ---->| /dev/pts/5 |
|
standard error ( 2 ) ---->| /dev/pts/5 |
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Then our shell, Bash sees ''2>&1'' so it duplicates 1, and the file
|
Then our shell, Bash sees ''2>&1'' so it duplicates 1, and the file
|
||||||
descriptor look like this:
|
descriptor look like this:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
standard input ( 0 ) ---->| /dev/pts/5 |
|
standard input ( 0 ) ---->| /dev/pts/5 |
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
|
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
standard output ( 1 ) ---->| /dev/pts/5 |
|
standard output ( 1 ) ---->| /dev/pts/5 |
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
|
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
standard error ( 2 ) ---->| /dev/pts/5 |
|
standard error ( 2 ) ---->| /dev/pts/5 |
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
That's right, nothing has changed, 2 was already pointing to the same
|
That's right, nothing has changed, 2 was already pointing to the same
|
||||||
place as 1. Now Bash sees ''> file'' and thus changes ''stdout'':
|
place as 1. Now Bash sees ''> file'' and thus changes ''stdout'':
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
standard input ( 0 ) ---->| /dev/pts/5 |
|
standard input ( 0 ) ---->| /dev/pts/5 |
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
|
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
standard output ( 1 ) ---->| file |
|
standard output ( 1 ) ---->| file |
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
|
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
standard error ( 2 ) ---->| /dev/pts/5 |
|
standard error ( 2 ) ---->| /dev/pts/5 |
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
And that's not what we want.
|
And that's not what we want.
|
||||||
|
|
||||||
* ''>file 2>&1''
|
* ''>file 2>&1''
|
||||||
|
|
||||||
Now let's look at the correct ''command >file 2>&1''. We start as in the
|
Now let's look at the correct ''command >file 2>&1''. We start as in the
|
||||||
previous example, and Bash sees ''> file'':
|
previous example, and Bash sees ''> file'':
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
standard input ( 0 ) ---->| /dev/pts/5 |
|
standard input ( 0 ) ---->| /dev/pts/5 |
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
|
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
standard output ( 1 ) ---->| file |
|
standard output ( 1 ) ---->| file |
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
|
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
standard error ( 2 ) ---->| /dev/pts/5 |
|
standard error ( 2 ) ---->| /dev/pts/5 |
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Then it sees our duplication ''2>&1'':
|
Then it sees our duplication ''2>&1'':
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
standard input ( 0 ) ---->| /dev/pts/5 |
|
standard input ( 0 ) ---->| /dev/pts/5 |
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
|
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
standard output ( 1 ) ---->| file |
|
standard output ( 1 ) ---->| file |
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
|
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
standard error ( 2 ) ---->| file |
|
standard error ( 2 ) ---->| file |
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
And voila, both ''1'' and ''2'' are redirected to file.
|
And voila, both ''1'' and ''2'' are redirected to file.
|
||||||
|
|
||||||
===== Why sed 's/foo/bar/' file >file Doesn't Work =====
|
===== Why sed 's/foo/bar/' file >file Doesn't Work =====
|
||||||
|
|
||||||
This is a common error, we want to modify a file using something that
|
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
|
reads from a file and writes the result to ''stdout''. To do this, we redirect stdout
|
||||||
@ -342,7 +342,7 @@ have seen, the redirections are setup before the command is actually
|
|||||||
executed.
|
executed.
|
||||||
|
|
||||||
So **BEFORE** sed starts, standard output has already been redirected, with
|
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
|
the additional side effect that, because we used >, "file" gets truncated. When ''sed'' starts to
|
||||||
read the file, it contains nothing.
|
read the file, it contains nothing.
|
||||||
|
|
||||||
|
|
||||||
@ -354,60 +354,60 @@ program. So what does this have to do with redirection?
|
|||||||
If you don't specify a program, the redirection after ''exec'' modifies the
|
If you don't specify a program, the redirection after ''exec'' modifies the
|
||||||
file descriptors of the current shell.
|
file descriptors of the current shell.
|
||||||
|
|
||||||
For example, all the commands after ''exec 2>file'' will have file
|
For example, all the commands after ''exec 2>file'' will have file
|
||||||
descriptors like:
|
descriptors like:
|
||||||
<code>
|
<code>
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
standard input ( 0 ) ---->| /dev/pts/5 |
|
standard input ( 0 ) ---->| /dev/pts/5 |
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
|
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
standard output ( 1 ) ---->| /dev/pts/5 |
|
standard output ( 1 ) ---->| /dev/pts/5 |
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
|
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
standard error ( 2 ) ---->| file |
|
standard error ( 2 ) ---->| file |
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
All the the errors sent to ''stderr'' by the commands after the ''exec 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
|
will go to the file, just as if you had the command in a script and
|
||||||
ran ''myscript 2>file''.
|
ran ''myscript 2>file''.
|
||||||
|
|
||||||
''exec'' can be used, if, for instance, you want to log the errors the
|
''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
|
commands in your script produce, just add ''exec 2>myscript.errors'' at
|
||||||
the beginning of your script.
|
the beginning of your script.
|
||||||
|
|
||||||
Let's see another use case. We want to read a file line by line, this
|
Let's see another use case. We want to read a file line by line, this
|
||||||
is easy, we just do:
|
is easy, we just do:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
while read -r line;do echo "$line";done < file
|
while read -r line;do echo "$line";done < file
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Now, we want, after printing each line, to do a pause, waiting for
|
Now, we want, after printing each line, to do a pause, waiting for
|
||||||
the user to press a key:
|
the user to press a key:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
while read -r line;do echo "$line"; read -p "Press any key" -n 1;done < file
|
while read -r line;do echo "$line"; read -p "Press any key" -n 1;done < file
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
And, surprise this doesn't work. Why? because the shell descriptor of
|
And, surprise this doesn't work. Why? because the shell descriptor of
|
||||||
the while loop looks like:
|
the while loop looks like:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
standard input ( 0 ) ---->| file |
|
standard input ( 0 ) ---->| file |
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
|
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
standard output ( 1 ) ---->| /dev/pts/5 |
|
standard output ( 1 ) ---->| /dev/pts/5 |
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
|
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
standard error ( 2 ) ---->| /dev/pts/5 |
|
standard error ( 2 ) ---->| /dev/pts/5 |
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
and our read inherits these descriptors, and our command (''read -p "Press any key" -n 1'')
|
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.
|
inherits them, and thus reads from file and not from our terminal.
|
||||||
@ -416,30 +416,30 @@ 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
|
descriptor from which ''read'' should read. Cool. Now let's use ''exec'' to
|
||||||
get another descriptor:
|
get another descriptor:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
exec 3<file
|
exec 3<file
|
||||||
while read -u 3 line;do echo "$line"; read -p "Press any key" -n 1;done
|
while read -u 3 line;do echo "$line"; read -p "Press any key" -n 1;done
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Now the file descriptors look like:
|
Now the file descriptors look like:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
standard input ( 0 ) ---->| /dev/pts/5 |
|
standard input ( 0 ) ---->| /dev/pts/5 |
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
|
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
standard output ( 1 ) ---->| /dev/pts/5 |
|
standard output ( 1 ) ---->| /dev/pts/5 |
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
|
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
standard error ( 2 ) ---->| /dev/pts/5 |
|
standard error ( 2 ) ---->| /dev/pts/5 |
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
|
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
new descriptor ( 3 ) ---->| file |
|
new descriptor ( 3 ) ---->| file |
|
||||||
--- +-----------------------+
|
--- +-----------------------+
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
and it works.
|
and it works.
|
||||||
|
|
||||||
@ -447,182 +447,182 @@ and it works.
|
|||||||
===== Closing The File Descriptors =====
|
===== Closing The File Descriptors =====
|
||||||
|
|
||||||
Closing a file through a file descriptor is easy, just make it a duplicate of -.
|
Closing a file through a file descriptor is easy, just make it a duplicate of -.
|
||||||
For instance, let's close ''stdin <&-'' and ''stderr 2>&-'':
|
For instance, let's close ''stdin <&-'' and ''stderr 2>&-'':
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
bash -c '{ lsof -a -p $$ -d0,1,2 ;} <&- 2>&-'
|
bash -c '{ lsof -a -p $$ -d0,1,2 ;} <&- 2>&-'
|
||||||
COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
|
COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
|
||||||
bash 10668 pgas 1u CHR 136,2 4 /dev/pts/2
|
bash 10668 pgas 1u CHR 136,2 4 /dev/pts/2
|
||||||
</code>
|
</code>
|
||||||
we see that inside the ''{}'' that only ''1'' is still here.
|
we see that inside the ''{}'' that only ''1'' is still here.
|
||||||
|
|
||||||
Though the OS will probably clean up the mess, it is perhaps a
|
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
|
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
|
open a file descriptor with ''exec 3>file'', all the commands afterwards
|
||||||
will inherit it. It's probably better to do something like:
|
will inherit it. It's probably better to do something like:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
exec 3>file
|
exec 3>file
|
||||||
.....
|
.....
|
||||||
#commands that uses 3
|
#commands that uses 3
|
||||||
.....
|
.....
|
||||||
exec 3>&-
|
exec 3>&-
|
||||||
|
|
||||||
#we don't need 3 any more
|
#we don't need 3 any more
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
I've seen some people using this as a way to discard, say stderr, using something like: command 2>&-.
|
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.
|
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.
|
When in doubt, I use 2>/dev/null.
|
||||||
|
|
||||||
|
|
||||||
====== An Example ======
|
====== An Example ======
|
||||||
|
|
||||||
This example comes from [[http://groups.google.com/group/comp.unix.shell/browse_thread/thread/64206d154894a4ef/ffe4c2e382034ed9#ffe4c2e382034ed9|this post (ffe4c2e382034ed9)]] on the comp.unix.shell group:
|
This example comes from [[http://groups.google.com/group/comp.unix.shell/browse_thread/thread/64206d154894a4ef/ffe4c2e382034ed9#ffe4c2e382034ed9|this post (ffe4c2e382034ed9)]] on the comp.unix.shell group:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
cmd1 3>&- |
|
cmd1 3>&- |
|
||||||
cmd2 2>&3 3>&-
|
cmd2 2>&3 3>&-
|
||||||
} 2>&1 >&4 4>&- |
|
} 2>&1 >&4 4>&- |
|
||||||
cmd3 3>&- 4>&-
|
cmd3 3>&- 4>&-
|
||||||
|
|
||||||
} 3>&2 4>&1
|
} 3>&2 4>&1
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
The redirections are processed from left to right, but as the file
|
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
|
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
|
the inner contexts. We will assume that we run this command in a
|
||||||
terminal. Let's start with the outer ''{ } 3>&2 4>&1''.
|
terminal. Let's start with the outer ''{ } 3>&2 4>&1''.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
--- +-------------+ --- +-------------+
|
--- +-------------+ --- +-------------+
|
||||||
( 0 ) ---->| /dev/pts/5 | ( 3 ) ---->| /dev/pts/5 |
|
( 0 ) ---->| /dev/pts/5 | ( 3 ) ---->| /dev/pts/5 |
|
||||||
--- +-------------+ --- +-------------+
|
--- +-------------+ --- +-------------+
|
||||||
|
|
||||||
--- +-------------+ --- +-------------+
|
--- +-------------+ --- +-------------+
|
||||||
( 1 ) ---->| /dev/pts/5 | ( 4 ) ---->| /dev/pts/5 |
|
( 1 ) ---->| /dev/pts/5 | ( 4 ) ---->| /dev/pts/5 |
|
||||||
--- +-------------+ --- +-------------+
|
--- +-------------+ --- +-------------+
|
||||||
|
|
||||||
--- +-------------+
|
--- +-------------+
|
||||||
( 2 ) ---->| /dev/pts/5 |
|
( 2 ) ---->| /dev/pts/5 |
|
||||||
--- +-------------+
|
--- +-------------+
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
We only made 2 copies of ''stderr'' and ''stdout''. ''3>&1 4>&1'' would have
|
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
|
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
|
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
|
with ''1'' pointing to ''file.stdout'' and 2 pointing to ''file.stderr'', you will
|
||||||
see why these redirections are very nice.
|
see why these redirections are very nice.
|
||||||
|
|
||||||
Let's continue with the right part of the second pipe: ''| cmd3 3>&- 4>&-''
|
Let's continue with the right part of the second pipe: ''| cmd3 3>&- 4>&-''
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
--- +-------------+
|
--- +-------------+
|
||||||
( 0 ) ---->| 2nd pipe |
|
( 0 ) ---->| 2nd pipe |
|
||||||
--- +-------------+
|
--- +-------------+
|
||||||
|
|
||||||
--- +-------------+
|
--- +-------------+
|
||||||
( 1 ) ---->| /dev/pts/5 |
|
( 1 ) ---->| /dev/pts/5 |
|
||||||
--- +-------------+
|
--- +-------------+
|
||||||
|
|
||||||
--- +-------------+
|
--- +-------------+
|
||||||
( 2 ) ---->| /dev/pts/5 |
|
( 2 ) ---->| /dev/pts/5 |
|
||||||
--- +-------------+
|
--- +-------------+
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
It inherits the previous file descriptors, closes 3 and 4 and sets up a pipe
|
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>&- |''
|
for reading. Now for the left part of the second pipe ''{...} 2>&1 >&4 4>&- |''
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
--- +-------------+ --- +-------------+
|
--- +-------------+ --- +-------------+
|
||||||
( 0 ) ---->| /dev/pts/5 | ( 3 ) ---->| /dev/pts/5 |
|
( 0 ) ---->| /dev/pts/5 | ( 3 ) ---->| /dev/pts/5 |
|
||||||
--- +-------------+ --- +-------------+
|
--- +-------------+ --- +-------------+
|
||||||
|
|
||||||
--- +-------------+
|
--- +-------------+
|
||||||
( 1 ) ---->| /dev/pts/5 |
|
( 1 ) ---->| /dev/pts/5 |
|
||||||
--- +-------------+
|
--- +-------------+
|
||||||
|
|
||||||
--- +-------------+
|
--- +-------------+
|
||||||
( 2 ) ---->| 2nd pipe |
|
( 2 ) ---->| 2nd pipe |
|
||||||
--- +-------------+
|
--- +-------------+
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
First, The file descriptor ''1'' is connected to the pipe (''|''), then ''2'' is
|
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 ''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
|
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
|
descriptors of the inner ''{}''. Lcet's go inside and have a look at the
|
||||||
right part of the first pipe: ''| cmd2 2>&3 3>&-''
|
right part of the first pipe: ''| cmd2 2>&3 3>&-''
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
--- +-------------+
|
--- +-------------+
|
||||||
( 0 ) ---->| 1st pipe |
|
( 0 ) ---->| 1st pipe |
|
||||||
--- +-------------+
|
--- +-------------+
|
||||||
|
|
||||||
--- +-------------+
|
--- +-------------+
|
||||||
( 1 ) ---->| /dev/pts/5 |
|
( 1 ) ---->| /dev/pts/5 |
|
||||||
--- +-------------+
|
--- +-------------+
|
||||||
|
|
||||||
--- +-------------+
|
--- +-------------+
|
||||||
( 2 ) ---->| /dev/pts/5 |
|
( 2 ) ---->| /dev/pts/5 |
|
||||||
--- +-------------+
|
--- +-------------+
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
It inherits the previous file descriptors, connects 0 to the 1st pipe,
|
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
|
the file descriptor 2 is made a copy of 3, and 3 is closed. Finally, for the left
|
||||||
part of the pipe:
|
part of the pipe:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
--- +-------------+
|
--- +-------------+
|
||||||
( 0 ) ---->| /dev/pts/5 |
|
( 0 ) ---->| /dev/pts/5 |
|
||||||
--- +-------------+
|
--- +-------------+
|
||||||
|
|
||||||
--- +-------------+
|
--- +-------------+
|
||||||
( 1 ) ---->| 1st pipe |
|
( 1 ) ---->| 1st pipe |
|
||||||
--- +-------------+
|
--- +-------------+
|
||||||
|
|
||||||
--- +-------------+
|
--- +-------------+
|
||||||
( 2 ) ---->| 2nd pipe |
|
( 2 ) ---->| 2nd pipe |
|
||||||
--- +-------------+
|
--- +-------------+
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
It also inherits the file descriptor of the left part of the 2nd
|
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.
|
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:
|
The purpose of all this becomes clear if we take only the commands:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
cmd2
|
cmd2
|
||||||
|
|
||||||
--- +-------------+
|
--- +-------------+
|
||||||
-->( 0 ) ---->| 1st pipe |
|
-->( 0 ) ---->| 1st pipe |
|
||||||
/ --- +-------------+
|
/ --- +-------------+
|
||||||
/
|
/
|
||||||
/ --- +-------------+
|
/ --- +-------------+
|
||||||
cmd 1 / ( 1 ) ---->| /dev/pts/5 |
|
cmd 1 / ( 1 ) ---->| /dev/pts/5 |
|
||||||
/ --- +-------------+
|
/ --- +-------------+
|
||||||
/
|
/
|
||||||
--- +-------------+ / --- +-------------+
|
--- +-------------+ / --- +-------------+
|
||||||
( 0 ) ---->| /dev/pts/5 | / ( 2 ) ---->| /dev/pts/5 |
|
( 0 ) ---->| /dev/pts/5 | / ( 2 ) ---->| /dev/pts/5 |
|
||||||
--- +-------------+ / --- +-------------+
|
--- +-------------+ / --- +-------------+
|
||||||
/
|
/
|
||||||
--- +-------------+ / cmd3
|
--- +-------------+ / cmd3
|
||||||
( 1 ) ---->| 1st pipe | /
|
( 1 ) ---->| 1st pipe | /
|
||||||
--- +-------------+ --- +-------------+
|
--- +-------------+ --- +-------------+
|
||||||
------------>( 0 ) ---->| 2nd pipe |
|
------------>( 0 ) ---->| 2nd pipe |
|
||||||
--- +-------------+ / --- +-------------+
|
--- +-------------+ / --- +-------------+
|
||||||
( 2 ) ---->| 2nd pipe |/
|
( 2 ) ---->| 2nd pipe |/
|
||||||
--- +-------------+ --- +-------------+
|
--- +-------------+ --- +-------------+
|
||||||
( 1 ) ---->| /dev/pts/5 |
|
( 1 ) ---->| /dev/pts/5 |
|
||||||
--- +-------------+
|
--- +-------------+
|
||||||
|
|
||||||
--- +-------------+
|
--- +-------------+
|
||||||
( 2 ) ---->| /dev/pts/5 |
|
( 2 ) ---->| /dev/pts/5 |
|
||||||
--- +-------------+
|
--- +-------------+
|
||||||
|
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
|
|
||||||
As said previously, as an exercise, you can start with ''1'' open on a file
|
As said previously, as an exercise, you can start with ''1'' open on a file
|
||||||
@ -632,31 +632,31 @@ to the original ''stdin'' and how the ''stderr'' goes to the original ''stderr''
|
|||||||
|
|
||||||
====== Syntax ======
|
====== Syntax ======
|
||||||
|
|
||||||
I used to have trouble choosing between ''0&<3'' ''3&>1'' ''3>&1'' ''<nowiki>->2</nowiki>'' ''-<&0'' ''&-<0''
|
I used to have trouble choosing between ''0&<3'' ''3&>1'' ''3>&1'' ''<nowiki>->2</nowiki>'' ''-<&0'' ''&-<0''
|
||||||
''0<&-'' etc... (I think probably because the syntax is more representative of the result, i.e., the redirection, than
|
''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).
|
what is done, i.e., opening, closing, or duplicating file descriptors).
|
||||||
|
|
||||||
If this fits your situation, then maybe the following "rules" will help
|
If this fits your situation, then maybe the following "rules" will help
|
||||||
you, a redirection is always like the following:
|
you, a redirection is always like the following:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
lhs op rhs
|
lhs op rhs
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
* ''lhs'' is always a file description, i.e., a number:
|
* ''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 ''<nowiki>>></nowiki>'', there is an implicit 1.
|
* 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 ''<nowiki>>></nowiki>'', there is an implicit 1.
|
||||||
|
|
||||||
|
|
||||||
* ''op'' is ''<'', ''>'', ''<nowiki>>></nowiki>'', ''>|'', or ''<>'':
|
* ''op'' is ''<'', ''>'', ''<nowiki>>></nowiki>'', ''>|'', or ''<>'':
|
||||||
* ''<'' if the file decriptor in ''lhs'' will be read, ''>'' if it will be written, ''<nowiki>>></nowiki>'' if data is to be appended to the file, ''>|'' to overwrite an existing file or ''<>'' if it will be both read and written.
|
* ''<'' if the file decriptor in ''lhs'' will be read, ''>'' if it will be written, ''<nowiki>>></nowiki>'' 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:
|
* ''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.
|
* 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
|
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 ''<nowiki>&->0</nowiki>'' is
|
inexact, but I think it really helps to easily find that, say ''<nowiki>&->0</nowiki>'' is
|
||||||
incorrect.
|
incorrect.
|
||||||
|
|
||||||
==== A note on style ====
|
==== A note on style ====
|
||||||
@ -669,29 +669,29 @@ The shell is pretty loose about what it considers a valid redirect. While opinio
|
|||||||
|
|
||||||
* **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.
|
* **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]])
|
* **Never** use the Csh ''&>foo'' and ''>&foo'' shorthand redirects. Use the long form ''>foo 2>&1''. (see: [[obsolete]])
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
# Good! This is clearly a simple commmand with two arguments and 4 redirections
|
# Good! This is clearly a simple commmand with two arguments and 4 redirections
|
||||||
cmd arg1 arg2 <myFile 3<&1 2>/dev/null >&2
|
cmd arg1 arg2 <myFile 3<&1 2>/dev/null >&2
|
||||||
|
|
||||||
# Good!
|
# Good!
|
||||||
{ cmd1 <<<'my input'; cmd2; } >someFile
|
{ cmd1 <<<'my input'; cmd2; } >someFile
|
||||||
|
|
||||||
# Bad. Is the "1" 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).
|
# Bad. Is the "1" 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.
|
# The redirects are also not delimited in any obvious way.
|
||||||
cmd 2>& 1 <<< stuff
|
cmd 2>& 1 <<< stuff
|
||||||
|
|
||||||
# Hideously Bad. It's difficult to tell where the redirects are and whether they're even valid redirects.
|
# 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.
|
# This is in fact one command with one argument, an assignment, and three redirects.
|
||||||
foo=bar<baz bork<<< blarg>bleh
|
foo=bar<baz bork<<< blarg>bleh
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
====== Conclusion ======
|
====== Conclusion ======
|
||||||
|
|
||||||
I hope this tutorial worked for you.
|
I hope this tutorial worked for you.
|
||||||
|
|
||||||
I lied, I did not explain ''1>&3-'', go check the manual ;-)
|
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
|
Thanks to Stéphane Chazelas from whom I stole both the intro and the
|
||||||
example....
|
example....
|
||||||
|
@ -4,7 +4,7 @@ The one of the simplest way to check your bash/sh scripts is run it and check it
|
|||||||
|
|
||||||
We have a simple **stat.sh** script:
|
We have a simple **stat.sh** script:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
if [ -z "$1" ]
|
if [ -z "$1" ]
|
||||||
@ -19,26 +19,26 @@ FILES=$(find $DIR -name '*.py' | wc -l)
|
|||||||
LINES=$((find $DIR -name '*.py' -print0 | xargs -0 cat) | wc -l)
|
LINES=$((find $DIR -name '*.py' -print0 | xargs -0 cat) | wc -l)
|
||||||
echo "PYTHON FILES: $FILES"
|
echo "PYTHON FILES: $FILES"
|
||||||
echo "PYTHON LINES: $LINES"
|
echo "PYTHON LINES: $LINES"
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
This script evaluate the number of python files and the number of python code lines in the files.
|
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>**
|
We can use it like **./stat.sh <dir>**
|
||||||
|
|
||||||
==== Create testsuit ====
|
==== Create testsuit ====
|
||||||
|
|
||||||
Then make test suits for **stat.sh**. We make a directory **testsuit** which contain test python files.
|
Then make test suits for **stat.sh**. We make a directory **testsuit** which contain test python files.
|
||||||
|
|
||||||
**testsuit/main.py**
|
**testsuit/main.py**
|
||||||
<code>
|
<code>
|
||||||
import foo
|
import foo
|
||||||
print(foo)
|
print(foo)
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
**testsuit/foo.py**
|
**testsuit/foo.py**
|
||||||
<code>
|
<code>
|
||||||
BAR = 1
|
BAR = 1
|
||||||
BUZ = BAR + 2
|
BUZ = BAR + 2
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Ok! Our test suit is ready!
|
Ok! Our test suit is ready!
|
||||||
We have 2 python files which contains 4 lines of code.
|
We have 2 python files which contains 4 lines of code.
|
||||||
@ -49,22 +49,22 @@ Lets write tests. We just write a shell command for testing our work.
|
|||||||
|
|
||||||
Create file **tests.bashtest**:
|
Create file **tests.bashtest**:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
$ ./stat.sh testsuit/
|
$ ./stat.sh testsuit/
|
||||||
Evaluate *.py statistics
|
Evaluate *.py statistics
|
||||||
PYTHON FILES: 2
|
PYTHON FILES: 2
|
||||||
PYTHON LINES: 4
|
PYTHON LINES: 4
|
||||||
|
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
This is our test! This is simple. Try to run it.
|
This is our test! This is simple. Try to run it.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
# install bashtest if required!
|
# install bashtest if required!
|
||||||
$ pip install bashtest
|
$ pip install bashtest
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
# run tests
|
# run tests
|
||||||
$ bashtest *.bashtest
|
$ bashtest *.bashtest
|
||||||
1 items passed all tests:
|
1 items passed all tests:
|
||||||
@ -72,11 +72,11 @@ $ bashtest *.bashtest
|
|||||||
1 tests in 1 items.
|
1 tests in 1 items.
|
||||||
1 passed and 0 failed.
|
1 passed and 0 failed.
|
||||||
Test passed.
|
Test passed.
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Thats all. We wrote one test. You can write more tests if you want.
|
Thats all. We wrote one test. You can write more tests if you want.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
$ ls testsuit/
|
$ ls testsuit/
|
||||||
foo.py main.py
|
foo.py main.py
|
||||||
|
|
||||||
@ -85,18 +85,18 @@ Evaluate *.py statistics
|
|||||||
PYTHON FILES: 2
|
PYTHON FILES: 2
|
||||||
PYTHON LINES: 4
|
PYTHON LINES: 4
|
||||||
|
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
And run tests again:
|
And run tests again:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
$ bashtest *.bashtest
|
$ bashtest *.bashtest
|
||||||
1 items passed all tests:
|
1 items passed all tests:
|
||||||
2 tests in tests.bashtest
|
2 tests in tests.bashtest
|
||||||
2 tests in 1 items.
|
2 tests in 1 items.
|
||||||
2 passed and 0 failed.
|
2 passed and 0 failed.
|
||||||
Test passed.
|
Test passed.
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
You can find more **.bashtest** examples in the [[https://github.com/pahaz/bashtest | bashtest github repo]]. You can also write your question or report a bug [[https://github.com/pahaz/bashtest/issues | here]].
|
You can find more **.bashtest** examples in the [[https://github.com/pahaz/bashtest | bashtest github repo]]. You can also write your question or report a bug [[https://github.com/pahaz/bashtest/issues | here]].
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
====== List of shell options ======
|
====== List of shell options ======
|
||||||
|
|
||||||
{{keywords>bash shell scripting options runtime variable behaviour}}
|
{{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.
|
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.
|
||||||
|
|
||||||
@ -145,7 +145,7 @@ If set, the extended [[syntax:pattern | pattern matching]] features are enabled.
|
|||||||
^Option: |''extquote'' ^Since: |3.0-alpha (?) |
|
^Option: |''extquote'' ^Since: |3.0-alpha (?) |
|
||||||
^Shell mode: |all ^Default: |on |
|
^Shell mode: |all ^Default: |on |
|
||||||
|
|
||||||
If set, ''<nowiki>$'string'</nowiki>'' and ''$"string"'' quoting is performed within [[syntax:pe | parameter expansions]] enclosed in double quotes. See the important note below under [[#parser_configurations | Parser configurations]].
|
If set, ''<nowiki>$'string'</nowiki>'' and ''$"string"'' quoting is performed within [[syntax:pe | parameter expansions]] enclosed in double quotes. See the important note below under [[#parser_configurations | Parser configurations]].
|
||||||
|
|
||||||
==== failglob ====
|
==== failglob ====
|
||||||
^Option: |''failglob'' ^Since: |3.0-alpha |
|
^Option: |''failglob'' ^Since: |3.0-alpha |
|
||||||
@ -171,7 +171,7 @@ characters will collate together.
|
|||||||
^Option: |''globstar'' ^Since: |4.0-alpha |
|
^Option: |''globstar'' ^Since: |4.0-alpha |
|
||||||
^Shell mode: |all ^Default: |off |
|
^Shell mode: |all ^Default: |off |
|
||||||
|
|
||||||
If set, recursive globbing with ''<nowiki>**</nowiki>'' is enabled.
|
If set, recursive globbing with ''<nowiki>**</nowiki>'' is enabled.
|
||||||
|
|
||||||
==== gnu_errfmt ====
|
==== gnu_errfmt ====
|
||||||
^Option: |''gnu_errfmt'' ^Since: |3.0-alpha |
|
^Option: |''gnu_errfmt'' ^Since: |3.0-alpha |
|
||||||
@ -261,7 +261,7 @@ If set, Bash matches filenames in a case-insensitive fashion when performing pat
|
|||||||
^Option: |''nocasematch'' ^Since: |3.1-alpha1 |
|
^Option: |''nocasematch'' ^Since: |3.1-alpha1 |
|
||||||
^Shell mode: |all ^Default: |off |
|
^Shell mode: |all ^Default: |off |
|
||||||
|
|
||||||
If set, Bash matches patterns in a case-insensitive fashion when performing matching while executing ''case'' or ''<nowiki>[[</nowiki>'' conditional commands.
|
If set, Bash matches patterns in a case-insensitive fashion when performing matching while executing ''case'' or ''<nowiki>[[</nowiki>'' conditional commands.
|
||||||
|
|
||||||
==== nullglob ====
|
==== nullglob ====
|
||||||
^Option: |''nullglob'' ^Since: |unknown |
|
^Option: |''nullglob'' ^Since: |unknown |
|
||||||
@ -323,17 +323,17 @@ There are two options that influence the parsing this way:
|
|||||||
* ''extquote''
|
* ''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**.
|
Consequence: You **can't** use the new syntax (e.g. the extended globbing syntax) and the command to enable it **in the same line**.
|
||||||
<code>
|
<code>
|
||||||
$ shopt -s extglob; echo !(*.txt) # this is the WRONG way!
|
$ shopt -s extglob; echo !(*.txt) # this is the WRONG way!
|
||||||
-bash: syntax error near unexpected token `('
|
-bash: syntax error near unexpected token `('
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
You have to configure the parser **before** a line with new syntax is parsed:
|
You have to configure the parser **before** a line with new syntax is parsed:
|
||||||
<code>
|
<code>
|
||||||
$ shopt -s extglob # standalone - CORRECT way!
|
$ shopt -s extglob # standalone - CORRECT way!
|
||||||
$ echo !(*.txt)
|
$ echo !(*.txt)
|
||||||
...
|
...
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== See also =====
|
===== See also =====
|
||||||
|
|
||||||
|
@ -2,17 +2,17 @@ Comments extracted from the bash source and therefore Copyright (C) 1987-2004 Fr
|
|||||||
the terms of the GNU General Public License etc..
|
the terms of the GNU General Public License etc..
|
||||||
|
|
||||||
from ''mailcheck.c'':
|
from ''mailcheck.c'':
|
||||||
<code C>
|
<code C>
|
||||||
/* check_mail () is useful for more than just checking mail. Since it has
|
/* 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
|
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
|
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
|
file has been read, thus letting one know when someone else has logged
|
||||||
in. Pretty good, huh? */
|
in. Pretty good, huh? */
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
From ''builtins/read.def'':
|
From ''builtins/read.def'':
|
||||||
|
|
||||||
<code C>
|
<code C>
|
||||||
/* If there are no variables, save the text of the line read to the
|
/* If there are no variables, save the text of the line read to the
|
||||||
variable $REPLY. ksh93 strips leading and trailing IFS whitespace,
|
variable $REPLY. ksh93 strips leading and trailing IFS whitespace,
|
||||||
so that `read x ; echo "$x"' and `read ; echo "$REPLY"' behave the
|
so that `read x ; echo "$x"' and `read ; echo "$REPLY"' behave the
|
||||||
@ -21,10 +21,10 @@ From ''builtins/read.def'':
|
|||||||
to read a line completely without interpretation or modification
|
to read a line completely without interpretation or modification
|
||||||
unless you mess with $IFS (e.g., setting it to the empty string).
|
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. */
|
If you disagree, change the occurrences of `#if 0' to `#if 1' below. */
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
from ''variables.c'':
|
from ''variables.c'':
|
||||||
<code C>
|
<code C>
|
||||||
/*
|
/*
|
||||||
* 24 October 2001
|
* 24 October 2001
|
||||||
*
|
*
|
||||||
@ -35,10 +35,10 @@ from ''variables.c'':
|
|||||||
* I've made that behavior conditional on SSH_SOURCE_BASHRC being defined
|
* I've made that behavior conditional on SSH_SOURCE_BASHRC being defined
|
||||||
* in config-top.h.
|
* in config-top.h.
|
||||||
*/
|
*/
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
From ''shell.h'':
|
From ''shell.h'':
|
||||||
<code C>
|
<code C>
|
||||||
/* Values that can be returned by execute_command (). */
|
/* Values that can be returned by execute_command (). */
|
||||||
#define EXECUTION_FAILURE 1
|
#define EXECUTION_FAILURE 1
|
||||||
#define EXECUTION_SUCCESS 0
|
#define EXECUTION_SUCCESS 0
|
||||||
@ -54,12 +54,12 @@ From ''shell.h'':
|
|||||||
#define EX_NOINPUT 126
|
#define EX_NOINPUT 126
|
||||||
#define EX_NOTFOUND 127
|
#define EX_NOTFOUND 127
|
||||||
|
|
||||||
#define EX_SHERRBASE 256 /* all special error values are > this. */
|
#define EX_SHERRBASE 256 /* all special error values are > this. */
|
||||||
|
|
||||||
#define EX_BADSYNTAX 257 /* shell syntax error */
|
#define EX_BADSYNTAX 257 /* shell syntax error */
|
||||||
#define EX_USAGE 258 /* syntax error in usage */
|
#define EX_USAGE 258 /* syntax error in usage */
|
||||||
#define EX_REDIRFAIL 259 /* redirection failed */
|
#define EX_REDIRFAIL 259 /* redirection failed */
|
||||||
#define EX_BADASSIGN 260 /* variable assignment error */
|
#define EX_BADASSIGN 260 /* variable assignment error */
|
||||||
#define EX_EXPFAIL 261 /* word expansion failed */
|
#define EX_EXPFAIL 261 /* word expansion failed */
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
|
@ -4,49 +4,49 @@ Nothing special, just my private collection of some more or less funny shell stu
|
|||||||
|
|
||||||
Usually Bash and/or Linux (GNU Toolset) specific.
|
Usually Bash and/or Linux (GNU Toolset) specific.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
$ %blow
|
$ %blow
|
||||||
-bash: fg: %blow: no such job
|
-bash: fg: %blow: no such job
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
$ ar m god
|
$ ar m god
|
||||||
ar: creating god
|
ar: creating god
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
$ touch /pussy
|
$ touch /pussy
|
||||||
touch: cannot touch `/pussy': Permission denied
|
touch: cannot touch `/pussy': Permission denied
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
$ mount; fsck; fsck; fsck; umount; sleep
|
$ mount; fsck; fsck; fsck; umount; sleep
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
# the lover variant
|
# the lover variant
|
||||||
$ unzip; strip; touch; finger; grep; mount; fsck; more; yes; fsck; fsck; umount; sleep
|
$ unzip; strip; touch; finger; grep; mount; fsck; more; yes; fsck; fsck; umount; sleep
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
# it's not directly funny, only because it's not obvious that this is an sed command
|
# it's not directly funny, only because it's not obvious that this is an sed command
|
||||||
# for the <<<, it works only in Bash
|
# for the <<<, it works only in Bash
|
||||||
$ sed streetlight <<< reeter
|
$ sed streetlight <<< reeter
|
||||||
lighter
|
lighter
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
# see above for comments
|
# see above for comments
|
||||||
$ sed statement <<< cat
|
$ sed statement <<< cat
|
||||||
cement
|
cement
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
$ \(-
|
$ \(-
|
||||||
bash: (-: command not found
|
bash: (-: command not found
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
$ echo '[q]sa[ln0=aln256%Pln256/snlbx]sb3135071790101768542287578439snlbxq'|dc
|
$ echo '[q]sa[ln0=aln256%Pln256/snlbx]sb3135071790101768542287578439snlbxq'|dc
|
||||||
GET A LIFE!
|
GET A LIFE!
|
||||||
</code>
|
</code>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
====== Bash's behaviour ======
|
====== Bash's behaviour ======
|
||||||
|
|
||||||
{{keywords>bash shell scripting startup files dotfiles modes POSIX}}
|
{{keywords>bash shell scripting startup files dotfiles modes POSIX}}
|
||||||
|
|
||||||
FIXME incomplete
|
FIXME incomplete
|
||||||
|
|
||||||
@ -20,13 +20,13 @@ Why an extra login shell mode? There are many actions and variable sets that onl
|
|||||||
__**Methods to start Bash as a login shell:**__
|
__**Methods to start Bash as a login shell:**__
|
||||||
* the first character of ''argv[0]'' is ''-'' (a hyphen): traditional UNIX(r) shells start from the ''login'' binary
|
* 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 ''-l'' option
|
||||||
* Bash is started with the ''<nowiki>--login</nowiki>'' option
|
* Bash is started with the ''<nowiki>--login</nowiki>'' option
|
||||||
|
|
||||||
__**Methods to test for login shell mode:**__
|
__**Methods to test for login shell mode:**__
|
||||||
* the shell option ''[[internals:shell_options#login_shell |login_shell]]'' is set
|
* the shell option ''[[internals:shell_options#login_shell |login_shell]]'' is set
|
||||||
|
|
||||||
__**Related switches:**__
|
__**Related switches:**__
|
||||||
* ''<nowiki>--noprofile</nowiki>'' disables reading of all profile files
|
* ''<nowiki>--noprofile</nowiki>'' disables reading of all profile files
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -43,8 +43,8 @@ __**Methods to test for interactive-shell mode:**__
|
|||||||
|
|
||||||
__**Related switches:**__
|
__**Related switches:**__
|
||||||
* ''-i'' forces the interactive mode
|
* ''-i'' forces the interactive mode
|
||||||
* ''<nowiki>--norc</nowiki>'' disables reading of the startup files (e.g. ''/etc/bash.bashrc'' if supported) and ''~/.bashrc''
|
* ''<nowiki>--norc</nowiki>'' disables reading of the startup files (e.g. ''/etc/bash.bashrc'' if supported) and ''~/.bashrc''
|
||||||
* ''<nowiki>--rcfile</nowiki>'' defines another startup file (instead of ''/etc/bash.bashrc'' and ''~/.bashrc'')
|
* ''<nowiki>--rcfile</nowiki>'' defines another startup file (instead of ''/etc/bash.bashrc'' and ''~/.bashrc'')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -70,7 +70,7 @@ When Bash is started in POSIX(r) mode, it follows the POSIX(r) standard for star
|
|||||||
No other startup files are read. Hence, a non-interactive shell doesn't read any startup files in POSIX(r) mode.
|
No other startup files are read. Hence, a non-interactive shell doesn't read any startup files in POSIX(r) mode.
|
||||||
|
|
||||||
__**Bash starts in POSIX(r) mode when:**__
|
__**Bash starts in POSIX(r) mode when:**__
|
||||||
* the commandline option ''<nowiki>--posix</nowiki>'' is specified
|
* the commandline option ''<nowiki>--posix</nowiki>'' is specified
|
||||||
* the environment variable [[syntax:shellvars#POSIXLY_CORRECT|POSIXLY_CORRECT]] is set
|
* the environment variable [[syntax:shellvars#POSIXLY_CORRECT|POSIXLY_CORRECT]] is set
|
||||||
|
|
||||||
|
|
||||||
@ -103,7 +103,7 @@ __**Bash starts in POSIX(r) mode when:**__
|
|||||||
==== POSIX run mode ====
|
==== POSIX run mode ====
|
||||||
|
|
||||||
In POSIX(r) mode, Bash follows the POSIX(r) standard regarding behaviour and parsing (excerpt from a Bash maintainer's document):
|
In POSIX(r) mode, Bash follows the POSIX(r) standard regarding behaviour and parsing (excerpt from a Bash maintainer's document):
|
||||||
<code>
|
<code>
|
||||||
Starting Bash with the `--posix' command-line option or executing `set
|
Starting Bash with the `--posix' command-line option or executing `set
|
||||||
-o posix' while Bash is running will cause Bash to conform more closely
|
-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
|
to the POSIX standard by changing the behavior to match that specified
|
||||||
@ -278,14 +278,14 @@ even when in POSIX mode. Specifically:
|
|||||||
|
|
||||||
Bash can be configured to be POSIX-conformant by default, by specifying
|
Bash can be configured to be POSIX-conformant by default, by specifying
|
||||||
the `--enable-strict-posix-default' to `configure' when building.
|
the `--enable-strict-posix-default' to `configure' when building.
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
|
|
||||||
FIXME help me to find out what breaks in POSIX(r) mode!
|
FIXME help me to find out what breaks in POSIX(r) mode!
|
||||||
|
|
||||||
__**The POSIX(r) mode can be switched on by:**__
|
__**The POSIX(r) mode can be switched on by:**__
|
||||||
* Bash starting as ''sh'' (the basename of ''argv[0]'' is ''sh'')
|
* Bash starting as ''sh'' (the basename of ''argv[0]'' is ''sh'')
|
||||||
* starting Bash with the commandline option ''<nowiki>--posix</nowiki>''
|
* starting Bash with the commandline option ''<nowiki>--posix</nowiki>''
|
||||||
* on startup, the environment variable [[syntax:shellvars#POSIXLY_CORRECT|POSIXLY_CORRECT]] is set
|
* on startup, the environment variable [[syntax:shellvars#POSIXLY_CORRECT|POSIXLY_CORRECT]] is set
|
||||||
* the command ''set -o posix''
|
* the command ''set -o posix''
|
||||||
|
|
||||||
@ -303,7 +303,7 @@ In restricted mode, Bash sets up (and runs) a shell environment that's far more
|
|||||||
* filenames containing a ''/'' (slash) can't be specified as argument to the ''-p'' option of the ''hash'' 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
|
* function definitions are not inherited from the environment at shell startup
|
||||||
* the environment variable [[syntax:shellvars#SHELLOPTS|SHELLOPTS]] is ignored at startup
|
* the environment variable [[syntax:shellvars#SHELLOPTS|SHELLOPTS]] is ignored at startup
|
||||||
* redirecting output using the ''>'', ''>|'', ''<>'', ''>&'', ''&>'', and ''<nowiki>>></nowiki>'' redirection operators isn't allowed
|
* redirecting output using the ''>'', ''>|'', ''<>'', ''>&'', ''&>'', and ''<nowiki>>></nowiki>'' redirection operators isn't allowed
|
||||||
* the ''exec'' builtin command can't replace the shell with another process
|
* 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
|
* 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
|
* using the ''enable'' builtin command to enable disabled shell builtins doesn't work
|
||||||
@ -318,7 +318,7 @@ When the command that is run is a shell script, then the restrictions are **turn
|
|||||||
__**The restricted shell can be switched on by:**__
|
__**The restricted shell can be switched on by:**__
|
||||||
* calling Bash as ''rbash'' (the basename of ''argv[0]'' is ''rbash'')
|
* calling Bash as ''rbash'' (the basename of ''argv[0]'' is ''rbash'')
|
||||||
* calling Bash with the ''-r'' option
|
* calling Bash with the ''-r'' option
|
||||||
* calling Bash with the ''<nowiki>--restricted</nowiki>'' option
|
* calling Bash with the ''<nowiki>--restricted</nowiki>'' option
|
||||||
|
|
||||||
__**Tests for restricted mode:**__
|
__**Tests for restricted mode:**__
|
||||||
* the special parameter ''$-'' contains the letter ''r'' (lowercase R)
|
* the special parameter ''$-'' contains the letter ''r'' (lowercase R)
|
||||||
|
@ -58,7 +58,7 @@ For this topic, see also
|
|||||||
==== General (all/many builtins) ====
|
==== General (all/many builtins) ====
|
||||||
^Feature or change description ^Appeared in Bash version ^See also/remarks ^
|
^Feature or change description ^Appeared in Bash version ^See also/remarks ^
|
||||||
|generally return 2 on usage error |2.0 | |
|
|generally return 2 on usage error |2.0 | |
|
||||||
|generally accept ''<nowiki>--</nowiki>'' (end of options) |2.0 | |
|
|generally accept ''<nowiki>--</nowiki>'' (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 |
|
|(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 ====
|
==== printf ====
|
||||||
@ -91,12 +91,12 @@ For this topic, see also
|
|||||||
* [[commands:classictest]]
|
* [[commands:classictest]]
|
||||||
|
|
||||||
^ Feature or change description ^ Appeared in Bash version ^ See also/remarks ^
|
^ Feature or change description ^ Appeared in Bash version ^ See also/remarks ^
|
||||||
| ''test'': ''-o'', ''=='', ''<'' and ''>'' | 2.0 | |
|
| ''test'': ''-o'', ''=='', ''<'' and ''>'' | 2.0 | |
|
||||||
| ''test'': ''-N'' | 2.02 | |
|
| ''test'': ''-N'' | 2.02 | |
|
||||||
| ''%%[[...]]%%'': new | 2.02-alpha1 | KSH93 |
|
| ''%%[[...]]%%'': new | 2.02-alpha1 | KSH93 |
|
||||||
| ''%%[[...]]%%'': regex support (''=~'') | 3.0-alpha | |
|
| ''%%[[...]]%%'': regex support (''=~'') | 3.0-alpha | |
|
||||||
| ''%%[[...]]%%'': quotable right-hand-side of ''=~'' forces string matching | 3.2-alpha | for consistency with pattern matching |
|
| ''%%[[...]]%%'': 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) |
|
| ''%%[[...]]%%'': ''<'' 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 | check if a variable is set |
|
||||||
| ''test''/''%%[%%''/''%%[[%%'': ''-v'' | 4.2-alpha | support array syntax to check for elements |
|
| ''test''/''%%[%%''/''%%[[%%'': ''-v'' | 4.2-alpha | support array syntax to check for elements |
|
||||||
| ''test''/''%%[%%''/''%%[[%%'': ''-N'' accepts nanoseconds | 5.1-alpha | |
|
| ''test''/''%%[%%''/''%%[[%%'': ''-N'' accepts nanoseconds | 5.1-alpha | |
|
||||||
@ -126,7 +126,7 @@ For this topic, see also
|
|||||||
| ''caller'' | new | 3.0 | [[commands:builtin:caller]] |
|
| ''caller'' | new | 3.0 | [[commands:builtin:caller]] |
|
||||||
| ''coproc'' | new | 4.0-alpha | |
|
| ''coproc'' | new | 4.0-alpha | |
|
||||||
| ''declare'' | new options ''-l'' and ''-u'' | 4.0-alpha | together with case-changing expansion forms |
|
| ''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) |
|
| ''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 | |
|
| ''read'' | changed ''-t'' (fractional seconds) | 4.0-alpha | |
|
||||||
| ''mapfile'' | new | 4.0-alpha | |
|
| ''mapfile'' | new | 4.0-alpha | |
|
||||||
| ''read'' | new option ''-i'' | 4.0-alpha | |
|
| ''read'' | new option ''-i'' | 4.0-alpha | |
|
||||||
@ -234,7 +234,7 @@ For this topic, see also
|
|||||||
| ''%%$"..."%%'' (localized strings) | 2.0 | |
|
| ''%%$"..."%%'' (localized strings) | 2.0 | |
|
||||||
| ''%%$'...'%%'' (ANSI-C-like strings) | 2.0 | |
|
| ''%%$'...'%%'' (ANSI-C-like strings) | 2.0 | |
|
||||||
| ''%%\xNNN%%'' in ''%%$'...'%%'' (and ''echo -e'') | 2.02-alpha1 | |
|
| ''%%\xNNN%%'' in ''%%$'...'%%'' (and ''echo -e'') | 2.02-alpha1 | |
|
||||||
| ''%%$(< FILENAME)%%'' (file content) | 2.02-alpha1 | |
|
| ''%%$(< FILENAME)%%'' (file content) | 2.02-alpha1 | |
|
||||||
| globbing (''fnmatch()'') capable of POSIX(r) character classes etc. | 2.02-alpha1 | |
|
| globbing (''fnmatch()'') capable of POSIX(r) character classes etc. | 2.02-alpha1 | |
|
||||||
| extended globbing | 2.02-alpha1 | KSH88 |
|
| extended globbing | 2.02-alpha1 | KSH88 |
|
||||||
| globbing inside array mass-assignment: ''ARRAY=(*.txt)'' | 2.03-alpha | |
|
| globbing inside array mass-assignment: ''ARRAY=(*.txt)'' | 2.03-alpha | |
|
||||||
@ -269,11 +269,11 @@ For this topic, see also
|
|||||||
* [[syntax:expansion:arith]]
|
* [[syntax:expansion:arith]]
|
||||||
|
|
||||||
^Feature or change description ^Appeared in Bash version ^Remarks ^
|
^Feature or change description ^Appeared in Bash version ^Remarks ^
|
||||||
|''<nowiki>((...))</nowiki>'' |2.0-beta2 |KSH93 |
|
|''<nowiki>((...))</nowiki>'' |2.0-beta2 |KSH93 |
|
||||||
|ternary operator |2.0 | |
|
|ternary operator |2.0 | |
|
||||||
|base 64 integer constants |2.0 |the max. base before is unknown. Anybody? |
|
|base 64 integer constants |2.0 |the max. base before is unknown. Anybody? |
|
||||||
|deprecated ''$[...]'' in favor of ''<nowiki>$((...))</nowiki>'' |2.0 | |
|
|deprecated ''$[...]'' in favor of ''<nowiki>$((...))</nowiki>'' |2.0 | |
|
||||||
|exponentiaition operator (''<nowiki>**</nowiki>'') |2.02-alpha1 | |
|
|exponentiaition operator (''<nowiki>**</nowiki>'') |2.02-alpha1 | |
|
||||||
|comma operator ''EXPR,EXPR'' |2.04-devel | |
|
|comma operator ''EXPR,EXPR'' |2.04-devel | |
|
||||||
|pre- and postfix operators |2.04-devel | |
|
|pre- and postfix operators |2.04-devel | |
|
||||||
|
|
||||||
@ -286,12 +286,12 @@ For this topic, see also
|
|||||||
| socket redirection (''/dev/tcp/'', ''/dev/udp/'') | 2.04-devel | |
|
| socket redirection (''/dev/tcp/'', ''/dev/udp/'') | 2.04-devel | |
|
||||||
| OS/filesystem-independent support for ''/dev/std(in|out|err)'' and ''/dev/fd/*'' | 2.04 | |
|
| OS/filesystem-independent support for ''/dev/std(in|out|err)'' and ''/dev/fd/*'' | 2.04 | |
|
||||||
| socket redirection accepts service names | 2.05 | |
|
| socket redirection accepts service names | 2.05 | |
|
||||||
| ''[n]<&word-'' and ''[n]>&word-'' FD-duplicate/closing | 2.05b-alpha1 | KSH93 |
|
| ''[n]<&word-'' and ''[n]>&word-'' FD-duplicate/closing | 2.05b-alpha1 | KSH93 |
|
||||||
| Here strings: ''%%<<< WORD%%'' | 2.05b-alpha1 | |
|
| Here strings: ''%%<<< WORD%%'' | 2.05b-alpha1 | |
|
||||||
| ''|&'' (synonym for ''2>&1 |'') | 4.0-alpha | |
|
| ''|&'' (synonym for ''2>&1 |'') | 4.0-alpha | |
|
||||||
| ''%%&>>%%'' (equiv. to ''%%>>FILE 2>&1%%'') | 4.0-alpha | |
|
| ''%%&>>%%'' (equiv. to ''%%>>FILE 2>&1%%'') | 4.0-alpha | |
|
||||||
| ''<nowiki>{varname}</nowiki>'' style automatic file descriptor allocation | 4.1-alpha | ksh93 |
|
| ''<nowiki>{varname}</nowiki>'' style automatic file descriptor allocation | 4.1-alpha | ksh93 |
|
||||||
| ''<nowiki>{varname[idx]}</nowiki>'' fd allocation accepts array subscripts and special-meaning variables | 4.3-alpha | ksh93 |
|
| ''<nowiki>{varname[idx]}</nowiki>'' fd allocation accepts array subscripts and special-meaning variables | 4.3-alpha | ksh93 |
|
||||||
|
|
||||||
|
|
||||||
===== Misc =====
|
===== Misc =====
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
====== The basics of shell scripting ======
|
====== The basics of shell scripting ======
|
||||||
|
|
||||||
{{keywords>bash shell scripting basics learning tutorial}}
|
{{keywords>bash shell scripting basics learning tutorial}}
|
||||||
|
|
||||||
===== Script files =====
|
===== 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:
|
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:
|
||||||
<code>
|
<code>
|
||||||
bash ./myfile
|
bash ./myfile
|
||||||
</code>
|
</code>
|
||||||
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.
|
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.
|
||||||
<code>
|
<code>
|
||||||
chmod +x ./myfile
|
chmod +x ./myfile
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
@ -22,38 +22,38 @@ If the file is executable, and you want to use it by calling only the script nam
|
|||||||
|
|
||||||
===== The Shebang =====
|
===== The Shebang =====
|
||||||
The in-file specification of the interpreter of that file, for example:
|
The in-file specification of the interpreter of that file, for example:
|
||||||
<code bash>
|
<code bash>
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
echo "Hello world..."
|
echo "Hello world..."
|
||||||
</code>
|
</code>
|
||||||
This is interpreted by the kernel ((under specific circumstances, also by the shell itself)) 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!
|
This is interpreted by the kernel ((under specific circumstances, also by the shell itself)) 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:
|
You can follow the process by using ''echo'' as a fake interpreter:
|
||||||
<code>
|
<code>
|
||||||
#!/bin/echo
|
#!/bin/echo
|
||||||
</code>
|
</code>
|
||||||
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.
|
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.
|
||||||
<code>
|
<code>
|
||||||
$ /home/bash/bin/test testword hello
|
$ /home/bash/bin/test testword hello
|
||||||
/home/bash/bin/test testword hello
|
/home/bash/bin/test testword hello
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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''"
|
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 [[http://www.in-ulm.de/~mascheck/various/shebang/|#!-magic]]).
|
If the interpreter can be specified with arguments and how long it can be is system-specific (see [[http://www.in-ulm.de/~mascheck/various/shebang/|#!-magic]]).
|
||||||
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.
|
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%>
|
<WRAP center round important 60%>
|
||||||
__**Attention:**__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.
|
__**Attention:**__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>
|
</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:
|
**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:
|
||||||
* [[scripting:bashbehaviour | Bash behaviour]].
|
* [[scripting:bashbehaviour | Bash behaviour]].
|
||||||
|
|
||||||
A common method is to specify a shebang like
|
A common method is to specify a shebang like
|
||||||
<code>
|
<code>
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
</code>
|
</code>
|
||||||
...which just moves the location of the potential problem to
|
...which just moves the location of the potential problem to
|
||||||
* the ''env'' utility must be located in /usr/bin/
|
* the ''env'' utility must be located in /usr/bin/
|
||||||
* the needed ''bash'' binary must be located in ''PATH''
|
* the needed ''bash'' binary must be located in ''PATH''
|
||||||
@ -105,40 +105,40 @@ The lower codes 0 to 125 are not reserved and may be used for whatever the progr
|
|||||||
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.
|
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:
|
An example of using the exit code of the program ''grep'' to check if a specific user is present in /etc/passwd:
|
||||||
<code bash>
|
<code bash>
|
||||||
if grep ^root /etc/passwd; then
|
if grep ^root /etc/passwd; then
|
||||||
echo "The user root was found"
|
echo "The user root was found"
|
||||||
else
|
else
|
||||||
echo "The user root was not found"
|
echo "The user root was not found"
|
||||||
fi
|
fi
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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!
|
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!
|
||||||
|
|
||||||
<code bash>
|
<code bash>
|
||||||
if [ "$mystring" = "Hello world" ]; then
|
if [ "$mystring" = "Hello world" ]; then
|
||||||
echo "Yeah dude, you entered the right words..."
|
echo "Yeah dude, you entered the right words..."
|
||||||
else
|
else
|
||||||
echo "Eeeek - go away..."
|
echo "Eeeek - go away..."
|
||||||
fi
|
fi
|
||||||
</code>
|
</code>
|
||||||
Read more about [[commands:classictest | the test command]]
|
Read more about [[commands:classictest | the test command]]
|
||||||
|
|
||||||
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:
|
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:
|
||||||
<code bash>
|
<code bash>
|
||||||
grep ^root: /etc/passwd >/dev/null || echo "root was not found - check the pub at the corner."
|
grep ^root: /etc/passwd >/dev/null || echo "root was not found - check the pub at the corner."
|
||||||
which vi && echo "Your favourite editor is installed."
|
which vi && echo "Your favourite editor is installed."
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Please, when your script exits on errors, provide a "FALSE" exit code, so others can check the script execution.
|
Please, when your script exits on errors, provide a "FALSE" exit code, so others can check the script execution.
|
||||||
|
|
||||||
===== Comments =====
|
===== 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:
|
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:
|
||||||
<code bash>
|
<code bash>
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# This is a small script to say something.
|
# This is a small script to say something.
|
||||||
echo "Be liberal in what you accept, and conservative in what you send" # say something
|
echo "Be liberal in what you accept, and conservative in what you send" # say something
|
||||||
</code>
|
</code>
|
||||||
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.
|
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.
|
||||||
|
|
||||||
|
|
||||||
@ -148,21 +148,21 @@ The first thing was already explained, it's the so-called shebang, for the shell
|
|||||||
|
|
||||||
==== Block commenting ====
|
==== 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:
|
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:
|
||||||
<code bash>
|
<code bash>
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# Write info mails, do some tasks and bring down the system in a safe way
|
# Write info mails, do some tasks and bring down the system in a safe way
|
||||||
echo "System halt requested" | mail -s "System halt" netadmin@example.com
|
echo "System halt requested" | mail -s "System halt" netadmin@example.com
|
||||||
logger -t SYSHALT "System halt requested"
|
logger -t SYSHALT "System halt requested"
|
||||||
|
|
||||||
##### The following "code block" is effectively ignored
|
##### The following "code block" is effectively ignored
|
||||||
: <<"SOMEWORD"
|
: <<"SOMEWORD"
|
||||||
/etc/init.d/mydatabase clean_stop
|
/etc/init.d/mydatabase clean_stop
|
||||||
mydatabase_dump /var/db/db1 /mnt/fsrv0/backups/db1
|
mydatabase_dump /var/db/db1 /mnt/fsrv0/backups/db1
|
||||||
logger -t SYSHALT "System halt: pre-shutdown actions done, now shutting down the system"
|
logger -t SYSHALT "System halt: pre-shutdown actions done, now shutting down the system"
|
||||||
shutdown -h NOW
|
shutdown -h NOW
|
||||||
SOMEWORD
|
SOMEWORD
|
||||||
##### The ignored codeblock ends here
|
##### The ignored codeblock ends here
|
||||||
</code>
|
</code>
|
||||||
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.
|
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 [[syntax:redirection#tag_heredoc | redirection with here-documents]] for more
|
The here-document-tag was quoted here **to avoid substitutions** in the "commented" text! Check [[syntax:redirection#tag_heredoc | redirection with here-documents]] for more
|
||||||
@ -174,19 +174,19 @@ The here-document-tag was quoted here **to avoid substitutions** in the "co
|
|||||||
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.
|
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:
|
Compare the following //equivalent// code snippets:
|
||||||
<code bash>
|
<code bash>
|
||||||
myvariable=test
|
myvariable=test
|
||||||
echo $myvariable
|
echo $myvariable
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
<code bash>
|
<code bash>
|
||||||
myfunction() {
|
myfunction() {
|
||||||
myvariable=test
|
myvariable=test
|
||||||
}
|
}
|
||||||
|
|
||||||
myfunction
|
myfunction
|
||||||
echo $myvariable
|
echo $myvariable
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
In both cases, the variable ''myvariable'' is set and accessible from everywhere in that script, both in functions and in the "main program".
|
In both cases, the variable ''myvariable'' is set and accessible from everywhere in that script, both in functions and in the "main program".
|
||||||
|
|
||||||
@ -197,7 +197,7 @@ In both cases, the variable ''myvariable'' is set and accessible from everywhere
|
|||||||
Bash provides ways to make a variable's scope //local// to a function:
|
Bash provides ways to make a variable's scope //local// to a function:
|
||||||
* Using the ''local'' keyword, or
|
* Using the ''local'' keyword, or
|
||||||
* Using ''declare'' (which will //detect// when it was called from within a function and make the variable(s) local).
|
* Using ''declare'' (which will //detect// when it was called from within a function and make the variable(s) local).
|
||||||
<code bash>
|
<code bash>
|
||||||
myfunc() {
|
myfunc() {
|
||||||
local var=VALUE
|
local var=VALUE
|
||||||
|
|
||||||
@ -206,11 +206,11 @@ declare var=VALUE
|
|||||||
|
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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:
|
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:
|
||||||
|
|
||||||
<code bash>
|
<code bash>
|
||||||
foo=external
|
foo=external
|
||||||
|
|
||||||
printvalue() {
|
printvalue() {
|
||||||
@ -228,7 +228,7 @@ printvalue
|
|||||||
|
|
||||||
# this will print - again - "external"
|
# this will print - again - "external"
|
||||||
echo $foo
|
echo $foo
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== Environment variables ====
|
==== Environment variables ====
|
||||||
|
|
||||||
@ -237,13 +237,13 @@ The environment space is not directly related to the topic about scope, but it's
|
|||||||
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.**
|
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:
|
A variable can be tagged to be part of the environment using the ''export'' command:
|
||||||
<code bash>
|
<code bash>
|
||||||
# create a new variable and set it:
|
# create a new variable and set it:
|
||||||
# -> This is a normal shell variable, not an environment variable!
|
# -> This is a normal shell variable, not an environment variable!
|
||||||
myvariable="Hello world."
|
myvariable="Hello world."
|
||||||
|
|
||||||
# make the variable visible to all child processes:
|
# make the variable visible to all child processes:
|
||||||
# -> Make it an environment variable: "export" it
|
# -> Make it an environment variable: "export" it
|
||||||
export myvariable
|
export myvariable
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
====== Debugging a script ======
|
====== Debugging a script ======
|
||||||
|
|
||||||
{{keywords>bash shell scripting bug debug debugging}}
|
{{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.
|
These few lines are not intended as a full-fledged debugging tutorial, but as hints and comments about debugging a Bash script.
|
||||||
|
|
||||||
@ -40,23 +40,23 @@ An available syslog interface is ''logger'' ([[http://unixhelp.ed.ac.uk/CGI/man-
|
|||||||
===== Inject debugging code =====
|
===== Inject debugging code =====
|
||||||
|
|
||||||
Insert **echos** everywhere you can, and print to ''stderr'':
|
Insert **echos** everywhere you can, and print to ''stderr'':
|
||||||
<code>
|
<code>
|
||||||
echo "DEBUG: current i=$i" >&2
|
echo "DEBUG: current i=$i" >&2
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
If you read input from **anywhere**, such as a file or [[syntax:expansion:cmdsubst | command substitution]], print the debug output with literal quotes, to see leading and trailing spaces!
|
If you read input from **anywhere**, such as a file or [[syntax:expansion:cmdsubst | command substitution]], print the debug output with literal quotes, to see leading and trailing spaces!
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
pid=$(< fooservice.pid)
|
pid=$(< fooservice.pid)
|
||||||
echo "DEBUG: read from file: pid=\"$pid\"" >&2
|
echo "DEBUG: read from file: pid=\"$pid\"" >&2
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Bash's [[commands:builtin:printf | printf]] command has the ''%q'' format, which is handy for verifying whether strings are what they appear to be.
|
Bash's [[commands:builtin:printf | printf]] command has the ''%q'' format, which is handy for verifying whether strings are what they appear to be.
|
||||||
<code>
|
<code>
|
||||||
foo=$(< inputfile)
|
foo=$(< inputfile)
|
||||||
printf "DEBUG: foo is |%q|\n" "$foo" >&2
|
printf "DEBUG: foo is |%q|\n" "$foo" >&2
|
||||||
# exposes whitespace (such as CRs, see below) and non-printing characters
|
# exposes whitespace (such as CRs, see below) and non-printing characters
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Use shell debug output =====
|
===== Use shell debug output =====
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ There are two useful debug outputs for that task (both are written to ''stderr''
|
|||||||
* ''set -x'' mode (''set -o xtrace'')
|
* ''set -x'' mode (''set -o xtrace'')
|
||||||
* print everything as if it were executed, after [[syntax:expansion:intro | substitution and expansion]] is applied
|
* print everything as if it were executed, after [[syntax:expansion:intro | substitution and expansion]] is applied
|
||||||
* indicate the depth-level of the subshell (by default by prefixing a ''+'' (plus) sign to the displayed command)
|
* indicate the depth-level of the subshell (by default by prefixing a ''+'' (plus) sign to the displayed command)
|
||||||
* indicate the recognized words after [[syntax:expansion:wordsplit | word splitting]] by marking them like ''<nowiki>'x y'</nowiki>''
|
* indicate the recognized words after [[syntax:expansion:wordsplit | word splitting]] by marking them like ''<nowiki>'x y'</nowiki>''
|
||||||
* in shell version 4.1, this debug output can be printed to a configurable file descriptor, rather than sdtout by setting the [[syntax:shellvars#BASH_XTRACEFD|BASH_XTRACEFD]] variable.
|
* in shell version 4.1, this debug output can be printed to a configurable file descriptor, rather than sdtout by setting the [[syntax:shellvars#BASH_XTRACEFD|BASH_XTRACEFD]] variable.
|
||||||
|
|
||||||
**__Hint:__** These modes can be entered when calling Bash:
|
**__Hint:__** These modes can be entered when calling Bash:
|
||||||
@ -79,30 +79,30 @@ There are two useful debug outputs for that task (both are written to ''stderr''
|
|||||||
==== Simple example of how to interpret xtrace output ====
|
==== Simple example of how to interpret xtrace output ====
|
||||||
|
|
||||||
Here's a simple command (a string comparison using the [[commands:classictest | classic test command]]) executed while in ''set -x'' mode:
|
Here's a simple command (a string comparison using the [[commands:classictest | classic test command]]) executed while in ''set -x'' mode:
|
||||||
<code>
|
<code>
|
||||||
set -x
|
set -x
|
||||||
foo="bar baz"
|
foo="bar baz"
|
||||||
[ $foo = test ]
|
[ $foo = test ]
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
That fails. Why? Let's see the ''xtrace'' output:
|
That fails. Why? Let's see the ''xtrace'' output:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
+ '[' bar baz = test ']'
|
+ '[' bar baz = test ']'
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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...
|
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...
|
||||||
<code>
|
<code>
|
||||||
# next try
|
# next try
|
||||||
[ "$foo" = test ]
|
[ "$foo" = test ]
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
''xtrace'' now gives
|
''xtrace'' now gives
|
||||||
<code>
|
<code>
|
||||||
+ '[' 'bar baz' = test ']'
|
+ '[' 'bar baz' = test ']'
|
||||||
^ ^
|
^ ^
|
||||||
word markers!
|
word markers!
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -113,19 +113,19 @@ And now you see that it's ("bar" and "baz") recognized as tw
|
|||||||
(by AnMaster)
|
(by AnMaster)
|
||||||
|
|
||||||
''xtrace'' output would be more useful if it contained source file and line number. Add this assignment [[syntax:shellvars#PS4|PS4]] at the beginning of your script to enable the inclusion of that information:
|
''xtrace'' output would be more useful if it contained source file and line number. Add this assignment [[syntax:shellvars#PS4|PS4]] at the beginning of your script to enable the inclusion of that information:
|
||||||
<code>
|
<code>
|
||||||
export PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'
|
export PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'
|
||||||
</code>
|
</code>
|
||||||
**Be sure to use single quotes here!**
|
**Be sure to use single quotes here!**
|
||||||
|
|
||||||
The output would look like this when you trace code //outside a function//:
|
The output would look like this when you trace code //outside a function//:
|
||||||
<code>
|
<code>
|
||||||
+(somefile.bash:412): echo 'Hello world'
|
+(somefile.bash:412): echo 'Hello world'
|
||||||
</code>
|
</code>
|
||||||
...and like this when you trace code //inside a function//:
|
...and like this when you trace code //inside a function//:
|
||||||
<code>
|
<code>
|
||||||
+(somefile.bash:412): myfunc(): echo 'Hello world'
|
+(somefile.bash:412): myfunc(): echo 'Hello world'
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
That helps a lot when the script is long, or when the main script sources many other files.
|
That helps a lot when the script is long, or when the main script sources many other files.
|
||||||
|
|
||||||
@ -134,57 +134,57 @@ If you test variables that flag the state of options, such as with ''%%if [[ -n
|
|||||||
===== Debugging commands depending on a set variable =====
|
===== Debugging commands depending on a set variable =====
|
||||||
|
|
||||||
For general debugging purposes you can also define a function and a variable to use:
|
For general debugging purposes you can also define a function and a variable to use:
|
||||||
<code>
|
<code>
|
||||||
debugme() {
|
debugme() {
|
||||||
[[ $script_debug = 1 ]] && "$@" || :
|
[[ $script_debug = 1 ]] && "$@" || :
|
||||||
# be sure to append || : or || true here or use return 0, since the return code
|
# 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
|
# of this function should always be 0 to not influence anything else with an unwanted
|
||||||
# "false" return code (for example the script's exit code if this function is used
|
# "false" return code (for example the script's exit code if this function is used
|
||||||
# as the very last command in the script)
|
# as the very last command in the script)
|
||||||
}
|
}
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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:
|
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:
|
||||||
<code>
|
<code>
|
||||||
script_debug=1
|
script_debug=1
|
||||||
# to turn it off, set script_debug=0
|
# to turn it off, set script_debug=0
|
||||||
|
|
||||||
debugme logger "Sorting the database"
|
debugme logger "Sorting the database"
|
||||||
database_sort
|
database_sort
|
||||||
debugme logger "Finished sorting the database, exit code $?"
|
debugme logger "Finished sorting the database, exit code $?"
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Of course this can be used to execute something other than echo during debugging:
|
Of course this can be used to execute something other than echo during debugging:
|
||||||
<code>
|
<code>
|
||||||
debugme set -x
|
debugme set -x
|
||||||
# ... some code ...
|
# ... some code ...
|
||||||
debugme set +x
|
debugme set +x
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Dry-run STDIN driven commands =====
|
===== Dry-run STDIN driven commands =====
|
||||||
|
|
||||||
Imagine you have a script that runs FTP commands using the standard FTP client:
|
Imagine you have a script that runs FTP commands using the standard FTP client:
|
||||||
|
|
||||||
<code bash>
|
<code bash>
|
||||||
ftp user@host <<FTP
|
ftp user@host <<FTP
|
||||||
cd /data
|
cd /data
|
||||||
get current.log
|
get current.log
|
||||||
dele current.log
|
dele current.log
|
||||||
FTP
|
FTP
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
A method to dry-run this with debug output is:
|
A method to dry-run this with debug output is:
|
||||||
<code bash>
|
<code bash>
|
||||||
if [[ $DRY_RUN = yes ]]; then
|
if [[ $DRY_RUN = yes ]]; then
|
||||||
sed 's/^/DRY_RUN FTP: /'
|
sed 's/^/DRY_RUN FTP: /'
|
||||||
else
|
else
|
||||||
ftp user@host
|
ftp user@host
|
||||||
fi <<FTP
|
fi <<FTP
|
||||||
cd /data
|
cd /data
|
||||||
get current.log
|
get current.log
|
||||||
dele current.log
|
dele current.log
|
||||||
FTP
|
FTP
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
This can be wrapped in a shell function for more readable code.
|
This can be wrapped in a shell function for more readable code.
|
||||||
|
|
||||||
@ -193,9 +193,9 @@ This can be wrapped in a shell function for more readable code.
|
|||||||
|
|
||||||
|
|
||||||
==== Unexpected end of file ====
|
==== Unexpected end of file ====
|
||||||
<code>
|
<code>
|
||||||
script.sh: line 100: syntax error: unexpected end of file
|
script.sh: line 100: syntax error: unexpected end of file
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Usually indicates exactly what it says: An unexpected end of file. It's unexpected because Bash waits for the closing of a [[syntax:ccmd:intro | compound command]]:
|
Usually indicates exactly what it says: An unexpected end of file. It's unexpected because Bash waits for the closing of a [[syntax:ccmd:intro | compound command]]:
|
||||||
* did you close your ''do'' with a ''done''?
|
* did you close your ''do'' with a ''done''?
|
||||||
@ -205,7 +205,7 @@ Usually indicates exactly what it says: An unexpected end of file. It's unexpect
|
|||||||
* did you close your ''('' with a '')''?
|
* did you close your ''('' with a '')''?
|
||||||
|
|
||||||
|
|
||||||
**__Note:__** 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 [[syntax:redirection | 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>')''
|
**__Note:__** 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 [[syntax:redirection | 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>')''
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -213,51 +213,51 @@ Usually indicates exactly what it says: An unexpected end of file. It's unexpect
|
|||||||
|
|
||||||
==== Unexpected end of file while looking for matching ... ====
|
==== Unexpected end of file while looking for matching ... ====
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
script.sh: line 50: unexpected EOF while looking for matching `"'
|
script.sh: line 50: unexpected EOF while looking for matching `"'
|
||||||
script.sh: line 100: syntax error: unexpected end of file
|
script.sh: line 100: syntax error: unexpected end of file
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
This one indicates the double-quote opened in line 50 does not have a matching closing quote.
|
This one indicates the double-quote opened in line 50 does not have a matching closing quote.
|
||||||
|
|
||||||
These //unmatched errors// occur with:
|
These //unmatched errors// occur with:
|
||||||
* double-quote pairs
|
* double-quote pairs
|
||||||
* single-quote pairs (also ''<nowiki>$'string'</nowiki>''!)
|
* single-quote pairs (also ''<nowiki>$'string'</nowiki>''!)
|
||||||
* missing a closing ''}'' with [[syntax:pe | parameter expansion syntax]]
|
* missing a closing ''}'' with [[syntax:pe | parameter expansion syntax]]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
==== Too many arguments ====
|
==== Too many arguments ====
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
bash: test: too many arguments
|
bash: test: too many arguments
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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.
|
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 ====
|
==== !": event not found ====
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
$ echo "Hello world!"
|
$ echo "Hello world!"
|
||||||
bash: !": event not found
|
bash: !": event not found
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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:
|
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:
|
||||||
<code>
|
<code>
|
||||||
set +H
|
set +H
|
||||||
# or
|
# or
|
||||||
set +o histexpand
|
set +o histexpand
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== syntax error near unexpected token `(' ====
|
==== syntax error near unexpected token `(' ====
|
||||||
|
|
||||||
When this happens during a script **function definition** or on the commandline, e.g.
|
When this happens during a script **function definition** or on the commandline, e.g.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
$ foo () { echo "Hello world"; }
|
$ foo () { echo "Hello world"; }
|
||||||
bash: syntax error near unexpected token `('
|
bash: syntax error near unexpected token `('
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
@ -283,12 +283,12 @@ Some possible sources of CRs:
|
|||||||
==== Why do CRs hurt? ====
|
==== 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!):
|
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!):
|
||||||
<code>
|
<code>
|
||||||
#!/bin/bash^M
|
#!/bin/bash^M
|
||||||
^M
|
^M
|
||||||
echo "Hello world"^M
|
echo "Hello world"^M
|
||||||
...
|
...
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Here's what happens because of the ''#!/bin/bash^M'' in our shebang:
|
Here's what happens because of the ''#!/bin/bash^M'' in our shebang:
|
||||||
* the file ''/bin/bash^M'' doesn't exist (hopefully)
|
* the file ''/bin/bash^M'' doesn't exist (hopefully)
|
||||||
@ -296,18 +296,18 @@ Here's what happens because of the ''#!/bin/bash^M'' in our shebang:
|
|||||||
* the script can't be executed
|
* the script can't be executed
|
||||||
|
|
||||||
The error message can vary. If you're lucky, you'll get:
|
The error message can vary. If you're lucky, you'll get:
|
||||||
<code>
|
<code>
|
||||||
bash: ./testing.sh: /bin/bash^M: bad interpreter: No such file or directory
|
bash: ./testing.sh: /bin/bash^M: bad interpreter: No such file or directory
|
||||||
</code>
|
</code>
|
||||||
which alerts you to the CR. But you may also get the following:
|
which alerts you to the CR. But you may also get the following:
|
||||||
<code>
|
<code>
|
||||||
: bad interpreter: No such file or directory
|
: bad interpreter: No such file or directory
|
||||||
</code>
|
</code>
|
||||||
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!
|
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>
|
<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!
|
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>
|
</note>
|
||||||
|
|
||||||
==== How can I find and eliminate them? ====
|
==== How can I find and eliminate them? ====
|
||||||
|
|
||||||
@ -316,7 +316,7 @@ It's easy to imagine the ''^M'' is bad in other places too. If you get weird and
|
|||||||
* with ''cat(1)'': ''cat -v FILE''
|
* with ''cat(1)'': ''cat -v FILE''
|
||||||
|
|
||||||
**To eliminate** them (only a few examples)
|
**To eliminate** them (only a few examples)
|
||||||
* blindly with ''tr(1)'': ''tr -d <nowiki>'\r'</nowiki> <FILE >FILE.new''
|
* blindly with ''tr(1)'': ''tr -d <nowiki>'\r'</nowiki> <FILE >FILE.new''
|
||||||
* controlled with ''recode(1)'': ''recode MSDOS..latin1 FILE''
|
* controlled with ''recode(1)'': ''recode MSDOS..latin1 FILE''
|
||||||
* controlled with ''dos2unix(1)'': ''dos2unix FILE''
|
* controlled with ''dos2unix(1)'': ''dos2unix FILE''
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
====== Beginner Mistakes ======
|
====== Beginner Mistakes ======
|
||||||
|
|
||||||
{{keywords>bash shell scripting pitfalls traps beginners}}
|
{{keywords>bash shell scripting pitfalls traps beginners}}
|
||||||
|
|
||||||
Here are some typical traps:
|
Here are some typical traps:
|
||||||
|
|
||||||
@ -28,16 +28,16 @@ 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!
|
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:
|
Workaround: You can call it using the pathname:
|
||||||
<code>
|
<code>
|
||||||
/home/user/bin/test
|
/home/user/bin/test
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Globbing =====
|
===== Globbing =====
|
||||||
|
|
||||||
==== Brace expansion is not globbing ====
|
==== Brace expansion is not globbing ====
|
||||||
|
|
||||||
The following command line is not related to globbing (filename expansion):
|
The following command line is not related to globbing (filename expansion):
|
||||||
<code>
|
<code>
|
||||||
# YOU EXPECT
|
# YOU EXPECT
|
||||||
# -i1.vob -i2.vob -i3.vob ....
|
# -i1.vob -i2.vob -i3.vob ....
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ echo -i{*.vob,}
|
|||||||
|
|
||||||
# YOU GET
|
# YOU GET
|
||||||
# -i*.vob -i
|
# -i*.vob -i
|
||||||
</code>
|
</code>
|
||||||
**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.
|
**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:
|
Please see:
|
||||||
@ -67,15 +67,15 @@ Please see:
|
|||||||
=== The Dollar-Sign ===
|
=== The Dollar-Sign ===
|
||||||
|
|
||||||
There is no ''$'' (dollar-sign) when you reference the **name** of a variable! Bash is not PHP!
|
There is no ''$'' (dollar-sign) when you reference the **name** of a variable! Bash is not PHP!
|
||||||
<code>
|
<code>
|
||||||
# THIS IS WRONG!
|
# THIS IS WRONG!
|
||||||
$myvar="Hello world!"
|
$myvar="Hello world!"
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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...
|
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...
|
||||||
<code>
|
<code>
|
||||||
="Hello world!"
|
="Hello world!"
|
||||||
</code>
|
</code>
|
||||||
...which **definitely is wrong**!
|
...which **definitely is wrong**!
|
||||||
|
|
||||||
When you need the **name** of a variable, you write **only the name**, for example
|
When you need the **name** of a variable, you write **only the name**, for example
|
||||||
@ -90,7 +90,7 @@ When you need the **content** of a variable, you prefix its name with **a dollar
|
|||||||
|
|
||||||
Putting spaces on either or both sides of the equal-sign (''='') when assigning a value to a variable **will** fail.
|
Putting spaces on either or both sides of the equal-sign (''='') when assigning a value to a variable **will** fail.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
# INCORRECT 1
|
# INCORRECT 1
|
||||||
example = Hello
|
example = Hello
|
||||||
|
|
||||||
@ -99,16 +99,16 @@ example= Hello
|
|||||||
|
|
||||||
# INCORRECT 3
|
# INCORRECT 3
|
||||||
example =Hello
|
example =Hello
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
The only valid form is **no spaces between the variable name and assigned value**:
|
The only valid form is **no spaces between the variable name and assigned value**:
|
||||||
<code>
|
<code>
|
||||||
# CORRECT 1
|
# CORRECT 1
|
||||||
example=Hello
|
example=Hello
|
||||||
|
|
||||||
# CORRECT 2
|
# CORRECT 2
|
||||||
example=" Hello"
|
example=" Hello"
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== Expanding (using) variables ====
|
==== Expanding (using) variables ====
|
||||||
|
|
||||||
@ -117,33 +117,33 @@ 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.
|
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:
|
Let's define an example variable containing text with spaces:
|
||||||
<code>
|
<code>
|
||||||
example="Hello world"
|
example="Hello world"
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
^Used form^result^number of words^
|
^Used form^result^number of words^
|
||||||
|''$example'' |''Hello world''|2|
|
|''$example'' |''Hello world''|2|
|
||||||
|''"$example"'' |''Hello world''|1|
|
|''"$example"'' |''Hello world''|1|
|
||||||
|''\$example'' |''$example''|1|
|
|''\$example'' |''$example''|1|
|
||||||
|''<nowiki>'$example'</nowiki>'' |''$example''|1|
|
|''<nowiki>'$example'</nowiki>'' |''$example''|1|
|
||||||
|
|
||||||
If you use parameter expansion, you **must** use the **name** (''PATH'') of the referenced variables/parameters. i.e. **not** (''$PATH''):
|
If you use parameter expansion, you **must** use the **name** (''PATH'') of the referenced variables/parameters. i.e. **not** (''$PATH''):
|
||||||
<code>
|
<code>
|
||||||
# WRONG!
|
# WRONG!
|
||||||
echo "The first character of PATH is ${$PATH:0:1}"
|
echo "The first character of PATH is ${$PATH:0:1}"
|
||||||
|
|
||||||
# CORRECT
|
# CORRECT
|
||||||
echo "The first character of PATH is ${PATH:0:1}"
|
echo "The first character of PATH is ${PATH:0:1}"
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Note that if you are using variables in [[syntax:arith_expr | arithmetic expressions]], then the bare **name** is allowed:
|
Note that if you are using variables in [[syntax:arith_expr | arithmetic expressions]], then the bare **name** is allowed:
|
||||||
<code>
|
<code>
|
||||||
((a=$a+7)) # Add 7 to a
|
((a=$a+7)) # Add 7 to a
|
||||||
((a = a + 7)) # Add 7 to a. Identical to the previous command.
|
((a = a + 7)) # Add 7 to a. Identical to the previous command.
|
||||||
((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.
|
a=$((a+7)) # POSIX-compatible version of previous code.
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
|
|
||||||
Please see:
|
Please see:
|
||||||
@ -156,22 +156,22 @@ Please see:
|
|||||||
|
|
||||||
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''):
|
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''):
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
$ cat script.sh
|
$ cat script.sh
|
||||||
export hello=world
|
export hello=world
|
||||||
|
|
||||||
$ ./script.sh
|
$ ./script.sh
|
||||||
$ echo $hello
|
$ echo $hello
|
||||||
$
|
$
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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:
|
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:
|
||||||
<code>
|
<code>
|
||||||
$ source ./script.sh
|
$ source ./script.sh
|
||||||
$ echo $hello
|
$ echo $hello
|
||||||
world
|
world
|
||||||
$
|
$
|
||||||
</code>
|
</code>
|
||||||
In this case, the export command is of no use.
|
In this case, the export command is of no use.
|
||||||
|
|
||||||
Please see:
|
Please see:
|
||||||
@ -182,25 +182,25 @@ Please see:
|
|||||||
|
|
||||||
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:
|
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:
|
||||||
|
|
||||||
<code bash>
|
<code bash>
|
||||||
grep ^root: /etc/passwd >/dev/null 2>&1
|
grep ^root: /etc/passwd >/dev/null 2>&1
|
||||||
|
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
echo "root was not found - check the pub at the corner"
|
echo "root was not found - check the pub at the corner"
|
||||||
fi
|
fi
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
This can be simplified to:
|
This can be simplified to:
|
||||||
<code bash>
|
<code bash>
|
||||||
if ! grep ^root: /etc/passwd >/dev/null 2>&1; then
|
if ! grep ^root: /etc/passwd >/dev/null 2>&1; then
|
||||||
echo "root was not found - check the pub at the corner"
|
echo "root was not found - check the pub at the corner"
|
||||||
fi
|
fi
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Or, simpler yet:
|
Or, simpler yet:
|
||||||
<code bash>
|
<code bash>
|
||||||
grep ^root: /etc/passwd >/dev/null 2>&1 || echo "root was not found - check the pub at the corner"
|
grep ^root: /etc/passwd >/dev/null 2>&1 || echo "root was not found - check the pub at the corner"
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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 ''$?''.
|
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 ''$?''.
|
||||||
|
|
||||||
@ -212,13 +212,13 @@ See also:
|
|||||||
It's important to remember the different ways to run a child command, and whether you want the output, the return value, or neither.
|
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:
|
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:
|
||||||
<code>
|
<code>
|
||||||
$(ls -l /tmp)
|
$(ls -l /tmp)
|
||||||
newvariable=$(printf "foo")
|
newvariable=$(printf "foo")
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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:
|
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:
|
||||||
<code>
|
<code>
|
||||||
if grep someuser /etc/passwd ; then
|
if grep someuser /etc/passwd ; then
|
||||||
# do something
|
# do something
|
||||||
fi
|
fi
|
||||||
@ -226,15 +226,15 @@ fi
|
|||||||
if ( w | grep someuser | grep sqlplus ) ; then
|
if ( w | grep someuser | grep sqlplus ) ; then
|
||||||
# someuser is logged in and running sqlplus
|
# someuser is logged in and running sqlplus
|
||||||
fi
|
fi
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Make sure you're using the form you intended:
|
Make sure you're using the form you intended:
|
||||||
<code>
|
<code>
|
||||||
# WRONG!
|
# WRONG!
|
||||||
if $(grep ERROR /var/log/messages) ; then
|
if $(grep ERROR /var/log/messages) ; then
|
||||||
# send alerts
|
# send alerts
|
||||||
fi
|
fi
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Please see:
|
Please see:
|
||||||
* [[syntax:ccmd:intro]]
|
* [[syntax:ccmd:intro]]
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
====== Portability talk ======
|
====== Portability talk ======
|
||||||
|
|
||||||
{{keywords>bash shell scripting portability POSIX portable}}
|
{{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.
|
The script programming language of BASH is based on the Bourne Shell syntax, with some extensions and derivations.
|
||||||
|
|
||||||
@ -11,12 +11,12 @@ Some syntax elements have a BASH-specific, and a portable(("portable"
|
|||||||
^ construct ^ portable equivalent ^ Description ^ Portability ^
|
^ construct ^ portable equivalent ^ Description ^ Portability ^
|
||||||
|''source\ FILE''|''. FILE''|include a script file|Bourne shell (bash, ksh, POSIX(r), zsh, ...)|
|
|''source\ FILE''|''. FILE''|include a script file|Bourne shell (bash, ksh, POSIX(r), zsh, ...)|
|
||||||
|''declare''\ keyword|''typeset'' keyword|define local variables (or variables with special attributes)|ksh, zsh, ..., **not POSIX!**|
|
|''declare''\ keyword|''typeset'' keyword|define local variables (or variables with special attributes)|ksh, zsh, ..., **not POSIX!**|
|
||||||
|''command\ <nowiki><<<</nowiki>\ WORD''|''command <nowiki><<</nowiki>MARKER\\ WORD\\ MARKER''|a here-string, a special form of the here-document, avoid it in portable scripts!|POSIX(r)|
|
|''command\ <nowiki><<<</nowiki>\ WORD''|''command <nowiki><<</nowiki>MARKER\\ WORD\\ MARKER''|a here-string, a special form of the here-document, avoid it in portable scripts!|POSIX(r)|
|
||||||
|''export VAR=VALUE''|''VAR=VALUE\\ export VAR''|Though POSIX(r) allows it, some shells don't want the assignment and the exporting in one command|POSIX(r), zsh, ksh, ...|
|
|''export VAR=VALUE''|''VAR=VALUE\\ export VAR''|Though POSIX(r) allows it, some shells don't want the assignment and the exporting in one command|POSIX(r), zsh, ksh, ...|
|
||||||
|''<nowiki>(( MATH ))</nowiki>''|''<nowiki>: $(( MATH ))</nowiki>''|POSIX(r) does't define an arithmetic compund command, many shells don't know it. Using the pseudo-command '':'' and the arithmetic expansion ''<nowiki>$(( ))</nowiki>'' is a kind of workaround here. **Attention:** Not all shell support assignment like ''<nowiki>$(( a = 1 + 1 ))</nowiki>''! Also see below for a probably more portable solution.|all POSIX(r) compatible shells|
|
|''<nowiki>(( MATH ))</nowiki>''|''<nowiki>: $(( MATH ))</nowiki>''|POSIX(r) does't define an arithmetic compund command, many shells don't know it. Using the pseudo-command '':'' and the arithmetic expansion ''<nowiki>$(( ))</nowiki>'' is a kind of workaround here. **Attention:** Not all shell support assignment like ''<nowiki>$(( a = 1 + 1 ))</nowiki>''! Also see below for a probably more portable solution.|all POSIX(r) compatible shells|
|
||||||
|''<nowiki>[[</nowiki>\ EXPRESSION\ <nowiki>]]</nowiki>''|''[ EXPRESSION ]''\\ or \\ ''test EXPRESSION''|The Bashish test keyword is reserved by POSIX(r), but not defined. Use the old fashioned way with the ''test'' command. See [[commands:classictest | the classic test command]]|POSIX(r) and others|
|
|''<nowiki>[[</nowiki>\ EXPRESSION\ <nowiki>]]</nowiki>''|''[ EXPRESSION ]''\\ or \\ ''test EXPRESSION''|The Bashish test keyword is reserved by POSIX(r), but not defined. Use the old fashioned way with the ''test'' command. See [[commands:classictest | the classic test command]]|POSIX(r) and others|
|
||||||
|''COMMAND\ <\ <(\ ...INPUTCOMMANDS...\ )''|''INPUTCOMMANDS\ >\ TEMPFILE\\ COMMAND\ <\ TEMPFILE''|Process substitution (here used with redirection); use the old fashioned way (tempfiles)|POSIX(r) and others|
|
|''COMMAND\ <\ <(\ ...INPUTCOMMANDS...\ )''|''INPUTCOMMANDS\ >\ TEMPFILE\\ COMMAND\ <\ TEMPFILE''|Process substitution (here used with redirection); use the old fashioned way (tempfiles)|POSIX(r) and others|
|
||||||
|''<nowiki>((echo X);(echo Y))</nowiki>'' |''( (echo X); (echo Y) )'' | Nested subshells (separate the inner ''()'' from the outer ''()'' by spaces, to not confuse the shell regarding arithmetic control operators) |POSIX(r) and others |
|
|''<nowiki>((echo X);(echo Y))</nowiki>'' |''( (echo X); (echo Y) )'' | Nested subshells (separate the inner ''()'' from the outer ''()'' by spaces, to not confuse the shell regarding arithmetic control operators) |POSIX(r) and others |
|
||||||
|
|
||||||
===== Portability rationale =====
|
===== Portability rationale =====
|
||||||
|
|
||||||
@ -36,8 +36,8 @@ Since an extra ''export'' doesn't hurt, the safest and most portable way is to a
|
|||||||
|
|
||||||
==== Arithmetics ====
|
==== 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 ''<nowiki>: $((MATH))</nowiki>'' construct mentioned as possible alternative. Regarding the exit code, a 100% equivalent construct would be:
|
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 ''<nowiki>: $((MATH))</nowiki>'' construct mentioned as possible alternative. Regarding the exit code, a 100% equivalent construct would be:
|
||||||
<code>
|
<code>
|
||||||
# Bash (or others) compound command
|
# Bash (or others) compound command
|
||||||
if ((MATH)); then
|
if ((MATH)); then
|
||||||
...
|
...
|
||||||
@ -45,9 +45,9 @@ if ((MATH)); then
|
|||||||
# portable equivalent command
|
# portable equivalent command
|
||||||
if [ "$((MATH))" -ne 0 ]; then
|
if [ "$((MATH))" -ne 0 ]; then
|
||||||
...
|
...
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Quotes around the arithmetic expansion ''<nowiki>$((MATH))</nowiki>'' 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//.
|
Quotes around the arithmetic expansion ''<nowiki>$((MATH))</nowiki>'' 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 ====
|
==== echo command ====
|
||||||
|
|
||||||
@ -56,7 +56,7 @@ The overall problem with ''echo'' is, that there are 2 (maybe more) mainstream f
|
|||||||
Why? (list of known behaviours)
|
Why? (list of known behaviours)
|
||||||
* may or may not automatically interpret backslash escpape codes in the strings
|
* 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 automatically interpret switches (like ''-n'')
|
||||||
* may or may not ignore "end of options" tag (''<nowiki>--</nowiki>'')
|
* may or may not ignore "end of options" tag (''<nowiki>--</nowiki>'')
|
||||||
* ''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)
|
* ''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 [[commands:builtin:printf | the ''printf'' command]].
|
For these, and possibly other, reasons, POSIX (SUS) standardized the existance of [[commands:builtin:printf | the ''printf'' command]].
|
||||||
@ -65,7 +65,7 @@ For these, and possibly other, reasons, POSIX (SUS) standardized the existance o
|
|||||||
==== Parameter expansions ====
|
==== Parameter expansions ====
|
||||||
|
|
||||||
* ''${var:x:x}'' is KSH93/Bash specific
|
* ''${var:x:x}'' is KSH93/Bash specific
|
||||||
* ''${var/../..}'' and ''${var<nowiki>//</nowiki>../..}'' are KSH93/Bash specific
|
* ''${var/../..}'' and ''${var<nowiki>//</nowiki>../..}'' 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)
|
* ''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 ====
|
==== Special variables ====
|
||||||
@ -73,31 +73,31 @@ For these, and possibly other, reasons, POSIX (SUS) standardized the existance o
|
|||||||
=== PWD ===
|
=== PWD ===
|
||||||
|
|
||||||
[[syntax:shellvars#PWD|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:
|
[[syntax:shellvars#PWD|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:
|
||||||
<code>
|
<code>
|
||||||
pwd -P > dev/null
|
pwd -P > dev/null
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
=== RANDOM ===
|
=== RANDOM ===
|
||||||
|
|
||||||
[[syntax:shellvars#RANDOM|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.
|
[[syntax:shellvars#RANDOM|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.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
# 'gawk' can produce random numbers using srand(). In this example, 10 integers between 1 and 500:
|
# '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["pid"]); for (i = 0; i < nNum; ++i) {print int(min + rand() * (max - min)} }')
|
randpm=$(gawk -v min=1 -v max=500 -v nNum=10 'BEGIN { srand(systime() + PROCINFO["pid"]); 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)
|
# '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="$(date +%Y%M%d%H%M%S)" 'BEGIN { srand(seed); for (i = 0; i < nNum; ++i) {print int(min + rand() * (max - min)} }')
|
randpm=$(mawk -v min=1 -v max=500 -v nNum=10 -v seed="$(date +%Y%M%d%H%M%S)" 'BEGIN { srand(seed); for (i = 0; i < nNum; ++i) {print int(min + rand() * (max - min)} }')
|
||||||
</code>
|
</code>
|
||||||
//Yes, I'm not an ''awk'' expert, so please correct it, rather than complaining about possible stupid code!//
|
//Yes, I'm not an ''awk'' expert, so please correct it, rather than complaining about possible stupid code!//
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
# Well, seeing how this //is// BASH-hackers.org I kinda missed the bash way of doing the above ;-)
|
# 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 :-)
|
# print a number between 0 and 500 :-)
|
||||||
printf $(( 500 * RANDOM / 32767 ))
|
printf $(( 500 * RANDOM / 32767 ))
|
||||||
|
|
||||||
# Or print 30 random numbers between 0 and 10 ;)
|
# Or print 30 random numbers between 0 and 10 ;)
|
||||||
X=0; while (( X++ < 30 )); do echo $(( 10 * RANDOM / 32767 )); done
|
X=0; while (( X++ < 30 )); do echo $(( 10 * RANDOM / 32767 )); done
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
|
|
||||||
=== SECONDS ===
|
=== SECONDS ===
|
||||||
@ -116,21 +116,21 @@ However, this method doesn't look nice. There are other ways of doing this, usin
|
|||||||
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.
|
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'':
|
For example, to check if the command ''ls'' is available in a location accessible by ''PATH'':
|
||||||
<code>
|
<code>
|
||||||
if hash ls >/dev/null 2>&1; then
|
if hash ls >/dev/null 2>&1; then
|
||||||
echo "ls is available"
|
echo "ls is available"
|
||||||
fi
|
fi
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Somewhat of a mass-check:
|
Somewhat of a mass-check:
|
||||||
<code>
|
<code>
|
||||||
for name in ls grep sed awk; do
|
for name in ls grep sed awk; do
|
||||||
if ! hash "$name" >/dev/null 2>&1; then
|
if ! hash "$name" >/dev/null 2>&1; then
|
||||||
echo "FAIL: Missing command '$name'"
|
echo "FAIL: Missing command '$name'"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Here (bash 3), ''hash'' also respects builtin commands. I don't know if this works everywhere, but it seems logical.
|
Here (bash 3), ''hash'' also respects builtin commands. I don't know if this works everywhere, but it seems logical.
|
||||||
|
|
||||||
@ -138,8 +138,8 @@ Here (bash 3), ''hash'' also respects builtin commands. I don't know if this wor
|
|||||||
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.
|
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'':
|
For example, to check if the command ''sed'' is available in a location accessible by ''PATH'':
|
||||||
<code>
|
<code>
|
||||||
if command -v sed >/dev/null 2>&1; then
|
if command -v sed >/dev/null 2>&1; then
|
||||||
echo "sed is available"
|
echo "sed is available"
|
||||||
fi
|
fi
|
||||||
</code>
|
</code>
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
====== Obsolete and deprecated syntax ======
|
====== Obsolete and deprecated syntax ======
|
||||||
|
|
||||||
{{keywords>bash shell scripting obsolete deprecated outdated}}
|
{{keywords>bash shell scripting obsolete deprecated outdated}}
|
||||||
|
|
||||||
This (incomplete) page describes some syntax and commands considered obsolete by some measure. A thorough discussion of the rationale is beyond the scope of this page. See the [[scripting:nonportable | portability page]] for a discussion on portability issues.
|
This (incomplete) page describes some syntax and commands considered obsolete by some measure. A thorough discussion of the rationale is beyond the scope of this page. See the [[scripting:nonportable | portability page]] for a discussion on portability issues.
|
||||||
|
|
||||||
This first table lists syntax that is tolerated by Bash but has few if any legitimate uses. These features exist mostly for Bourne, csh, or some other backward compatibility with obsolete shells, or were Bash-specific features considered failed experiments and deprecated or replaced with a better alternative. These should be irrelevant to most everyone except maybe code golfers. New scripts should never use them. None of the items on this list are specified by the most current version of POSIX, and some may be incompatible with POSIX.
|
This first table lists syntax that is tolerated by Bash but has few if any legitimate uses. These features exist mostly for Bourne, csh, or some other backward compatibility with obsolete shells, or were Bash-specific features considered failed experiments and deprecated or replaced with a better alternative. These should be irrelevant to most everyone except maybe code golfers. New scripts should never use them. None of the items on this list are specified by the most current version of POSIX, and some may be incompatible with POSIX.
|
||||||
|
|
||||||
^ Syntax ^ Replacement ^ Description ^
|
^ Syntax ^ Replacement ^ Description ^
|
||||||
|''&>FILE'' and ''>&FILE'' |''>FILE 2>&1'' |This redirection syntax is short for ''>FILE 2>&1'' and originates in the C Shell. The latter form is especially uncommon and should never be used, and the explicit form using separate redirections is preferred over both. These shortcuts contribute to confusion about the copy descriptor because the syntax is unclear. They also introduce parsing ambiguity, and conflict with POSIX. Shells without this feature treat ''cmd1 &>file cmd2'' as: "background ''cmd1'' and then execute ''cmd2'' with its stdout redirected to ''file''", which is the correct interpretation of this expression. See: [[syntax:redirection | redirection]]<code bash> $ { bash; dash </dev/fd/0; } <<<'echo foo>/dev/null&>/dev/fd/2 echo bar'
|
|''&>FILE'' and ''>&FILE'' |''>FILE 2>&1'' |This redirection syntax is short for ''>FILE 2>&1'' and originates in the C Shell. The latter form is especially uncommon and should never be used, and the explicit form using separate redirections is preferred over both. These shortcuts contribute to confusion about the copy descriptor because the syntax is unclear. They also introduce parsing ambiguity, and conflict with POSIX. Shells without this feature treat ''cmd1 &>file cmd2'' as: "background ''cmd1'' and then execute ''cmd2'' with its stdout redirected to ''file''", which is the correct interpretation of this expression. See: [[syntax:redirection | redirection]]<code bash> $ { bash; dash </dev/fd/0; } <<<'echo foo>/dev/null&>/dev/fd/2 echo bar'
|
||||||
foo echo bar
|
foo echo bar
|
||||||
bar</code>|
|
bar</code>|
|
||||||
|''$[EXPRESSION]'' |''<nowiki>$((EXPRESSION))</nowiki>'' |This undocumented syntax is completely replaced by the POSIX-conforming arithmetic expansion ''<nowiki>$((EXPRESSION))</nowiki>''. It is unimplemented almost everywhere except Bash and Zsh. See [[syntax:expansion:arith | arithmetic expansion]]. [[http://lists.gnu.org/archive/html/bug-bash/2012-04/msg00034.html | Some discussion]].|
|
|''$[EXPRESSION]'' |''<nowiki>$((EXPRESSION))</nowiki>'' |This undocumented syntax is completely replaced by the POSIX-conforming arithmetic expansion ''<nowiki>$((EXPRESSION))</nowiki>''. It is unimplemented almost everywhere except Bash and Zsh. See [[syntax:expansion:arith | arithmetic expansion]]. [[http://lists.gnu.org/archive/html/bug-bash/2012-04/msg00034.html | Some discussion]].|
|
||||||
|''COMMAND\ |&\ COMMAND'' |''COMMAND 2>&1 | COMMAND'' |This is an alternate pipeline operator derived from Zsh. Officially, it is not considered deprecated by Bash, but I highly discourage it. It conflicts with the list operator used for [[syntax:keywords:coproc | coprocess]] creation in most Korn shells. It also has confusing behavior. The stdout is redirected first like an ordinary pipe, while the stderr is actually redirected last -- after other redirects preceding the pipe operator. Overall, it's pointless syntax bloat. Use an explicit redirect instead.|
|
|''COMMAND\ |&\ COMMAND'' |''COMMAND 2>&1 | COMMAND'' |This is an alternate pipeline operator derived from Zsh. Officially, it is not considered deprecated by Bash, but I highly discourage it. It conflicts with the list operator used for [[syntax:keywords:coproc | coprocess]] creation in most Korn shells. It also has confusing behavior. The stdout is redirected first like an ordinary pipe, while the stderr is actually redirected last -- after other redirects preceding the pipe operator. Overall, it's pointless syntax bloat. Use an explicit redirect instead.|
|
||||||
|''function\ NAME()\ COMPOUND-CMD'' |''NAME()\ COMPOUND-CMD'' or ''function\ NAME\ {\ CMDS;\ }'' |This is an amalgamation between the Korn and POSIX style function definitions - using both the ''function'' keyword and parentheses. It has no useful purpose and no historical basis or reason to exist. It is not specified by POSIX. It is accepted by Bash, mksh, zsh, and perhaps some other Korn shells, where it is treated as identical to the POSIX-style function. It is not accepted by AT&T ksh. It should never be used. See the next table for the ''function'' keyword. Bash doesn't have this feature documented as expressly deprecated.|
|
|''function\ NAME()\ COMPOUND-CMD'' |''NAME()\ COMPOUND-CMD'' or ''function\ NAME\ {\ CMDS;\ }'' |This is an amalgamation between the Korn and POSIX style function definitions - using both the ''function'' keyword and parentheses. It has no useful purpose and no historical basis or reason to exist. It is not specified by POSIX. It is accepted by Bash, mksh, zsh, and perhaps some other Korn shells, where it is treated as identical to the POSIX-style function. It is not accepted by AT&T ksh. It should never be used. See the next table for the ''function'' keyword. Bash doesn't have this feature documented as expressly deprecated.|
|
||||||
|''for x; { ...;}'' |''do'', ''done'', ''in'', ''esac'', etc. |This undocumented syntax replaces the ''do'' and ''done'' reserved words with braces. Many Korn shells support various permutations on this syntax for certain compound commands like ''for'', ''case'', and ''while''. Which ones and certain details like whether a newline or semicolon are required vary. Only ''for'' works in Bash. Needless to say, don't use it.|
|
|''for x; { ...;}'' |''do'', ''done'', ''in'', ''esac'', etc. |This undocumented syntax replaces the ''do'' and ''done'' reserved words with braces. Many Korn shells support various permutations on this syntax for certain compound commands like ''for'', ''case'', and ''while''. Which ones and certain details like whether a newline or semicolon are required vary. Only ''for'' works in Bash. Needless to say, don't use it.|
|
||||||
|
|
||||||
This table lists syntax that is specified by POSIX (unless otherwise specified below), but has been superseded by superior alternatives (either in POSIX, Bash, or both), or is highly discouraged for other reasons such as encouraging bad practices or dangerous code. Those that are specified by POSIX may be badly designed and unchangeable for historical reasons.
|
This table lists syntax that is specified by POSIX (unless otherwise specified below), but has been superseded by superior alternatives (either in POSIX, Bash, or both), or is highly discouraged for other reasons such as encouraging bad practices or dangerous code. Those that are specified by POSIX may be badly designed and unchangeable for historical reasons.
|
||||||
@ -20,22 +20,22 @@ This table lists syntax that is specified by POSIX (unless otherwise specified b
|
|||||||
^ Syntax ^ Replacement ^ Description ^
|
^ Syntax ^ Replacement ^ Description ^
|
||||||
|Unquoted expansions, [[syntax:expansion:wordsplit]], and [[syntax:expansion:globs]] |[[http://mywiki.wooledge.org/Quotes | Proper quoting]], Ksh/Bash-style [[syntax:arrays | arrays]], The "$@" expansion, [[commands:builtin:read]] |//Quoting errors// are a broad category of common mistakes brought about by a few unintuitive features carried over from the Bourne shell due to complaints of broken scripts and changes in previously documented behavior. Most of the important expansions are performed at the same time from left to right. However, a few expansions, most notably word-splitting and globbing, and in shells other than Bash, [[syntax:expansion:brace | brace expansion]], are performed **on the results of previous expansions, by default, unless they are quoted.** This means that the act of expanding an unquoted variable in an ordinary argument context, depending on the value of the variable, can yield different results depending on possibly uncontrolled side-effects like the value of ''IFS'', and the names of files in the current working directory. You can't get globbing without word-splitting, or vice versa (without ''set -f''). [[http://mywiki.wooledge.org/BashFAQ/050 | You can't store a command or character-delimited list in a variable and safely evaluate it with unquoted expansion]]. If possible, always choose a shell that supports Korn shell arrays such as Bash. They are a vital but non-standard feature for writing clean, safe scripts. Well-written scripts don't use word-splitting. A few exceptions are listed on the [[syntax:expansion:wordsplit | word splitting page]]. A significant proportion of the issues on the famous [[http://mywiki.wooledge.org/BashPitfalls | Pitfalls list]] fall under this category. See also: //[[http://mywiki.wooledge.org/DontReadLinesWithFor | Don't read lines with for!]]// |
|
|Unquoted expansions, [[syntax:expansion:wordsplit]], and [[syntax:expansion:globs]] |[[http://mywiki.wooledge.org/Quotes | Proper quoting]], Ksh/Bash-style [[syntax:arrays | arrays]], The "$@" expansion, [[commands:builtin:read]] |//Quoting errors// are a broad category of common mistakes brought about by a few unintuitive features carried over from the Bourne shell due to complaints of broken scripts and changes in previously documented behavior. Most of the important expansions are performed at the same time from left to right. However, a few expansions, most notably word-splitting and globbing, and in shells other than Bash, [[syntax:expansion:brace | brace expansion]], are performed **on the results of previous expansions, by default, unless they are quoted.** This means that the act of expanding an unquoted variable in an ordinary argument context, depending on the value of the variable, can yield different results depending on possibly uncontrolled side-effects like the value of ''IFS'', and the names of files in the current working directory. You can't get globbing without word-splitting, or vice versa (without ''set -f''). [[http://mywiki.wooledge.org/BashFAQ/050 | You can't store a command or character-delimited list in a variable and safely evaluate it with unquoted expansion]]. If possible, always choose a shell that supports Korn shell arrays such as Bash. They are a vital but non-standard feature for writing clean, safe scripts. Well-written scripts don't use word-splitting. A few exceptions are listed on the [[syntax:expansion:wordsplit | word splitting page]]. A significant proportion of the issues on the famous [[http://mywiki.wooledge.org/BashPitfalls | Pitfalls list]] fall under this category. See also: //[[http://mywiki.wooledge.org/DontReadLinesWithFor | Don't read lines with for!]]// |
|
||||||
|''`COMMANDS`'' |''$(COMMANDS)'' |This is the older Bourne-compatible form of the [[syntax:expansion:cmdsubst | command substitution]]. Both the ''`COMMANDS`'' and ''$(COMMANDS)'' syntaxes are specified by POSIX, but the latter is __greatly__ preferred, though the former is unfortunately still very prevalent in scripts. New-style command substitutions are widely implemented by every modern shell (and then some). The only reason for using backticks is for compatibility with a real Bourne shell (like Heirloom). Backtick command substitutions require special escaping when nested, and examples found in the wild are improperly quoted more often than not. See: //[[http://mywiki.wooledge.org/BashFAQ/082 | Why is $(...) preferred over `...` (backticks)?]]//.|
|
|''`COMMANDS`'' |''$(COMMANDS)'' |This is the older Bourne-compatible form of the [[syntax:expansion:cmdsubst | command substitution]]. Both the ''`COMMANDS`'' and ''$(COMMANDS)'' syntaxes are specified by POSIX, but the latter is __greatly__ preferred, though the former is unfortunately still very prevalent in scripts. New-style command substitutions are widely implemented by every modern shell (and then some). The only reason for using backticks is for compatibility with a real Bourne shell (like Heirloom). Backtick command substitutions require special escaping when nested, and examples found in the wild are improperly quoted more often than not. See: //[[http://mywiki.wooledge.org/BashFAQ/082 | Why is $(...) preferred over `...` (backticks)?]]//.|
|
||||||
|''[\ EXPRESSION\ ]''\ and\ ''test\ EXPRESSION'' |''<nowiki>[[</nowiki>\ EXPRESSION\ <nowiki>]]</nowiki>'' |''test'' and ''['' are the Bourne/POSIX commands for evaluating test expressions (they are almost identical, and ''['' is somewhat more common). The expressions consist of regular arguments, unlike the Ksh/Bash ''<nowiki>[[</nowiki>'' command. While the issue is analogous to ''let'' vs ''<nowiki>((</nowiki>'', the advantages of ''<nowiki>[[</nowiki>'' vs ''['' are even more important because the arguments/expansions aren't just concatenated into one expression. With the classic ''['' command, the number of arguments is significant. If at all possible, use the [[syntax:ccmd:conditional_expression | conditional expression]] ("new test command") ''<nowiki>[[ EXPRESSION ]]</nowiki>''. Unless there is a need for POSIX compatibility, there are only a few reasons to use ''[''. ''<nowiki>[[</nowiki>'' is one of the most portable and consistent non-POSIX ksh extensions available. See: [[syntax:ccmd:conditional_expression]] and //[[http://mywiki.wooledge.org/BashFAQ/031 | What is the difference between test, [ and [[ ?]]// |
|
|''[\ EXPRESSION\ ]''\ and\ ''test\ EXPRESSION'' |''<nowiki>[[</nowiki>\ EXPRESSION\ <nowiki>]]</nowiki>'' |''test'' and ''['' are the Bourne/POSIX commands for evaluating test expressions (they are almost identical, and ''['' is somewhat more common). The expressions consist of regular arguments, unlike the Ksh/Bash ''<nowiki>[[</nowiki>'' command. While the issue is analogous to ''let'' vs ''<nowiki>((</nowiki>'', the advantages of ''<nowiki>[[</nowiki>'' vs ''['' are even more important because the arguments/expansions aren't just concatenated into one expression. With the classic ''['' command, the number of arguments is significant. If at all possible, use the [[syntax:ccmd:conditional_expression | conditional expression]] ("new test command") ''<nowiki>[[ EXPRESSION ]]</nowiki>''. Unless there is a need for POSIX compatibility, there are only a few reasons to use ''[''. ''<nowiki>[[</nowiki>'' is one of the most portable and consistent non-POSIX ksh extensions available. See: [[syntax:ccmd:conditional_expression]] and //[[http://mywiki.wooledge.org/BashFAQ/031 | What is the difference between test, [ and [[ ?]]// |
|
||||||
|''set -e'', ''set -o errexit''\\ and the ''ERR'' trap |proper control flow and error handling |''set -e'' causes untested non-zero exit statuses to be fatal. It is a debugging feature intended for use only during development and should not be used in production code, especially init scripts and other high-availability scripts. Do not be tempted to think of this as "error handling"; it's not, it's just a way to find the place you've //forgotten// to put error handling.\\ Think of it as akin to ''use strict'' in Perl or ''throws'' in C++: tough love that makes you write better code. Many guides recommend avoiding it entirely because of the apparently-complex rules for when non-zero statuses cause the script to abort. Conversely, large software projects with experienced coders may recommend or even mandate its use.\\ Because it provides no notification of the location of the error, it's more useful combined with ''set -x'' or the ''DEBUG'' trap and other Bash debug features, and both flags are normally better set on the command line rather than within the script itself.\\ Most of this also applies to the ''ERR'' trap, though I've seen it used in a few places in shells that lack ''pipefail'' or ''PIPESTATUS''. The ''ERR'' trap is not POSIX, but ''set -e'' is. ''failglob'' is another Bash feature that falls into this category (mainly useful for debugging).\\ **The ''set -e'' feature generates more questions and false bug reports on the Bash mailing list than all other features combined!** Please do not rely on ''set -e'' for logic in scripts. If you still refuse to take this advice, make sure you understand **exactly** how it works. See: //[[http://mywiki.wooledge.org/BashFAQ/105 | Why doesn't set -e (or set -o errexit, or trap ERR) do what I expected?]]// and [[http://www.fvue.nl/wiki/Bash:_Error_handling]] |
|
|''set -e'', ''set -o errexit''\\ and the ''ERR'' trap |proper control flow and error handling |''set -e'' causes untested non-zero exit statuses to be fatal. It is a debugging feature intended for use only during development and should not be used in production code, especially init scripts and other high-availability scripts. Do not be tempted to think of this as "error handling"; it's not, it's just a way to find the place you've //forgotten// to put error handling.\\ Think of it as akin to ''use strict'' in Perl or ''throws'' in C++: tough love that makes you write better code. Many guides recommend avoiding it entirely because of the apparently-complex rules for when non-zero statuses cause the script to abort. Conversely, large software projects with experienced coders may recommend or even mandate its use.\\ Because it provides no notification of the location of the error, it's more useful combined with ''set -x'' or the ''DEBUG'' trap and other Bash debug features, and both flags are normally better set on the command line rather than within the script itself.\\ Most of this also applies to the ''ERR'' trap, though I've seen it used in a few places in shells that lack ''pipefail'' or ''PIPESTATUS''. The ''ERR'' trap is not POSIX, but ''set -e'' is. ''failglob'' is another Bash feature that falls into this category (mainly useful for debugging).\\ **The ''set -e'' feature generates more questions and false bug reports on the Bash mailing list than all other features combined!** Please do not rely on ''set -e'' for logic in scripts. If you still refuse to take this advice, make sure you understand **exactly** how it works. See: //[[http://mywiki.wooledge.org/BashFAQ/105 | Why doesn't set -e (or set -o errexit, or trap ERR) do what I expected?]]// and [[http://www.fvue.nl/wiki/Bash:_Error_handling]] |
|
||||||
|''set -u'' or ''set -o nounset'' |Proper control flow and error handling |''set -u'' causes attempts to expand unset variables or parameters as fatal errors. Like ''set -e'', it bypasses control flow and exits immediately from the current shell environment. Like non-zero statuses, unset variables are a normal part of most non-trivial shell scripts. Living with ''set -u'' requires hacks like ''${1+"$1"}'' for each expansion that might possibly be unset. Only very current shells guarantee that expanding ''@'' or ''*'' won't trigger an error when no parameters are set ([[http://austingroupbugs.net/view.php?id=155]], [[http://www.in-ulm.de/~mascheck/various/bourne_args/]]). Apparently some find it useful for debugging. See //[[http://mywiki.wooledge.org/BashFAQ/083 | How do I determine whether a variable is already defined? Or a function?]]// for how to properly test for defined variables. Don't use ''set -u''. |
|
|''set -u'' or ''set -o nounset'' |Proper control flow and error handling |''set -u'' causes attempts to expand unset variables or parameters as fatal errors. Like ''set -e'', it bypasses control flow and exits immediately from the current shell environment. Like non-zero statuses, unset variables are a normal part of most non-trivial shell scripts. Living with ''set -u'' requires hacks like ''${1+"$1"}'' for each expansion that might possibly be unset. Only very current shells guarantee that expanding ''@'' or ''*'' won't trigger an error when no parameters are set ([[http://austingroupbugs.net/view.php?id=155]], [[http://www.in-ulm.de/~mascheck/various/bourne_args/]]). Apparently some find it useful for debugging. See //[[http://mywiki.wooledge.org/BashFAQ/083 | How do I determine whether a variable is already defined? Or a function?]]// for how to properly test for defined variables. Don't use ''set -u''. |
|
||||||
|''${var?msg}'' or ''${var:?msg}'' |Proper control flow and error handling |Like ''set -u'', this expansion causes a fatal error which immediately exits the current shell environment if the given parameter is unset or is null. It prints the error message given, to the right of the operator. If a value is expected and you'd like to create an assertion or cause errors, it's better to test for undefined variables using one of [[http://mywiki.wooledge.org/BashFAQ/083 | these techniques]] and handle the error manually, or call a ''die'' function. This expansion is defined by POSIX. It's better than ''set -u'', because it's explicit, but not by much. It also allows you to accidentally construct hilariously deceptive error messages: <code>bash -c 'f() { definitely_not_printf "${printf:?"$1" - No such option}"; }; f -v'
|
|''${var?msg}'' or ''${var:?msg}'' |Proper control flow and error handling |Like ''set -u'', this expansion causes a fatal error which immediately exits the current shell environment if the given parameter is unset or is null. It prints the error message given, to the right of the operator. If a value is expected and you'd like to create an assertion or cause errors, it's better to test for undefined variables using one of [[http://mywiki.wooledge.org/BashFAQ/083 | these techniques]] and handle the error manually, or call a ''die'' function. This expansion is defined by POSIX. It's better than ''set -u'', because it's explicit, but not by much. It also allows you to accidentally construct hilariously deceptive error messages: <code>bash -c 'f() { definitely_not_printf "${printf:?"$1" - No such option}"; }; f -v'
|
||||||
bash: printf: -v - No such option</code> |
|
bash: printf: -v - No such option</code> |
|
||||||
|
|
||||||
This table lists features that are used only if you have a specific reason to prefer it over another alternative. These have some legitimate uses if you know what you're doing, such as for those with specific portability requirements, or in order to make use of some subtle behavioral differences. These are frequently (mis)used for no reason. Writing portable scripts that go outside of POSIX features requires knowing how to account for many (often undocumented) differences across many shells. If you do happen to know what you're doing, don't be too surprised if you run across someone telling you not to use these.
|
This table lists features that are used only if you have a specific reason to prefer it over another alternative. These have some legitimate uses if you know what you're doing, such as for those with specific portability requirements, or in order to make use of some subtle behavioral differences. These are frequently (mis)used for no reason. Writing portable scripts that go outside of POSIX features requires knowing how to account for many (often undocumented) differences across many shells. If you do happen to know what you're doing, don't be too surprised if you run across someone telling you not to use these.
|
||||||
|
|
||||||
^ Syntax ^ Replacement ^ Description ^
|
^ Syntax ^ Replacement ^ Description ^
|
||||||
|''function\ NAME\ {\ CMDS;\ }'' |''NAME()\ COMPOUND-CMD'' |This is the ksh form of function definition created to extend the Bourne and POSIX form with modified behaviors and additional features like local variables. The idea was for new-style functions to be analogous to regular builtins with their own environment and scope, while POSIX-style functions are more like special builtins. ''function'' is supported by almost every ksh-derived shell including Bash and Zsh, but isn't specified by POSIX. Bash treats all function styles the same, but this is unusual. ''function'' has some preferable characteristics in many ksh variants, making it more portable for scripts that use non-POSIX extensions by some measures. If you're going to use the ''function'' keyword, it implies that you're either targeting Ksh specifically, or that you have detailed knowledge of how to compensate for differences across shells. It should always be used consistently with ''typeset'', but never used with ''declare'' or ''local''. Also in ksh93, the braces are not a [[syntax:ccmd:grouping_plain | command group]], but a required part of the syntax (unlike Bash and others). See [[syntax:basicgrammar#shell_function_definitions | shell function definitions]] |
|
|''function\ NAME\ {\ CMDS;\ }'' |''NAME()\ COMPOUND-CMD'' |This is the ksh form of function definition created to extend the Bourne and POSIX form with modified behaviors and additional features like local variables. The idea was for new-style functions to be analogous to regular builtins with their own environment and scope, while POSIX-style functions are more like special builtins. ''function'' is supported by almost every ksh-derived shell including Bash and Zsh, but isn't specified by POSIX. Bash treats all function styles the same, but this is unusual. ''function'' has some preferable characteristics in many ksh variants, making it more portable for scripts that use non-POSIX extensions by some measures. If you're going to use the ''function'' keyword, it implies that you're either targeting Ksh specifically, or that you have detailed knowledge of how to compensate for differences across shells. It should always be used consistently with ''typeset'', but never used with ''declare'' or ''local''. Also in ksh93, the braces are not a [[syntax:ccmd:grouping_plain | command group]], but a required part of the syntax (unlike Bash and others). See [[syntax:basicgrammar#shell_function_definitions | shell function definitions]] |
|
||||||
|''typeset'' |''declare'', ''local'', ''export'', ''readonly'' |This is closely related to the above, and should often be used together. ''typeset'' exists primarily for ''ksh'' compatibility, but is marked as "deprecated" in Bash (though I don't entirely agree with this). This makes some sense, because future compatibility can't be guaranteed, and any compatibility at all, requires understanding the non-POSIX features of other shells and their differences. Using ''declare'' instead of ''typeset'' emphasizes your intention to be "Bash-only", and definitely breaks everywhere else (except possibly zsh if you're lucky). The issue is further complicated by Dash and the [[http://www.debian.org/doc/debian-policy/ch-files.html#s-scripts | Debian policy]] requirement for a ''local'' builtin, which is itself not entirely compatible with Bash and other shells. |
|
|''typeset'' |''declare'', ''local'', ''export'', ''readonly'' |This is closely related to the above, and should often be used together. ''typeset'' exists primarily for ''ksh'' compatibility, but is marked as "deprecated" in Bash (though I don't entirely agree with this). This makes some sense, because future compatibility can't be guaranteed, and any compatibility at all, requires understanding the non-POSIX features of other shells and their differences. Using ''declare'' instead of ''typeset'' emphasizes your intention to be "Bash-only", and definitely breaks everywhere else (except possibly zsh if you're lucky). The issue is further complicated by Dash and the [[http://www.debian.org/doc/debian-policy/ch-files.html#s-scripts | Debian policy]] requirement for a ''local'' builtin, which is itself not entirely compatible with Bash and other shells. |
|
||||||
|''let 'EXPR' '' |''<nowiki>((EXPR))</nowiki>'' or ''[\ <nowiki>$((EXPR))</nowiki>\ -ne\ 0 ]'' |''let'' is the "simple command" variant of arithmetic evaluation command, which takes regular arguments. Both ''let'' and ''<nowiki>((expr))</nowiki>'' were present in ksh88, and everything that supports one should support the other. Neither are POSIX. The compound variant is preferable because it doesn't take regular arguments for [[syntax:expansion:wordsplit | wordsplitting]] and [[syntax:expansion:globs | globbing]], which makes it safer and clearer. It is also usually faster, especially in Bash, where compound commands are typically significantly faster. Some of the (few) reasons for using ''let'' are detailed on the [[commands:builtin:let | let]] page. See [[syntax:ccmd:arithmetic_eval | arithmetic evaluation compound command]] |
|
|''let 'EXPR' '' |''<nowiki>((EXPR))</nowiki>'' or ''[\ <nowiki>$((EXPR))</nowiki>\ -ne\ 0 ]'' |''let'' is the "simple command" variant of arithmetic evaluation command, which takes regular arguments. Both ''let'' and ''<nowiki>((expr))</nowiki>'' were present in ksh88, and everything that supports one should support the other. Neither are POSIX. The compound variant is preferable because it doesn't take regular arguments for [[syntax:expansion:wordsplit | wordsplitting]] and [[syntax:expansion:globs | globbing]], which makes it safer and clearer. It is also usually faster, especially in Bash, where compound commands are typically significantly faster. Some of the (few) reasons for using ''let'' are detailed on the [[commands:builtin:let | let]] page. See [[syntax:ccmd:arithmetic_eval | arithmetic evaluation compound command]] |
|
||||||
|''eval'' |Depends. Often code can be restructured to use better alternatives. |''eval'' is thrown in here for good measure, as sadly it is so often misused that any use of ''eval'' (even the rare clever one) is immediately dismissed as wrong by experts, and among the most immediate solutions abused by beginners. In reality, there are correct ways to use ''eval'', and even cases in which it's necessary, even in sophisticated shells like Bash and Ksh. ''eval'' is unusual in that it is less frequently appropriate in more feature-rich shells than in more minimal shells like Dash, where it is used to compensate for more limitations. If you find yourself needing ''eval'' too frequently, it might be a sign that you're either better off using a different language entirely, or trying to borrow an idiom from some other paradigm that isn't well suited to the shell language. By the same token, there are some cases in which working too hard to avoid ''eval'' ends up adding a lot of complexity and sacrificing all portability. Don't substitute a clever ''eval'' for something that's a bit "too clever", just to avoid the ''eval'', yet, take reasonable measures to avoid it where it is sensible to do so. See: [[commands:builtin:eval]] and [[http://mywiki.wooledge.org/BashFAQ/048 | Eval command and security issues]]. |
|
|''eval'' |Depends. Often code can be restructured to use better alternatives. |''eval'' is thrown in here for good measure, as sadly it is so often misused that any use of ''eval'' (even the rare clever one) is immediately dismissed as wrong by experts, and among the most immediate solutions abused by beginners. In reality, there are correct ways to use ''eval'', and even cases in which it's necessary, even in sophisticated shells like Bash and Ksh. ''eval'' is unusual in that it is less frequently appropriate in more feature-rich shells than in more minimal shells like Dash, where it is used to compensate for more limitations. If you find yourself needing ''eval'' too frequently, it might be a sign that you're either better off using a different language entirely, or trying to borrow an idiom from some other paradigm that isn't well suited to the shell language. By the same token, there are some cases in which working too hard to avoid ''eval'' ends up adding a lot of complexity and sacrificing all portability. Don't substitute a clever ''eval'' for something that's a bit "too clever", just to avoid the ''eval'', yet, take reasonable measures to avoid it where it is sensible to do so. See: [[commands:builtin:eval]] and [[http://mywiki.wooledge.org/BashFAQ/048 | Eval command and security issues]]. |
|
||||||
|
|
||||||
===== See also =====
|
===== See also =====
|
||||||
* [[scripting:nonportable | Non-portable syntax and command uses]]
|
* [[scripting:nonportable | Non-portable syntax and command uses]]
|
||||||
* [[scripting:bashchanges]]
|
* [[scripting:bashchanges]]
|
||||||
* [[BashFAQ>061 | Greg's BashFAQ 061: List of essential features added (with the Bash version tag)]]
|
* [[BashFAQ>061 | Greg's BashFAQ 061: List of essential features added (with the Bash version tag)]]
|
||||||
* [[http://mywiki.wooledge.org/Bashism | Bash <-> POSIX Portability guide with a focus on Dash]]
|
* [[http://mywiki.wooledge.org/Bashism | Bash <-> POSIX Portability guide with a focus on Dash]]
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
====== Handling positional parameters ======
|
====== Handling positional parameters ======
|
||||||
|
|
||||||
{{keywords>bash shell scripting arguments positional parameters options}}
|
{{keywords>bash shell scripting arguments positional parameters options}}
|
||||||
|
|
||||||
|
|
||||||
===== Intro =====
|
===== Intro =====
|
||||||
@ -32,25 +32,25 @@ is usually set to the script's name exactly as called, and it's
|
|||||||
set on shell initialization:
|
set on shell initialization:
|
||||||
|
|
||||||
__Testscript__ - it just echos ''$0'':
|
__Testscript__ - it just echos ''$0'':
|
||||||
<code>
|
<code>
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
echo "$0"
|
echo "$0"
|
||||||
</code>
|
</code>
|
||||||
You see, ''$0'' is always set to the name the script is called with (''>'' is the prompt...):
|
You see, ''$0'' is always set to the name the script is called with (''>'' is the prompt...):
|
||||||
<code>
|
<code>
|
||||||
> ./testscript
|
> ./testscript
|
||||||
./testscript
|
./testscript
|
||||||
</code>
|
</code>
|
||||||
<code>
|
<code>
|
||||||
> /usr/bin/testscript
|
> /usr/bin/testscript
|
||||||
/usr/bin/testscript
|
/usr/bin/testscript
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
However, this isn't true for login shells:
|
However, this isn't true for login shells:
|
||||||
<code>
|
<code>
|
||||||
> echo "$0"
|
> echo "$0"
|
||||||
-bash
|
-bash
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
In other terms, ''$0'' is not a positional parameter, it's a
|
In other terms, ''$0'' is not a positional parameter, it's a
|
||||||
special parameter independent from the positional parameter list. It
|
special parameter independent from the positional parameter list. It
|
||||||
@ -81,7 +81,7 @@ Enough theory, you want to access your script-arguments. Well, here we go.
|
|||||||
==== One by one ====
|
==== One by one ====
|
||||||
|
|
||||||
One way is to access specific parameters:
|
One way is to access specific parameters:
|
||||||
<code>
|
<code>
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
echo "Total number of arguments: $#"
|
echo "Total number of arguments: $#"
|
||||||
echo "Argument 1: $1"
|
echo "Argument 1: $1"
|
||||||
@ -89,14 +89,14 @@ echo "Argument 2: $2"
|
|||||||
echo "Argument 3: $3"
|
echo "Argument 3: $3"
|
||||||
echo "Argument 4: $4"
|
echo "Argument 4: $4"
|
||||||
echo "Argument 5: $5"
|
echo "Argument 5: $5"
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
While useful in another situation, this way is lacks flexibility.
|
While useful in another situation, this way is lacks flexibility.
|
||||||
The maximum number of arguments is a fixedvalue
|
The maximum number of arguments is a fixedvalue
|
||||||
- which is a bad idea if you write a script that takes many filenames
|
- which is a bad idea if you write a script that takes many filenames
|
||||||
as arguments.
|
as arguments.
|
||||||
|
|
||||||
=> forget that one
|
=> forget that one
|
||||||
|
|
||||||
==== Loops ====
|
==== Loops ====
|
||||||
|
|
||||||
@ -108,14 +108,14 @@ You can code a [[syntax:ccmd:c_for | C-style for-loop]] using ''$#''
|
|||||||
as the end value. On every iteration, the ''shift''-command is used to
|
as the end value. On every iteration, the ''shift''-command is used to
|
||||||
shift the argument list:
|
shift the argument list:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
numargs=$#
|
numargs=$#
|
||||||
for ((i=1 ; i <= numargs ; i++))
|
for ((i=1 ; i <= numargs ; i++))
|
||||||
do
|
do
|
||||||
echo "$1"
|
echo "$1"
|
||||||
shift
|
shift
|
||||||
done
|
done
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Not very stylish, but usable. The ''numargs'' variable is used
|
Not very stylish, but usable. The ''numargs'' variable is used
|
||||||
to store the initial value of ''$#'' because the shift command
|
to store the initial value of ''$#'' because the shift command
|
||||||
@ -126,12 +126,12 @@ will change it as the script runs.
|
|||||||
Another way to iterate one argument at a time is the ''for'' loop
|
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:
|
without a given wordlist. The loop uses the positional parameters as a wordlist:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
for arg
|
for arg
|
||||||
do
|
do
|
||||||
echo "$arg"
|
echo "$arg"
|
||||||
done
|
done
|
||||||
</code>
|
</code>
|
||||||
__Advantage:__ The positional parameters will be preserved
|
__Advantage:__ The positional parameters will be preserved
|
||||||
|
|
||||||
----
|
----
|
||||||
@ -140,24 +140,24 @@ 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''
|
it doesn't test for reaching ''$#''. It shifts and checks if ''$1''
|
||||||
still expands to something, using the [[commands:classictest | test command]]:
|
still expands to something, using the [[commands:classictest | test command]]:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
while [ "$1" ]
|
while [ "$1" ]
|
||||||
do
|
do
|
||||||
echo "$1"
|
echo "$1"
|
||||||
shift
|
shift
|
||||||
done
|
done
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Looks nice, but has the disadvantage of stopping when ''$1'' is empty
|
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
|
(null-string). Let's modify it to run as long as ''$1'' is defined
|
||||||
(but may be null), using [[syntax:pe#use_an_alternate_value | parameter expansion for an alternate value]]:
|
(but may be null), using [[syntax:pe#use_an_alternate_value | parameter expansion for an alternate value]]:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
while [ "${1+defined}" ]; do
|
while [ "${1+defined}" ]; do
|
||||||
echo "$1"
|
echo "$1"
|
||||||
shift
|
shift
|
||||||
done
|
done
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== Getopts ====
|
==== Getopts ====
|
||||||
|
|
||||||
@ -231,9 +231,9 @@ starting with the last one.
|
|||||||
|
|
||||||
__**Example:**__
|
__**Example:**__
|
||||||
START at the last positional parameter:
|
START at the last positional parameter:
|
||||||
<code>
|
<code>
|
||||||
echo "${@: -1}"
|
echo "${@: -1}"
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
__**Attention**__: 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''.
|
__**Attention**__: 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''.
|
||||||
|
|
||||||
@ -245,7 +245,7 @@ The [[ commands:builtin:set | builtin command, set ]]
|
|||||||
may be used to "artificially" change the positional parameters from
|
may be used to "artificially" change the positional parameters from
|
||||||
inside the script or function:
|
inside the script or function:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
set "This is" my new "set of" positional parameters
|
set "This is" my new "set of" positional parameters
|
||||||
|
|
||||||
# RESULTS IN
|
# RESULTS IN
|
||||||
@ -255,22 +255,22 @@ set "This is" my new "set of" positional parameters
|
|||||||
# $4: set of
|
# $4: set of
|
||||||
# $5: positional
|
# $5: positional
|
||||||
# $6: parameters
|
# $6: parameters
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
It's wise to signal "end of options" when setting positional
|
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
|
parameters this way. If not, the dashes might be interpreted as an option switch
|
||||||
by ''set'' itself:
|
by ''set'' itself:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
# both ways work, but behave differently. See the article about the set command!
|
# both ways work, but behave differently. See the article about the set command!
|
||||||
set -- ...
|
set -- ...
|
||||||
set - ...
|
set - ...
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Alternately this will also preserve any verbose (-v) or tracing (-x) flags, which may otherwise be reset by ''set''
|
Alternately this will also preserve any verbose (-v) or tracing (-x) flags, which may otherwise be reset by ''set''
|
||||||
<code>
|
<code>
|
||||||
set -$- ...
|
set -$- ...
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
FIXME continue
|
FIXME continue
|
||||||
|
|
||||||
@ -280,13 +280,13 @@ FIXME continue
|
|||||||
|
|
||||||
To make your program accept options as standard command syntax:
|
To make your program accept options as standard command syntax:
|
||||||
|
|
||||||
''COMMAND [options] <params>'' # Like 'cat -A file.txt'
|
''COMMAND [options] <params>'' # Like 'cat -A file.txt'
|
||||||
|
|
||||||
See simple option parsing code below. It's not that flexible. It
|
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
|
doesn't auto-interpret combined options (-fu USER) but it works and is
|
||||||
a good rudimentary way to parse your arguments.
|
a good rudimentary way to parse your arguments.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# Keeping options in alphabetical order makes it easy to add more.
|
# Keeping options in alphabetical order makes it easy to add more.
|
||||||
|
|
||||||
@ -327,7 +327,7 @@ do
|
|||||||
shift
|
shift
|
||||||
break;
|
break;
|
||||||
-*)
|
-*)
|
||||||
echo "Error: Unknown option: $1" >&2
|
echo "Error: Unknown option: $1" >&2
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
*) # No more options
|
*) # No more options
|
||||||
@ -337,7 +337,7 @@ do
|
|||||||
done
|
done
|
||||||
|
|
||||||
# End of file
|
# End of file
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== Filter unwanted options with a wrapper script ====
|
==== Filter unwanted options with a wrapper script ====
|
||||||
|
|
||||||
@ -347,7 +347,7 @@ positional parameters and builds a filtered array consisting of them, then
|
|||||||
calls ''ls'' with the new option set. It also respects the ''--''
|
calls ''ls'' with the new option set. It also respects the ''--''
|
||||||
as "end of options" for ''ls'' and doesn't change anything after it:
|
as "end of options" for ''ls'' and doesn't change anything after it:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# simple ls(1) wrapper that doesn't allow the -a option
|
# simple ls(1) wrapper that doesn't allow the -a option
|
||||||
@ -389,7 +389,7 @@ do
|
|||||||
done
|
done
|
||||||
|
|
||||||
/bin/ls "${options[@]}"
|
/bin/ls "${options[@]}"
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== Using getopts ====
|
==== Using getopts ====
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
====== Bash and the process tree ======
|
====== Bash and the process tree ======
|
||||||
|
|
||||||
{{keywords>bash shell scripting processes pipes variables environment}}
|
{{keywords>bash shell scripting processes pipes variables environment}}
|
||||||
|
|
||||||
===== The process tree =====
|
===== The process tree =====
|
||||||
|
|
||||||
@ -11,7 +11,7 @@ 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.
|
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.
|
||||||
|
|
||||||
**__Attention:__** 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'':
|
**__Attention:__** 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'':
|
||||||
<code>export MYVAR</code>
|
<code>export MYVAR</code>
|
||||||
Common system variables like [[syntax:shellvars#PATH|PATH]] or [[syntax:shellvars#HOME|HOME]] are usually part of the environment (as set by login scripts or programs).
|
Common system variables like [[syntax:shellvars#PATH|PATH]] or [[syntax:shellvars#HOME|HOME]] are usually part of the environment (as set by login scripts or programs).
|
||||||
|
|
||||||
===== Executing programs =====
|
===== Executing programs =====
|
||||||
@ -20,9 +20,9 @@ All the diagrams of the process tree use names like "''xterm''" or &qu
|
|||||||
|
|
||||||
Let's take a short look at what happens when you "execute a program" from the Bash prompt, a program like "ls":
|
Let's take a short look at what happens when you "execute a program" from the Bash prompt, a program like "ls":
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
$ ls
|
$ ls
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Bash will now perform **two steps**:
|
Bash will now perform **two steps**:
|
||||||
* It will make a copy of itself
|
* It will make a copy of itself
|
||||||
@ -30,13 +30,13 @@ Bash will now perform **two steps**:
|
|||||||
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**.
|
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...
|
For a short moment, you have a process tree that might look like this...
|
||||||
<code>
|
<code>
|
||||||
xterm ----- bash ----- bash(copy)
|
xterm ----- bash ----- bash(copy)
|
||||||
</code>
|
</code>
|
||||||
...and after the "second Bash" (the copy) replaces itself with the ''ls'' program (the copy execs it), it might look like
|
...and after the "second Bash" (the copy) replaces itself with the ''ls'' program (the copy execs it), it might look like
|
||||||
<code>
|
<code>
|
||||||
xterm ----- bash ----- ls
|
xterm ----- bash ----- ls
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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'').
|
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'').
|
||||||
|
|
||||||
@ -46,16 +46,16 @@ __**What is so important about it?**__ In our example, what the program ''ls'' d
|
|||||||
|
|
||||||
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'':
|
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'':
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
$ ls | grep myfile
|
$ ls | grep myfile
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
It results in a tree like this:
|
It results in a tree like this:
|
||||||
<code>
|
<code>
|
||||||
+-- ls
|
+-- ls
|
||||||
xterm ----- bash --|
|
xterm ----- bash --|
|
||||||
+-- grep
|
+-- grep
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
@ -63,32 +63,32 @@ __**How is that related to shell programming?!?**__
|
|||||||
|
|
||||||
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:
|
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:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
counter=0
|
counter=0
|
||||||
|
|
||||||
cat /etc/passwd | while read; do ((counter++)); done
|
cat /etc/passwd | while read; do ((counter++)); done
|
||||||
echo "Lines: $counter"
|
echo "Lines: $counter"
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
What? It's 0? Yes! The number of lines might not be 0, but the variable ''$counter'' still is 0. Why?
|
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:
|
Remember the diagram from above? Rewriting it a bit, we have:
|
||||||
<code>
|
<code>
|
||||||
+-- cat /etc/passwd
|
+-- cat /etc/passwd
|
||||||
xterm ----- bash --|
|
xterm ----- bash --|
|
||||||
+-- bash (while read; do ((counter++)); done)
|
+-- bash (while read; do ((counter++)); done)
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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!
|
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!
|
||||||
|
|
||||||
__**So, how do we count the lines?**__
|
__**So, how do we count the lines?**__
|
||||||
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:
|
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:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
counter=0
|
counter=0
|
||||||
|
|
||||||
while read; do ((counter++)); done </etc/passwd
|
while read; do ((counter++)); done </etc/passwd
|
||||||
echo "Lines: $counter"
|
echo "Lines: $counter"
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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.
|
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 =====
|
===== Actions that create a subshell =====
|
||||||
@ -102,11 +102,11 @@ As shown above, Bash will create subprocesses everytime it executes commands. Th
|
|||||||
But if your command is a subprocess that sets variables you want to use in your main script, that won't work.
|
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:
|
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:
|
||||||
<code>
|
<code>
|
||||||
source ./myvariables.sh
|
source ./myvariables.sh
|
||||||
# equivalent to:
|
# equivalent to:
|
||||||
. ./myvariables.sh
|
. ./myvariables.sh
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== Pipes ====
|
==== Pipes ====
|
||||||
|
|
||||||
@ -114,15 +114,15 @@ The last big section was about pipes, so no example here.
|
|||||||
==== Explicit subshell ====
|
==== Explicit subshell ====
|
||||||
|
|
||||||
If you group commands by enclosing them in parentheses, these commands are run inside a subshell:
|
If you group commands by enclosing them in parentheses, these commands are run inside a subshell:
|
||||||
<code>(echo PASSWD follows; cat /etc/passwd; echo GROUP follows; cat /etc/group) >output.txt</code>
|
<code>(echo PASSWD follows; cat /etc/passwd; echo GROUP follows; cat /etc/group) >output.txt</code>
|
||||||
==== Command substitution ====
|
==== Command substitution ====
|
||||||
|
|
||||||
With [[syntax:expansion:cmdsubst | command substitution]] 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:
|
With [[syntax:expansion:cmdsubst | command substitution]] 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:
|
||||||
<code>number_of_users=$(cat /etc/passwd | wc -l)</code>
|
<code>number_of_users=$(cat /etc/passwd | wc -l)</code>
|
||||||
Note that, in this example, a second subshell was created by using a pipe in the command substitution:
|
Note that, in this example, a second subshell was created by using a pipe in the command substitution:
|
||||||
<code>
|
<code>
|
||||||
+-- cat /etc/passwd
|
+-- cat /etc/passwd
|
||||||
xterm ----- bash ----- bash (cmd. subst.) --|
|
xterm ----- bash ----- bash (cmd. subst.) --|
|
||||||
+-- wc -l
|
+-- wc -l
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
|
@ -24,16 +24,16 @@ Speaking of hard-tabs: Avoid them if possible. They only make trouble. I can ima
|
|||||||
Whenever you need to break lines of long code, you should follow one of these two rules:
|
Whenever you need to break lines of long code, you should follow one of these two rules:
|
||||||
|
|
||||||
__**Indention using command width:**__
|
__**Indention using command width:**__
|
||||||
<code>
|
<code>
|
||||||
activate some_very_long_option \
|
activate some_very_long_option \
|
||||||
some_other_option
|
some_other_option
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
__**Indention using two spaces:**__
|
__**Indention using two spaces:**__
|
||||||
<code>
|
<code>
|
||||||
activate some_very_long_option \
|
activate some_very_long_option \
|
||||||
some_other_option
|
some_other_option
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Personally, with some exceptions, I prefer the first form because it supports the visual impression of "these belong together".
|
Personally, with some exceptions, I prefer the first form because it supports the visual impression of "these belong together".
|
||||||
|
|
||||||
@ -49,16 +49,16 @@ I'm used to (not all points apply to all compound commands, just pick the basic
|
|||||||
|
|
||||||
What?! Well, here again:
|
What?! Well, here again:
|
||||||
== Symbolic ==
|
== Symbolic ==
|
||||||
<code>
|
<code>
|
||||||
HEAD_KEYWORD parameters; BODY_BEGIN
|
HEAD_KEYWORD parameters; BODY_BEGIN
|
||||||
BODY_COMMANDS
|
BODY_COMMANDS
|
||||||
BODY_END
|
BODY_END
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
== if/then/elif/else ==
|
== 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:
|
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:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
if ...; then
|
if ...; then
|
||||||
...
|
...
|
||||||
elif ...; then
|
elif ...; then
|
||||||
@ -66,28 +66,28 @@ elif ...; then
|
|||||||
else
|
else
|
||||||
...
|
...
|
||||||
fi
|
fi
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
== for ==
|
== for ==
|
||||||
<code>
|
<code>
|
||||||
for f in /etc/*; do
|
for f in /etc/*; do
|
||||||
...
|
...
|
||||||
done
|
done
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
== while/until ==
|
== while/until ==
|
||||||
<code>
|
<code>
|
||||||
while [[ $answer != [YyNn] ]]; do
|
while [[ $answer != [YyNn] ]]; do
|
||||||
...
|
...
|
||||||
done
|
done
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
== The case construct ==
|
== The case construct ==
|
||||||
|
|
||||||
The ''case'' construct might need a bit more discussion here, since its structure is a bit more complex.
|
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:
|
In general, every new "layer" gets a new indentation level:
|
||||||
<code>
|
<code>
|
||||||
case $input in
|
case $input in
|
||||||
hello)
|
hello)
|
||||||
echo "You said hello"
|
echo "You said hello"
|
||||||
@ -102,7 +102,7 @@ case $input in
|
|||||||
echo "You said something weird..."
|
echo "You said something weird..."
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Some notes:
|
Some notes:
|
||||||
* if not 100% needed, the optional left parenthesis on the pattern is not used
|
* if not 100% needed, the optional left parenthesis on the pattern is not used
|
||||||
@ -129,7 +129,7 @@ Since all reserved variables are ''UPPERCASE'', the safest way is to only use ''
|
|||||||
* if you use ''UPPERCASE'' names, **do not use reserved variable names** (see [[http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08|SUS]] for an incomplete list)
|
* if you use ''UPPERCASE'' names, **do not use reserved variable names** (see [[http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08|SUS]] for an incomplete list)
|
||||||
* if you use ''UPPERCASE'' names, prepend the name with a unique prefix (''MY_'' in the example below)
|
* if you use ''UPPERCASE'' names, prepend the name with a unique prefix (''MY_'' in the example below)
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# the prefix 'MY_'
|
# the prefix 'MY_'
|
||||||
@ -138,7 +138,7 @@ MY_LOG_DIRECTORY=/var/adm/
|
|||||||
for file in "$MY_LOG_DIRECTORY"/*; do
|
for file in "$MY_LOG_DIRECTORY"/*; do
|
||||||
echo "Found Logfile: $file"
|
echo "Found Logfile: $file"
|
||||||
done
|
done
|
||||||
</code>
|
</code>
|
||||||
==== Variable initialization ====
|
==== 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).
|
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).
|
||||||
@ -146,11 +146,11 @@ As in C, it's always a good idea to initialize your variables, though, the shell
|
|||||||
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.
|
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**
|
The solution is simple and effective: **Initialize them**
|
||||||
<code>
|
<code>
|
||||||
my_input=""
|
my_input=""
|
||||||
my_array=()
|
my_array=()
|
||||||
my_number=0
|
my_number=0
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
If you do that for every variable you use, then you also have some in-code documentation for them.
|
If you do that for every variable you use, then you also have some in-code documentation for them.
|
||||||
|
|
||||||
@ -159,21 +159,21 @@ If you do that for every variable you use, then you also have some in-code docum
|
|||||||
Unless you are really sure what you're doing, **quote every 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.
|
There are some cases where this isn't needed from a technical point of view, e.g.
|
||||||
* inside ''<nowiki>[[ ... ]]</nowiki>'' (other than the RHS of the ''=='', ''!='', and ''=~'' operators)
|
* inside ''<nowiki>[[ ... ]]</nowiki>'' (other than the RHS of the ''=='', ''!='', and ''=~'' operators)
|
||||||
* the parameter (''WORD'') in ''case $WORD in ....''
|
* the parameter (''WORD'') in ''case $WORD in ....''
|
||||||
* variable assignment: ''VAR=$WORD''
|
* variable assignment: ''VAR=$WORD''
|
||||||
|
|
||||||
But quoting these is never a mistake. If you quote every parameter expansion, you'll be safe.
|
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.
|
If you need to parse a parameter as a list of words, you can't quote, of course, e.g.
|
||||||
<code>
|
<code>
|
||||||
list="one two three"
|
list="one two three"
|
||||||
|
|
||||||
# you MUST NOT quote $list here
|
# you MUST NOT quote $list here
|
||||||
for word in $list; do
|
for word in $list; do
|
||||||
...
|
...
|
||||||
done
|
done
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== Function names ====
|
==== Function names ====
|
||||||
|
|
||||||
@ -206,7 +206,7 @@ Avoid it, unless absolutely neccesary:
|
|||||||
|
|
||||||
The basic structure of a script simply reads:
|
The basic structure of a script simply reads:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
#!SHEBANG
|
#!SHEBANG
|
||||||
|
|
||||||
CONFIGURATION_VARIABLES
|
CONFIGURATION_VARIABLES
|
||||||
@ -214,7 +214,7 @@ CONFIGURATION_VARIABLES
|
|||||||
FUNCTION_DEFINITIONS
|
FUNCTION_DEFINITIONS
|
||||||
|
|
||||||
MAIN_CODE
|
MAIN_CODE
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== The shebang ====
|
==== The shebang ====
|
||||||
|
|
||||||
@ -241,11 +241,11 @@ Unless there are reasons not to, all function definitions should be declared bef
|
|||||||
Since a function isn't parsed before it is executed, you usually don't have to ensure they're in a specific order.
|
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 [[syntax:ccmd:grouping_plain | grouping compound command]]):
|
The portable form of the function definition should be used, without the ''function'' keyword (here using the [[syntax:ccmd:grouping_plain | grouping compound command]]):
|
||||||
<code>
|
<code>
|
||||||
getargs() {
|
getargs() {
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
@ -260,22 +260,22 @@ Speaking about the command grouping in function definitions using ''{ ...; }'':
|
|||||||
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.
|
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:
|
Example:
|
||||||
<code>
|
<code>
|
||||||
my_needed_commands="sed awk lsof who"
|
my_needed_commands="sed awk lsof who"
|
||||||
|
|
||||||
missing_counter=0
|
missing_counter=0
|
||||||
for needed_command in $my_needed_commands; do
|
for needed_command in $my_needed_commands; do
|
||||||
if ! hash "$needed_command" >/dev/null 2>&1; then
|
if ! hash "$needed_command" >/dev/null 2>&1; then
|
||||||
printf "Command not found in PATH: %s\n" "$needed_command" >&2
|
printf "Command not found in PATH: %s\n" "$needed_command" >&2
|
||||||
((missing_counter++))
|
((missing_counter++))
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
if ((missing_counter > 0)); then
|
if ((missing_counter > 0)); then
|
||||||
printf "Minimum %d commands are missing in PATH, aborting\n" "$missing_counter" >&2
|
printf "Minimum %d commands are missing in PATH, aborting\n" "$missing_counter" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== Exit meaningfully ====
|
==== Exit meaningfully ====
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
====== Terminal codes (ANSI/VT100) introduction ======
|
====== Terminal codes (ANSI/VT100) introduction ======
|
||||||
|
|
||||||
{{keywords>bash shell scripting colors cursor control vt100 ansi}}
|
{{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.
|
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.
|
||||||
|
|
||||||
@ -28,7 +28,7 @@ If I couldn't find a matching ANSI escape, you'll see a :?: as the code. Feel f
|
|||||||
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!**
|
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)
|
All codes that can be used with ''tput'' can be found in terminfo(5). (on OpenBSD at least)
|
||||||
See [[http://www.openbsd.org/cgi-bin/man.cgi?query=terminfo&apropos=0&sektion=5&manpath=OpenBSD+Current&arch=i386&format=html|OpenBSD's terminfo(5)]] under the __Capabilities__ section. The //cap-name// is the code to use with tput. A description of each code is also provided.
|
See [[http://www.openbsd.org/cgi-bin/man.cgi?query=terminfo&apropos=0&sektion=5&manpath=OpenBSD+Current&arch=i386&format=html|OpenBSD's terminfo(5)]] under the __Capabilities__ section. The //cap-name// is the code to use with tput. A description of each code is also provided.
|
||||||
|
|
||||||
==== General useful ASCII codes ====
|
==== General useful ASCII codes ====
|
||||||
|
|
||||||
@ -42,13 +42,13 @@ The **Ctrl-Key** representation is simply associating the non-printable characte
|
|||||||
|''VT''|11|013|0x0B|''\v''|''^K''|Vertical TAB|
|
|''VT''|11|013|0x0B|''\v''|''^K''|Vertical TAB|
|
||||||
|''FF''|12|014|0x0C|''\f''|''^L''|Formfeed (also: New page ''NP'')|
|
|''FF''|12|014|0x0C|''\f''|''^L''|Formfeed (also: New page ''NP'')|
|
||||||
|''CR''|13|015|0x0D|''\r''|''^M''|Carriage return|
|
|''CR''|13|015|0x0D|''\r''|''^M''|Carriage return|
|
||||||
|''ESC''|27|033|0x1B|''<none>''|''^[''|Escape character|
|
|''ESC''|27|033|0x1B|''<none>''|''^[''|Escape character|
|
||||||
|''DEL''|127|177|0x7F|''<none>''|''<none>''|Delete character|
|
|''DEL''|127|177|0x7F|''<none>''|''<none>''|Delete character|
|
||||||
|
|
||||||
==== Cursor handling ====
|
==== Cursor handling ====
|
||||||
|
|
||||||
^ANSI^terminfo equivalent^Description^
|
^ANSI^terminfo equivalent^Description^
|
||||||
|''[ <X> ; <Y> H''\\ ''[ <X> ; <Y> f''|''cup <X> <Y>''|Home-positioning to ''X'' and ''Y'' coordinates\\ :!: it seems that ANSI uses 1-1 as home while ''tput'' uses 0-0|
|
|''[ <X> ; <Y> H''\\ ''[ <X> ; <Y> f''|''cup <X> <Y>''|Home-positioning to ''X'' and ''Y'' coordinates\\ :!: it seems that ANSI uses 1-1 as home while ''tput'' uses 0-0|
|
||||||
|''[ H''|''home''|Move cursor to home position (0-0)|
|
|''[ H''|''home''|Move cursor to home position (0-0)|
|
||||||
|''7''|''sc''|Save current cursor position|
|
|''7''|''sc''|Save current cursor position|
|
||||||
|''8''|''rc''|Restore saved cursor position|
|
|''8''|''rc''|Restore saved cursor position|
|
||||||
@ -108,7 +108,7 @@ The **Ctrl-Key** representation is simply associating the non-printable characte
|
|||||||
Used capabilities: ''smcup'', ''rmcup''
|
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:
|
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:
|
||||||
<code>
|
<code>
|
||||||
# save, clear screen
|
# save, clear screen
|
||||||
tput smcup
|
tput smcup
|
||||||
clear
|
clear
|
||||||
@ -119,27 +119,27 @@ read -n1 -p "Press any key to continue..."
|
|||||||
|
|
||||||
# restore
|
# restore
|
||||||
tput rmcup
|
tput rmcup
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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:
|
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:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
echo -e '\033[?47h' # save screen
|
echo -e '\033[?47h' # save screen
|
||||||
echo -e '\033[?47l' # restore screen
|
echo -e '\033[?47l' # restore screen
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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:
|
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:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
export LESS=X
|
export LESS=X
|
||||||
less /path/to/file
|
less /path/to/file
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Similarly, ''vim'' can be configured not to "restore" the screen by adding the following to your ''~/.vimrc'':
|
Similarly, ''vim'' can be configured not to "restore" the screen by adding the following to your ''~/.vimrc'':
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
set t_ti= t_te=
|
set t_ti= t_te=
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -154,38 +154,38 @@ The Virtual Terminal implemented in the Linux kernel supports only 16 colors, an
|
|||||||
|
|
||||||
==== Hardcoded colors ====
|
==== Hardcoded colors ====
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
printf '%b\n' 'It is \033[31mnot\033[39m intelligent to use \033[32mhardcoded ANSI\033[39m codes!'
|
printf '%b\n' 'It is \033[31mnot\033[39m intelligent to use \033[32mhardcoded ANSI\033[39m codes!'
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== Colors using tput ====
|
==== Colors using tput ====
|
||||||
|
|
||||||
__Directly inside the echo:__
|
__Directly inside the echo:__
|
||||||
<code>
|
<code>
|
||||||
echo "TPUT is a $(tput setaf 2)nice$(tput setaf 9) and $(tput setaf 5)user friendly$(tput setaf 9) terminal capability database."
|
echo "TPUT is a $(tput setaf 2)nice$(tput setaf 9) and $(tput setaf 5)user friendly$(tput setaf 9) terminal capability database."
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
__With preset variables:__
|
__With preset variables:__
|
||||||
<code>
|
<code>
|
||||||
COL_NORM="$(tput setaf 9)"
|
COL_NORM="$(tput setaf 9)"
|
||||||
COL_RED="$(tput setaf 1)"
|
COL_RED="$(tput setaf 1)"
|
||||||
COL_GREEN="$(tput setaf 2)"
|
COL_GREEN="$(tput setaf 2)"
|
||||||
echo "It's ${COL_RED}red${COL_NORM} and ${COL_GREEN}green${COL_NORM} - have you seen?"
|
echo "It's ${COL_RED}red${COL_NORM} and ${COL_GREEN}green${COL_NORM} - have you seen?"
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== Misc ====
|
==== Misc ====
|
||||||
|
|
||||||
__HOME function__
|
__HOME function__
|
||||||
<code>
|
<code>
|
||||||
home() {
|
home() {
|
||||||
# yes, actually not much shorter ;-)
|
# yes, actually not much shorter ;-)
|
||||||
tput home
|
tput home
|
||||||
}
|
}
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== Silly but nice effect ====
|
==== Silly but nice effect ====
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
DATA[0]=" _/ _/ _/ _/ "
|
DATA[0]=" _/ _/ _/ _/ "
|
||||||
@ -217,23 +217,23 @@ clear
|
|||||||
|
|
||||||
while :; do
|
while :; do
|
||||||
|
|
||||||
for ((c=1; c <= 7; c++)); do
|
for ((c=1; c <= 7; c++)); do
|
||||||
tput setaf $c
|
tput setaf $c
|
||||||
for ((x=0; x<${#DATA[0]}; x++)); do
|
for ((x=0; x<${#DATA[0]}; x++)); do
|
||||||
for ((y=0; y<=4; y++)); do
|
for ((y=0; y<=4; y++)); do
|
||||||
draw_char $x $y
|
draw_char $x $y
|
||||||
done
|
done
|
||||||
done
|
done
|
||||||
done
|
done
|
||||||
|
|
||||||
done
|
done
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== Mandelbrot set ====
|
==== Mandelbrot set ====
|
||||||
|
|
||||||
This is a slightly modified version of Charles Cooke's colorful Mandelbrot plot scripts ([[http://earth.gkhs.net/ccooke/shell.html | original w/ screenshot]]) -- ungolfed, optimized a bit, and without hard-coded terminal escapes. The ''colorBox'' function is [[http://en.wikipedia.org/wiki/Memoization | memoized]] 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.
|
This is a slightly modified version of Charles Cooke's colorful Mandelbrot plot scripts ([[http://earth.gkhs.net/ccooke/shell.html | original w/ screenshot]]) -- ungolfed, optimized a bit, and without hard-coded terminal escapes. The ''colorBox'' function is [[http://en.wikipedia.org/wiki/Memoization | memoized]] 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.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
#!/usr/bin/env ksh
|
#!/usr/bin/env ksh
|
||||||
|
|
||||||
# Charles Cooke's 16-color Mandelbrot
|
# Charles Cooke's 16-color Mandelbrot
|
||||||
@ -242,11 +242,11 @@ This is a slightly modified version of Charles Cooke's colorful Mandelbrot plot
|
|||||||
|
|
||||||
function doBash {
|
function doBash {
|
||||||
typeset P Q X Y a b c i v x y
|
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 ((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 ((;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 :
|
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
|
done
|
||||||
colorBox $((i<99?i%16:0))
|
colorBox $((i<99?i%16:0))
|
||||||
done
|
done
|
||||||
echo
|
echo
|
||||||
done
|
done
|
||||||
@ -256,12 +256,12 @@ function doKsh {
|
|||||||
integer i
|
integer i
|
||||||
float a b c x=2.2 y=-1.05 X=3.2/cols Y=2.1/lines
|
float a b c x=2.2 y=-1.05 X=3.2/cols Y=2.1/lines
|
||||||
while
|
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 :
|
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
|
done
|
||||||
. colorBox $((i<99?i%16:0))
|
. colorBox $((i<99?i%16:0))
|
||||||
if ((x<1?!(x+=X):(y+=Y,x=-2.2))); then
|
if ((x<1?!(x+=X):(y+=Y,x=-2.2))); then
|
||||||
print
|
print
|
||||||
((y<1.05))
|
((y<1.05))
|
||||||
fi
|
fi
|
||||||
do :
|
do :
|
||||||
done
|
done
|
||||||
@ -277,5 +277,5 @@ unset -v lastclr
|
|||||||
typeset -a colrs
|
typeset -a colrs
|
||||||
trap 'tput sgr0; echo' EXIT
|
trap 'tput sgr0; echo' EXIT
|
||||||
${KSH_VERSION+. doKsh} ${BASH_VERSION+doBash}
|
${KSH_VERSION+. doKsh} ${BASH_VERSION+doBash}
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
====== List of Bash online-tutorials ======
|
====== List of Bash online-tutorials ======
|
||||||
|
|
||||||
{{keywords>bash shell scripting review tutorial list recommendation}}
|
{{keywords>bash shell scripting review tutorial list recommendation}}
|
||||||
|
|
||||||
Here's a list of some Bash tutorials.
|
Here's a list of some Bash tutorials.
|
||||||
|
|
||||||
@ -32,38 +32,38 @@ Please **contact** me
|
|||||||
|
|
||||||
|
|
||||||
The recommendation-indicator "REC" used below is a number between 1 and 10 visualized as a bar:
|
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=0> Not recommended to read, at best, don't click the link.
|
||||||
* ...
|
* ...
|
||||||
* <progress=50> Use it with care!
|
* <progress=50> Use it with care!
|
||||||
* ...
|
* ...
|
||||||
* <progress=100> **The perfect godlike tutorial** (I doubt I'll ever find it)
|
* <progress=100> **The perfect godlike tutorial** (I doubt I'll ever find it)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
^ Name (Links to review below)j ^ Weblink\ \ \ ^ REC\\ indicator ^ Comments ^
|
^ Name (Links to review below)j ^ Weblink\ \ \ ^ REC\\ indicator ^ Comments ^
|
||||||
| Bash\ guide on Greg's wiki | [[http://mywiki.wooledge.org/BashGuide|click]] ([[http://guide.bash.academy|new revision draft]]) | <progress=90> | This guide teaches modern stuff and good practises. I recommend learning from it. It was written by the guys in ''#bash''IRC channel on Freenode (mainly ''lhunath''), because there are so many bad tutorials out there. |
|
| Bash\ guide on Greg's wiki | [[http://mywiki.wooledge.org/BashGuide|click]] ([[http://guide.bash.academy|new revision draft]]) | <progress=90> | This guide teaches modern stuff and good practises. I recommend learning from it. It was written by the guys in ''#bash''IRC channel on Freenode (mainly ''lhunath''), because there are so many bad tutorials out there. |
|
||||||
| Steve Parker's shell scripting guide | [[http://steve-parker.org/sh/intro.shtml|click]] | <progress=90> | Very good (not only Bash) shell scripting guide. Teaches good practices, gives background information. |
|
| Steve Parker's shell scripting guide | [[http://steve-parker.org/sh/intro.shtml|click]] | <progress=90> | Very good (not only Bash) shell scripting guide. Teaches good practices, gives background information. |
|
||||||
| Bash Guide for Beginners ([[#rv_bgb|review]]) | [[http://tldp.org/LDP/Bash-Beginners-Guide/html/|click]] | <progress=80> | Good introduction that really requires no previous knowledge, also covers the most important unix utilities |
|
| Bash Guide for Beginners ([[#rv_bgb|review]]) | [[http://tldp.org/LDP/Bash-Beginners-Guide/html/|click]] | <progress=80> | Good introduction that really requires no previous knowledge, also covers the most important unix utilities |
|
||||||
| Advanced\ Bash\ Scripting\ Guide (ABS) ([[#rv_abs|review]]) | [[http://tldp.org/LDP/abs/html/|click]] | <progress=50> | 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. |
|
| Advanced\ Bash\ Scripting\ Guide (ABS) ([[#rv_abs|review]]) | [[http://tldp.org/LDP/abs/html/|click]] | <progress=50> | 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. |
|
||||||
| IBM developerWorks "Bash\ by\ example" | [[http://www.ibm.com/developerworks/library/l-bash.html|click(1)]]\\ [[http://www.ibm.com/developerworks/library/l-bash2.html|click(2)]]\\ [[http://www.ibm.com/developerworks/library/l-bash3.html|click(3)]] | <progress=80> | Doesn't teach outdated stuff, doesn't tell you wrong things. A good start, though not that detailed. |
|
| IBM developerWorks "Bash\ by\ example" | [[http://www.ibm.com/developerworks/library/l-bash.html|click(1)]]\\ [[http://www.ibm.com/developerworks/library/l-bash2.html|click(2)]]\\ [[http://www.ibm.com/developerworks/library/l-bash3.html|click(3)]] | <progress=80> | Doesn't teach outdated stuff, doesn't tell you wrong things. A good start, though not that detailed. |
|
||||||
| Deadman's | [[http://samrowe.com/wordpress/advancing-in-the-bash-shell/|click]] | <progress=70> | Focus isn't scripting per se. Focus is interactive use and increasing the productivity on the prompt. Teaches some nice features. |
|
| Deadman's | [[http://samrowe.com/wordpress/advancing-in-the-bash-shell/|click]] | <progress=70> | Focus isn't scripting per se. Focus is interactive use and increasing the productivity on the prompt. Teaches some nice features. |
|
||||||
| Bash Shell Programming in Linux (P. Lutus) | [[http://www.arachnoid.com/linux/shell_programming.html|click]] | <progress=70> | Good start. Though there are small bugs. |
|
| Bash Shell Programming in Linux (P. Lutus) | [[http://www.arachnoid.com/linux/shell_programming.html|click]] | <progress=70> | Good start. Though there are small bugs. |
|
||||||
| BASH Help at hypexr.org ([[#rv_hypexrorg|review]]) | [[http://www.hypexr.org/bash_tutorial.php|click]] | <progress=50> | Shows you some nice stuff and links to other ressources. Not a tutorial to learn Bash, though. |
|
| BASH Help at hypexr.org ([[#rv_hypexrorg|review]]) | [[http://www.hypexr.org/bash_tutorial.php|click]] | <progress=50> | Shows you some nice stuff and links to other ressources. Not a tutorial to learn Bash, though. |
|
||||||
| Bash Programming Introduction HowTo (TLDP) ([[#rv_bprogintrohowto|review]]) | [[http://tldp.org/HOWTO/Bash-Prog-Intro-HOWTO.html|click]] | <progress=10> | Absolute crap. Many syntax errors alone. |
|
| Bash Programming Introduction HowTo (TLDP) ([[#rv_bprogintrohowto|review]]) | [[http://tldp.org/HOWTO/Bash-Prog-Intro-HOWTO.html|click]] | <progress=10> | Absolute crap. Many syntax errors alone. |
|
||||||
| Quick guide ([[#rv_qguide|review]]) | [[http://www.panix.com/~elflord/unix/bash-tute.html|click]] | <progress=50> | Usable as a start. Doesn't teach wrong stuff, shows you good practices. |
|
| Quick guide ([[#rv_qguide|review]]) | [[http://www.panix.com/~elflord/unix/bash-tute.html|click]] | <progress=50> | Usable as a start. Doesn't teach wrong stuff, shows you good practices. |
|
||||||
| LinuxCommand.org: Writing shell scripts. ([[#rv_linuxcommand|review]]) incomplete, thus ranking isn't complete | [[http://linuxcommand.org/writing_shell_scripts.php|click]] | <progress=50> | Practise oriented, some mistakes/flaws, but sadly it stops in the middle |
|
| LinuxCommand.org: Writing shell scripts. ([[#rv_linuxcommand|review]]) incomplete, thus ranking isn't complete | [[http://linuxcommand.org/writing_shell_scripts.php|click]] | <progress=50> | Practise oriented, some mistakes/flaws, but sadly it stops in the middle |
|
||||||
| Linux\ Shell\ Scripting\ Tutorial\ v2.0 ([[#rv_linuxscriptv2|review]]) | [[http://bash.cyberciti.biz/guide/Main_Page|click]] | <progress=40> | currently reviewing (the tutorial is also under development) |
|
| Linux\ Shell\ Scripting\ Tutorial\ v2.0 ([[#rv_linuxscriptv2|review]]) | [[http://bash.cyberciti.biz/guide/Main_Page|click]] | <progress=40> | currently reviewing (the tutorial is also under development) |
|
||||||
| linuxconfig.org Bash\ Scripting\ Tutorial ([[#rv_linuxconfig|review]]) | [[http://www.linuxconfig.org/Bash_scripting_Tutorial|click]] | <progress=0> | Teaches many outdated, unstable, undetailed stuff. You won't learn scripting from there. |
|
| linuxconfig.org Bash\ Scripting\ Tutorial ([[#rv_linuxconfig|review]]) | [[http://www.linuxconfig.org/Bash_scripting_Tutorial|click]] | <progress=0> | Teaches many outdated, unstable, undetailed stuff. You won't learn scripting from there. |
|
||||||
| Beginner\ Linux\ Tutorial | [[http://linuxtutorial.todolistme.net|click]] | <progress=60> | A comprehensive introduction to the Linux Command Line including ample examples to make learning easy. |
|
| Beginner\ Linux\ Tutorial | [[http://linuxtutorial.todolistme.net|click]] | <progress=60> | A comprehensive introduction to the Linux Command Line including ample examples to make learning easy. |
|
||||||
| Beginner\ Bash\ Scripting\ Tutorial | [[http://ryanstutorials.net/bash-scripting-tutorial/|click]] | <progress=60> | A beginners guide to Bash scripting under Linux. |
|
| Beginner\ Bash\ Scripting\ Tutorial | [[http://ryanstutorials.net/bash-scripting-tutorial/|click]] | <progress=60> | A beginners guide to Bash scripting under Linux. |
|
||||||
| Linuxcommand.org: The\ Linux\ Command\ Line | [[http://linuxcommand.org/tlcl.php/|click]] | <progress=40> | 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. |
|
| Linuxcommand.org: The\ Linux\ Command\ Line | [[http://linuxcommand.org/tlcl.php/|click]] | <progress=40> | 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. |
|
||||||
===== Detailed reviews =====
|
===== Detailed reviews =====
|
||||||
|
|
||||||
<BOOKMARK:rv_linuxconfig>
|
<BOOKMARK:rv_linuxconfig>
|
||||||
==== linuxconfig.org Bash Scripting Tutorial ====
|
==== linuxconfig.org Bash Scripting Tutorial ====
|
||||||
|
|
||||||
Article link: [[http://www.linuxconfig.org/Bash_scripting_Tutorial]]\\
|
Article link: [[http://www.linuxconfig.org/Bash_scripting_Tutorial]]\\
|
||||||
Discussion link: <nowiki>http://www.linuxconfig.org/Talk:Bash_scripting_Tutorial</nowiki> UPDATE: Discussion page is gone.
|
Discussion link: <nowiki>http://www.linuxconfig.org/Talk:Bash_scripting_Tutorial</nowiki> UPDATE: Discussion page is gone.
|
||||||
|
|
||||||
Though the basic idea is nice, using flash terminal sessions and screenshots, there are many bugs or bad constructs.
|
Though the basic idea is nice, using flash terminal sessions and screenshots, there are many bugs or bad constructs.
|
||||||
|
|
||||||
@ -74,21 +74,21 @@ Some stuff I didn't like there:
|
|||||||
* uses backticks instead of ''$( ... )'' for [[syntax:expansion:cmdsubst|command substitution]]
|
* uses backticks instead of ''$( ... )'' for [[syntax:expansion:cmdsubst|command substitution]]
|
||||||
* incorrectly uses an additional array to store [[scripting:posparams|positional parameters]], disregarding that ''$@'' already is array-like
|
* incorrectly uses an additional array to store [[scripting:posparams|positional parameters]], disregarding that ''$@'' already is array-like
|
||||||
* uses ''echo -e'' and the historical control character ''\c'' instead of modern [[commands:builtin:printf]]
|
* uses ''echo -e'' and the historical control character ''\c'' instead of modern [[commands:builtin:printf]]
|
||||||
* uses ''for a in `seq 1 10`'' instead of a [[syntax:ccmd:c_for|C-like counter loop]] ''for <nowiki>((a=1; a <= 10; a++))</nowiki>''
|
* uses ''for a in `seq 1 10`'' instead of a [[syntax:ccmd:c_for|C-like counter loop]] ''for <nowiki>((a=1; a <= 10; a++))</nowiki>''
|
||||||
* the ''if/else'' stuff looks as if the ''test'' (or ''[ ...]'') command is the only thing Bash can execute and check
|
* 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 [[syntax:expansion:wordsplit|word-splitting]]
|
* a ''for'' loop example that **will** explode on [[syntax:expansion:wordsplit|word-splitting]]
|
||||||
* arithmetic tests (the while/until loop examples) using the historical "old way" with ''test'', not [[syntax:arith_expr|modern arithmetic components]]
|
* arithmetic tests (the while/until loop examples) using the historical "old way" with ''test'', not [[syntax:arith_expr|modern arithmetic components]]
|
||||||
* useless [[syntax:quoting|quoting]] of one-word strings (not that it hurts per se, but it shows that the author has no clue when to use quotes)
|
* useless [[syntax:quoting|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''
|
* 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 <nowiki>http://www.linuxconfig.org/Talk:Bash_scripting_Tutorial</nowiki>, I linked //this article// there. UPDATE: Discussion page is gone.
|
''twkm'' commented some things on their <nowiki>http://www.linuxconfig.org/Talk:Bash_scripting_Tutorial</nowiki>, 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.
|
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.
|
UPDATE: Discussion is available directly below the article. I linked this page, but waiting for moderator approval.
|
||||||
<BOOKMARK:rv_qguide>
|
<BOOKMARK:rv_qguide>
|
||||||
|
|
||||||
==== Quick Guide ====
|
==== Quick Guide ====
|
||||||
|
|
||||||
@ -100,7 +100,7 @@ This article is usable as basic introduction into the Bash world. It doesn't tea
|
|||||||
One point (I **have** to criticize **something** ;-) ):
|
One point (I **have** to criticize **something** ;-) ):
|
||||||
* the article says that there are no [[syntax:ccmd:c_for|C-styled for-loops]] in Bash, this is wrong (maybe the article is for a very old (pre ''2.05b'') Bash version)
|
* the article says that there are no [[syntax:ccmd:c_for|C-styled for-loops]] in Bash, this is wrong (maybe the article is for a very old (pre ''2.05b'') Bash version)
|
||||||
|
|
||||||
<BOOKMARK:rv_bprogintrohowto>
|
<BOOKMARK:rv_bprogintrohowto>
|
||||||
==== Bash Programming Introduction HowTo ====
|
==== Bash Programming Introduction HowTo ====
|
||||||
|
|
||||||
Article link: [[http://tldp.org/HOWTO/Bash-Prog-Intro-HOWTO.html]]\\
|
Article link: [[http://tldp.org/HOWTO/Bash-Prog-Intro-HOWTO.html]]\\
|
||||||
@ -115,7 +115,7 @@ A few points:
|
|||||||
|
|
||||||
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
|
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>
|
<BOOKMARK:rv_hypexrorg>
|
||||||
|
|
||||||
==== BASH Help at hypexr.org ====
|
==== BASH Help at hypexr.org ====
|
||||||
|
|
||||||
@ -127,7 +127,7 @@ The article is usable to step into the shell world. It's not a tutorial per se,
|
|||||||
Only one point:
|
Only one point:
|
||||||
* confusing description of the dotfiles
|
* confusing description of the dotfiles
|
||||||
|
|
||||||
<BOOKMARK:rv_abs>
|
<BOOKMARK:rv_abs>
|
||||||
==== Advanced Bash Scripting guide (ABS) ====
|
==== Advanced Bash Scripting guide (ABS) ====
|
||||||
|
|
||||||
Article link: [[http://tldp.org/LDP/abs/html/]]\\
|
Article link: [[http://tldp.org/LDP/abs/html/]]\\
|
||||||
@ -140,7 +140,7 @@ I don't want to write every point here that disturbs me. In general it's not tha
|
|||||||
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).
|
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>
|
<BOOKMARK:rv_bgb>
|
||||||
|
|
||||||
==== Bash Guide for Beginners ====
|
==== Bash Guide for Beginners ====
|
||||||
|
|
||||||
@ -151,7 +151,7 @@ Good introduction to bash and shell scripting, the guide is fairly complete and
|
|||||||
|
|
||||||
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.
|
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>
|
<BOOKMARK:rv_linuxscriptv2>
|
||||||
==== Linux Shell Scripting Tutorial v2.0 ====
|
==== Linux Shell Scripting Tutorial v2.0 ====
|
||||||
|
|
||||||
Article link: [[http://bash.cyberciti.biz/guide/Main_Page]]\\
|
Article link: [[http://bash.cyberciti.biz/guide/Main_Page]]\\
|
||||||
@ -160,19 +160,19 @@ 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
|
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:
|
This guide has some big problems. It seems to cover a lot of material but has some pretty nasty issues, too. Examples:
|
||||||
* <del>When showing how to echo variables, it shows echo ''$var'' and ''echo ${var}'' mostly without quoting.</del>
|
* <del>When showing how to echo variables, it shows echo ''$var'' and ''echo ${var}'' mostly without quoting.</del>
|
||||||
* Lots of 'test' and not much '['. Not really a "problem" - more a style thing. But...
|
* 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 =.
|
* test and [ and great, but what happened to \[\[? And test == is not used to check if two strings are equal. You use =.
|
||||||
* <del>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</del>
|
* <del>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</del>
|
||||||
* 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 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 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 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 { }
|
||||||
* <del>the function definition focuses on the "function" keyword, which is the worst way</del>
|
* <del>the function definition focuses on the "function" keyword, which is the worst way</del>
|
||||||
* The initial example page for pipes seems like a good list of examples of parsing things-you-do-NOT-want-to-parse
|
* The initial example page for pipes seems like a good list of examples of parsing things-you-do-NOT-want-to-parse
|
||||||
* <del>the page explaining special parameters misses $@, which is very important</del>
|
* <del>the page explaining special parameters misses $@, which is very important</del>
|
||||||
* <del>the page(s) about shell variables and environment doesn't even mention "environment" so far</del> some improvement.
|
* <del>the page(s) about shell variables and environment doesn't even mention "environment" so far</del> some improvement.
|
||||||
* <del>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</del> some improvement, but the quoting page is still misleading
|
* <del>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</del> some improvement, but the quoting page is still misleading
|
||||||
* <del>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!**</del>
|
* <del>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!**</del>
|
||||||
* etc. etc. etc. (nearly endless list, sadly)
|
* etc. etc. etc. (nearly endless list, sadly)
|
||||||
|
|
||||||
**Conclusion**
|
**Conclusion**
|
||||||
@ -186,7 +186,7 @@ The tutorial is under development. It improves here and there. But the code styl
|
|||||||
**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.
|
**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>
|
<BOOKMARK:rv_linuxcommand>
|
||||||
|
|
||||||
==== LinuxCommand.org: Writing shell scripts. ====
|
==== LinuxCommand.org: Writing shell scripts. ====
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
====== Small code snippets ======
|
====== Small code snippets ======
|
||||||
|
|
||||||
{{keywords>bash shell scripting code download snippet example}}
|
{{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.
|
These snippets are **not** meant as HowTo or Tutorial or FAQ. Mostly they are only a line of code and a short comment.
|
||||||
|
|
||||||
|
90
start.md
90
start.md
@ -3,21 +3,21 @@
|
|||||||
|
|
||||||
====== The Bash Hackers Wiki ======
|
====== The Bash Hackers Wiki ======
|
||||||
|
|
||||||
{{keywords>bash shell linux scripting}}
|
{{keywords>bash shell linux scripting}}
|
||||||
|
|
||||||
<WRAP center round box 90%>
|
<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 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 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.
|
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/>.
|
You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
[[wishes | What would YOU like to see here?]] (outdated and locked, please use the discussions)
|
[[wishes | What would YOU like to see here?]] (outdated and locked, please use the discussions)
|
||||||
|
|
||||||
**Stranger!** [[http://wiki.bash-hackers.org/start&do=register | Feel free to register]] and comment or edit the contents. There is a [[meta:need_love|Bash Hackers Wiki needs love page]] that lists some things to do. The registration is only there to prevent SPAM.
|
**Stranger!** [[http://wiki.bash-hackers.org/start&do=register | Feel free to register]] and comment or edit the contents. There is a [[meta:need_love|Bash Hackers Wiki needs love page]] that lists some things to do. The registration is only there to prevent SPAM.
|
||||||
</WRAP>
|
</WRAP>
|
||||||
|
|
||||||
===== Scripting and general information =====
|
===== Scripting and general information =====
|
||||||
* [[bash4 | Bash v4 - a rough overview]] (unmaintained, since Bash 4 is more or less standard)
|
* [[bash4 | Bash v4 - a rough overview]] (unmaintained, since Bash 4 is more or less standard)
|
||||||
@ -70,7 +70,7 @@ See also [[misc:readthesourceluke|some Bash source code excerpts]].
|
|||||||
* [[syntax:shellvars | Special parameters and shell variables]]
|
* [[syntax:shellvars | Special parameters and shell variables]]
|
||||||
* [[syntax:arrays | Arrays]]
|
* [[syntax:arrays | Arrays]]
|
||||||
|
|
||||||
<WRAP column 40%>
|
<WRAP column 40%>
|
||||||
//**__Compound commands__**//
|
//**__Compound commands__**//
|
||||||
|
|
||||||
^**[[syntax:ccmd:intro | Compound commands overview]]** ^^
|
^**[[syntax:ccmd:intro | Compound commands overview]]** ^^
|
||||||
@ -78,20 +78,20 @@ See also [[misc:readthesourceluke|some Bash source code excerpts]].
|
|||||||
|''{ ...; }''|[[syntax:ccmd:grouping_plain | command grouping]]|
|
|''{ ...; }''|[[syntax:ccmd:grouping_plain | command grouping]]|
|
||||||
|''( ... )''|[[syntax:ccmd:grouping_subshell | command grouping in a subshell]]|
|
|''( ... )''|[[syntax:ccmd:grouping_subshell | command grouping in a subshell]]|
|
||||||
^Conditionals ^^
|
^Conditionals ^^
|
||||||
|''<nowiki>[[ ... ]]</nowiki>''|[[syntax:ccmd:conditional_expression | conditional expression]]|
|
|''<nowiki>[[ ... ]]</nowiki>''|[[syntax:ccmd:conditional_expression | conditional expression]]|
|
||||||
|''if ...; then ...; fi''|[[syntax:ccmd:if_clause | conditional branching]]|
|
|''if ...; then ...; fi''|[[syntax:ccmd:if_clause | conditional branching]]|
|
||||||
|''case ... esac''|[[syntax:ccmd:case | pattern-based branching]]|
|
|''case ... esac''|[[syntax:ccmd:case | pattern-based branching]]|
|
||||||
^Loops ^^
|
^Loops ^^
|
||||||
|''for word in ...; do ...; done''|[[syntax:ccmd:classic_for | classic for-loop]]|
|
|''for word in ...; do ...; done''|[[syntax:ccmd:classic_for | classic for-loop]]|
|
||||||
|''<nowiki>for ((x=1; x<=10; x++)); do ...; done</nowiki>''|[[syntax:ccmd:c_for | C-style for-loop]]|
|
|''<nowiki>for ((x=1; x<=10; x++)); do ...; done</nowiki>''|[[syntax:ccmd:c_for | C-style for-loop]]|
|
||||||
|''while ...; do ...; done''|[[syntax:ccmd:while_loop | while loop]]|
|
|''while ...; do ...; done''|[[syntax:ccmd:while_loop | while loop]]|
|
||||||
|''until ...; do ...; done''|[[syntax:ccmd:until_loop | until loop]]|
|
|''until ...; do ...; done''|[[syntax:ccmd:until_loop | until loop]]|
|
||||||
^Misc ^^
|
^Misc ^^
|
||||||
|''<nowiki>(( ... ))</nowiki>''|[[syntax:ccmd:arithmetic_eval | arithmetic evaluation]]|
|
|''<nowiki>(( ... ))</nowiki>''|[[syntax:ccmd:arithmetic_eval | arithmetic evaluation]]|
|
||||||
|''select word in ...; do ...; done''|[[syntax:ccmd:user_select | user selections]]|
|
|''select word in ...; do ...; done''|[[syntax:ccmd:user_select | user selections]]|
|
||||||
</WRAP>
|
</WRAP>
|
||||||
|
|
||||||
<WRAP column 40%>
|
<WRAP column 40%>
|
||||||
//**__Expansions and substitutions__**//
|
//**__Expansions and substitutions__**//
|
||||||
|
|
||||||
^**[[syntax:expansion:intro | Introduction to expansions and substitutions]]** ^^
|
^**[[syntax:expansion:intro | Introduction to expansions and substitutions]]** ^^
|
||||||
@ -99,19 +99,19 @@ See also [[misc:readthesourceluke|some Bash source code excerpts]].
|
|||||||
|''~/ ~root/''|[[syntax:expansion:tilde | Tilde expansion]]|
|
|''~/ ~root/''|[[syntax:expansion:tilde | Tilde expansion]]|
|
||||||
|''$FOO ${BAR%.mp3}''|[[syntax:pe | Parameter expansion]]|
|
|''$FOO ${BAR%.mp3}''|[[syntax:pe | Parameter expansion]]|
|
||||||
|''`command` $(command)''|[[syntax:expansion:cmdsubst | Command substitution]]|
|
|''`command` $(command)''|[[syntax:expansion:cmdsubst | Command substitution]]|
|
||||||
|''<(command) >(command)''|[[syntax:expansion:proc_subst | Process substitution]]|
|
|''<(command) >(command)''|[[syntax:expansion:proc_subst | Process substitution]]|
|
||||||
|''<nowiki>$((1 + 2 + 3)) $[4 + 5 + 6]</nowiki>''|[[syntax:expansion:arith | Arithmetic expansion]]|
|
|''<nowiki>$((1 + 2 + 3)) $[4 + 5 + 6]</nowiki>''|[[syntax:expansion:arith | Arithmetic expansion]]|
|
||||||
|''Hello <nowiki><---></nowiki> Word!''|[[syntax:expansion:wordsplit | Word splitting]]|
|
|''Hello <nowiki><---></nowiki> Word!''|[[syntax:expansion:wordsplit | Word splitting]]|
|
||||||
|''/data/*-av/*.mp?''|[[syntax:expansion:globs | Pathname expansion]]|
|
|''/data/*-av/*.mp?''|[[syntax:expansion:globs | Pathname expansion]]|
|
||||||
</WRAP>
|
</WRAP>
|
||||||
<WRAP clear></WRAP>
|
<WRAP clear></WRAP>
|
||||||
|
|
||||||
===== Builtin Commands =====
|
===== 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.
|
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%>
|
<WRAP column 46%>
|
||||||
^ Declaration commands\\ <wrap center round lo todo box 80%>Commands that set and query attributes/types, and manipulate simple datastructures.</wrap> ^^ Alt ^ Type ^
|
^ Declaration commands\\ <wrap center round lo todo box 80%>Commands that set and query attributes/types, and manipulate simple datastructures.</wrap> ^^ Alt ^ Type ^
|
||||||
|[[commands:builtin:declare | declare]]|Display or set shell variables or functions along with attributes.| ''typeset'' | builtin |
|
|[[commands:builtin:declare | declare]]|Display or set shell variables or functions along with attributes.| ''typeset'' | builtin |
|
||||||
|[[commands:builtin:export | export]]|Display or set shell variables, also giving them the export attribute.| ''typeset -x'' | special builtin |
|
|[[commands:builtin:export | export]]|Display or set shell variables, also giving them the export attribute.| ''typeset -x'' | special builtin |
|
||||||
|[[commands:builtin:eval | eval]]|Evaluate arguments as shell code.| - | special builtin |
|
|[[commands:builtin:eval | eval]]|Evaluate arguments as shell code.| - | special builtin |
|
||||||
@ -119,20 +119,20 @@ This is a selection of builtin commands and command-like keywords, loosely arran
|
|||||||
|[[commands:builtin:readonly | readonly]]|Mark variables or functions as read-only.| ''typeset -r'' | special builtin |
|
|[[commands:builtin:readonly | readonly]]|Mark variables or functions as read-only.| ''typeset -r'' | special builtin |
|
||||||
|[[commands:builtin:unset | unset]]|Unset variables and functions.| - | special builtin |
|
|[[commands:builtin:unset | unset]]|Unset variables and functions.| - | special builtin |
|
||||||
|[[commands:builtin:shift | shift]]|Shift positional parameters| - | special builtin |
|
|[[commands:builtin:shift | shift]]|Shift positional parameters| - | special builtin |
|
||||||
^ I/O\\ <wrap center round lo todo box 80%>Commands for reading/parsing input, or producing/formatting output of standard streams.</wrap> ^^ Alt ^ Type ^
|
^ I/O\\ <wrap center round lo todo box 80%>Commands for reading/parsing input, or producing/formatting output of standard streams.</wrap> ^^ Alt ^ Type ^
|
||||||
|[[syntax:keywords:coproc | coproc]]|Co-processes: Run a command in the background with pipes for reading / writing its standard streams.| - | keyword |
|
|[[syntax:keywords:coproc | coproc]]|Co-processes: Run a command in the background with pipes for reading / writing its standard streams.| - | keyword |
|
||||||
|[[commands:builtin:echo | echo]]|Create output from arguments.| - | builtin |
|
|[[commands:builtin:echo | echo]]|Create output from arguments.| - | builtin |
|
||||||
|[[commands:builtin:mapfile | mapfile]]|Read lines of input into an array.| ''readarray'' | builtin |
|
|[[commands:builtin:mapfile | mapfile]]|Read lines of input into an array.| ''readarray'' | builtin |
|
||||||
|[[commands:builtin:printf | printf]]|"advanced ''echo''."| - | builtin |
|
|[[commands:builtin:printf | printf]]|"advanced ''echo''."| - | builtin |
|
||||||
|[[commands:builtin:read | read]]|Read input into variables or arrays, or split strings into fields using delimiters.| - | builtin |
|
|[[commands:builtin:read | read]]|Read input into variables or arrays, or split strings into fields using delimiters.| - | builtin |
|
||||||
^ Configuration and Debugging\\ <wrap center round lo todo box 80%>Commands that modify shell behavior, change special options, assist in debugging.</wrap> ^^ Alt ^ Type ^
|
^ Configuration and Debugging\\ <wrap center round lo todo box 80%>Commands that modify shell behavior, change special options, assist in debugging.</wrap> ^^ Alt ^ Type ^
|
||||||
|[[commands:builtin:caller | caller]]|Identify/print execution frames.| - | builtin |
|
|[[commands:builtin:caller | caller]]|Identify/print execution frames.| - | builtin |
|
||||||
|[[commands:builtin:set | set]]|Set the positional parameters and/or set options that affect shell behaviour.| - | special builtin |
|
|[[commands:builtin:set | set]]|Set the positional parameters and/or set options that affect shell behaviour.| - | special builtin |
|
||||||
|[[commands:builtin:shopt | shopt]]|set/get some bash-specific shell options.| - | builtin |
|
|[[commands:builtin:shopt | shopt]]|set/get some bash-specific shell options.| - | builtin |
|
||||||
</WRAP>
|
</WRAP>
|
||||||
|
|
||||||
<WRAP column 46%>
|
<WRAP column 46%>
|
||||||
^ Control flow and data processing\\ <wrap center round lo todo box 80%>Commands that operate on data and/or affect control flow.</wrap> |^ Alt ^ Type ^
|
^ Control flow and data processing\\ <wrap center round lo todo box 80%>Commands that operate on data and/or affect control flow.</wrap> |^ Alt ^ Type ^
|
||||||
| [[commands:builtin:true| colon]] | "true" null command. | true | special builtin |
|
| [[commands:builtin:true| colon]] | "true" null command. | true | special builtin |
|
||||||
| [[commands:builtin:source| dot]] | Source external files. | source | special builtin |
|
| [[commands:builtin:source| dot]] | Source external files. | source | special builtin |
|
||||||
| [[commands:builtin:false| false]] | Fail at doing nothing. | - | builtin |
|
| [[commands:builtin:false| false]] | Fail at doing nothing. | - | builtin |
|
||||||
@ -140,7 +140,7 @@ This is a selection of builtin commands and command-like keywords, loosely arran
|
|||||||
| [[commands:builtin:let| let]] | Arithmetic evaluation simple command. | - | builtin |
|
| [[commands:builtin:let| let]] | Arithmetic evaluation simple command. | - | builtin |
|
||||||
| [[commands:builtin:return| return]] | Return from a function with a specified exit status. | - | special builtin |
|
| [[commands:builtin:return| return]] | Return from a function with a specified exit status. | - | special builtin |
|
||||||
| [[commands:classictest| []] | The classic ''test'' simple command. | test | builtin |
|
| [[commands:classictest| []] | The classic ''test'' simple command. | test | builtin |
|
||||||
^ Process and Job control\\ <wrap center round lo todo box 80%>Commands related to jobs, signals, process groups, subshells.</wrap> |^ Alt ^ Type ^
|
^ Process and Job control\\ <wrap center round lo todo box 80%>Commands related to jobs, signals, process groups, subshells.</wrap> |^ Alt ^ Type ^
|
||||||
| [[commands:builtin:exec| exec]] | Replace the current shell process or set redirections. | - | special builtin |
|
| [[commands:builtin:exec| exec]] | Replace the current shell process or set redirections. | - | special builtin |
|
||||||
| [[commands:builtin:exit| exit]] | Exit the shell. | - | special builtin |
|
| [[commands:builtin:exit| exit]] | Exit the shell. | - | special builtin |
|
||||||
| [[commands:builtin:kill| kill]] | Send a signal to specified process(es) | - | builtin |
|
| [[commands:builtin:kill| kill]] | Send a signal to specified process(es) | - | builtin |
|
||||||
@ -148,10 +148,10 @@ This is a selection of builtin commands and command-like keywords, loosely arran
|
|||||||
| [[commands:builtin:times| times]] | Display process times. | - | special builtin |
|
| [[commands:builtin:times| times]] | Display process times. | - | special builtin |
|
||||||
| [[commands:builtin:wait| wait]] | Wait for background jobs and asynchronous lists. | - | builtin |
|
| [[commands:builtin:wait| wait]] | Wait for background jobs and asynchronous lists. | - | builtin |
|
||||||
^ |^ Alt ^ Type ^
|
^ |^ Alt ^ Type ^
|
||||||
</WRAP>
|
</WRAP>
|
||||||
<WRAP clear></WRAP>
|
<WRAP clear></WRAP>
|
||||||
<WRAP hide> previous alphabetical version
|
<WRAP hide> previous alphabetical version
|
||||||
<WRAP column 40%>
|
<WRAP column 40%>
|
||||||
^A-G^^
|
^A-G^^
|
||||||
|[[commands:builtin:caller | caller]]|identify/print execution frames (Bash builtin) |
|
|[[commands:builtin:caller | caller]]|identify/print execution frames (Bash builtin) |
|
||||||
|[[syntax:keywords:coproc | coproc]]|Co-processes (Bash keyword) |
|
|[[syntax:keywords:coproc | coproc]]|Co-processes (Bash keyword) |
|
||||||
@ -160,9 +160,9 @@ This is a selection of builtin commands and command-like keywords, loosely arran
|
|||||||
^I-N^^
|
^I-N^^
|
||||||
|[[commands:builtin:let | let]]|arithmetic evaluation - an old fashioned way (Bash builtin) |
|
|[[commands:builtin:let | let]]|arithmetic evaluation - an old fashioned way (Bash builtin) |
|
||||||
|[[commands:builtin:mapfile | mapfile]]|Mapping lines of input to an array, also named ''readarray'' (Bash builtin) |
|
|[[commands:builtin:mapfile | mapfile]]|Mapping lines of input to an array, also named ''readarray'' (Bash builtin) |
|
||||||
</WRAP>
|
</WRAP>
|
||||||
|
|
||||||
<WRAP column 40%>
|
<WRAP column 40%>
|
||||||
^O-T^^
|
^O-T^^
|
||||||
|[[commands:builtin:printf | printf]]|"advanced ''echo''" (Bash builtin) |
|
|[[commands:builtin:printf | printf]]|"advanced ''echo''" (Bash builtin) |
|
||||||
|[[commands:builtin:read | read]]|getting input from ''stdin'' (Bash builtin) |
|
|[[commands:builtin:read | read]]|getting input from ''stdin'' (Bash builtin) |
|
||||||
@ -172,12 +172,12 @@ This is a selection of builtin commands and command-like keywords, loosely arran
|
|||||||
|[[commands:classictest | test]]|the classic ''test'' command (Bash builtin) |
|
|[[commands:classictest | test]]|the classic ''test'' command (Bash builtin) |
|
||||||
^U-Z^^
|
^U-Z^^
|
||||||
|[[commands:builtin:unset | unset]]|unset variables and functions (Bash builtin) |
|
|[[commands:builtin:unset | unset]]|unset variables and functions (Bash builtin) |
|
||||||
</WRAP>
|
</WRAP>
|
||||||
<WRAP clear></WRAP>
|
<WRAP clear></WRAP>
|
||||||
</WRAP>
|
</WRAP>
|
||||||
===== Dictionary ======
|
===== Dictionary ======
|
||||||
|
|
||||||
<note tip>A list of expressions, words, and their meanings is [[dict:index | here]].</note>
|
<note tip>A list of expressions, words, and their meanings is [[dict:index | here]].</note>
|
||||||
|
|
||||||
===== Links =====
|
===== Links =====
|
||||||
|
|
||||||
@ -186,11 +186,11 @@ This is a selection of builtin commands and command-like keywords, loosely arran
|
|||||||
* [[http://tiswww.case.edu/php/chet/bash/bashtop.html|Chet Ramey's Bash page]] and its [[http://tiswww.case.edu/php/chet/bash/FAQ|FAQ]].
|
* [[http://tiswww.case.edu/php/chet/bash/bashtop.html|Chet Ramey's Bash page]] and its [[http://tiswww.case.edu/php/chet/bash/FAQ|FAQ]].
|
||||||
* [[http://www.gnu.org/software/bash/ | GNU Bash software page]]
|
* [[http://www.gnu.org/software/bash/ | GNU Bash software page]]
|
||||||
* Official Bash mailing lists:
|
* Official Bash mailing lists:
|
||||||
* **Bug reports**: <bug-bash@gnu.org> ([[http://mail.gnu.org/pipermail/bug-bash|archives]])
|
* **Bug reports**: <bug-bash@gnu.org> ([[http://mail.gnu.org/pipermail/bug-bash|archives]])
|
||||||
* **General questions**: <help-bash@gnu.org> ([[http://mail.gnu.org/pipermail/help-bash|archives]])
|
* **General questions**: <help-bash@gnu.org> ([[http://mail.gnu.org/pipermail/help-bash|archives]])
|
||||||
* Official Bash git repository:
|
* Official Bash git repository:
|
||||||
* **Browse**: [[http://git.savannah.gnu.org/cgit/bash.git | cgit]]
|
* **Browse**: [[http://git.savannah.gnu.org/cgit/bash.git | cgit]]
|
||||||
* **Clone**: //git:// <nowiki>ssh://git.sv.gnu.org/srv/git/bash.git</nowiki> • //ssh:// <nowiki>ssh://git.sv.gnu.org/srv/git/bash.git</nowiki> • //http:// <nowiki>http://git.savannah.gnu.org/r/bash.git</nowiki>
|
* **Clone**: //git:// <nowiki>ssh://git.sv.gnu.org/srv/git/bash.git</nowiki> • //ssh:// <nowiki>ssh://git.sv.gnu.org/srv/git/bash.git</nowiki> • //http:// <nowiki>http://git.savannah.gnu.org/r/bash.git</nowiki>
|
||||||
|
|
||||||
==== Recommended Shell resources ====
|
==== Recommended Shell resources ====
|
||||||
|
|
||||||
@ -209,7 +209,7 @@ This is a selection of builtin commands and command-like keywords, loosely arran
|
|||||||
=== Documentation / Reference ===
|
=== Documentation / Reference ===
|
||||||
|
|
||||||
* **Bash**: [[http://tiswww.case.edu/php/chet/bash/bash.html | man page]] [[http://tiswww.case.edu/php/chet/bash/bashref.html | info page]]
|
* **Bash**: [[http://tiswww.case.edu/php/chet/bash/bash.html | man page]] [[http://tiswww.case.edu/php/chet/bash/bashref.html | info page]]
|
||||||
* **AT&T ksh**: [[http://www2.research.att.com/sw/download/man/man1/ksh88.html | ksh88]] [[http://www2.research.att.com/sw/download/man/man1/ksh.html | ksh93]]
|
* **AT&T ksh**: [[http://www2.research.att.com/sw/download/man/man1/ksh88.html | ksh88]] [[http://www2.research.att.com/sw/download/man/man1/ksh.html | ksh93]]
|
||||||
* [[https://www.mirbsd.org/htman/i386/man1/mksh.htm | mksh]] (pdksh successor)
|
* [[https://www.mirbsd.org/htman/i386/man1/mksh.htm | mksh]] (pdksh successor)
|
||||||
* [[http://zsh.sourceforge.net/Doc/ | zsh]]
|
* [[http://zsh.sourceforge.net/Doc/ | zsh]]
|
||||||
* [[http://man7.org/linux/man-pages/man1/dash.1.html | dash]]
|
* [[http://man7.org/linux/man-pages/man1/dash.1.html | dash]]
|
||||||
@ -235,7 +235,7 @@ This is a selection of builtin commands and command-like keywords, loosely arran
|
|||||||
* [[https://github.com/hornos/shf3 | General purpose shell framework for bash 4]] - in development.
|
* [[https://github.com/hornos/shf3 | General purpose shell framework for bash 4]] - in development.
|
||||||
* [[https://github.com/chilicuil/learn/blob/master/sh/lib| General purpose bash library for bash 4]] - active development
|
* [[https://github.com/chilicuil/learn/blob/master/sh/lib| General purpose bash library for bash 4]] - active development
|
||||||
|
|
||||||
<div hide>
|
<div hide>
|
||||||
===== Most wanted =====
|
===== Most wanted =====
|
||||||
|
|
||||||
^Statistics for Month: **April 2012**^^^
|
^Statistics for Month: **April 2012**^^^
|
||||||
@ -253,10 +253,10 @@ This is a selection of builtin commands and command-like keywords, loosely arran
|
|||||||
|
|
||||||
* "Total visits": 35144
|
* "Total visits": 35144
|
||||||
* The overall "start" page is not counted
|
* The overall "start" page is not counted
|
||||||
</div>
|
</div>
|
||||||
===== Contact =====
|
===== Contact =====
|
||||||
|
|
||||||
<WRAP column 40%>
|
<WRAP column 40%>
|
||||||
Visit us in ''irc.freenode.net'', channel ''#bash'' ;-)
|
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.
|
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.
|
||||||
@ -270,8 +270,8 @@ It also would be nice to drop a line when
|
|||||||
* you don't like it
|
* you don't like it
|
||||||
* you found mistakes / bugs
|
* you found mistakes / bugs
|
||||||
Simply: Reader's feedback
|
Simply: Reader's feedback
|
||||||
</WRAP>
|
</WRAP>
|
||||||
|
|
||||||
<WRAP column 40%>
|
<WRAP column 40%>
|
||||||
{{contact>subj=Wiki reader feedback}}
|
{{contact>subj=Wiki reader feedback}}
|
||||||
</WRAP>
|
</WRAP>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
====== Arithmetic expressions ======
|
====== Arithmetic expressions ======
|
||||||
|
|
||||||
{{keywords>bash shell scripting math arithmetic C calculation integer}}
|
{{keywords>bash shell scripting math arithmetic C calculation integer}}
|
||||||
|
|
||||||
Arithmetic expressions are used in several situations:
|
Arithmetic expressions are used in several situations:
|
||||||
* [[syntax:ccmd:arithmetic_eval | arithmetic evaluation command]]
|
* [[syntax:ccmd:arithmetic_eval | arithmetic evaluation command]]
|
||||||
@ -22,13 +22,13 @@ Mathematical constants are simply fixed values you write: ''1'', ''3567'', or ''
|
|||||||
* ''0...'' (leading zero) is interpreted as an **octal** value
|
* ''0...'' (leading zero) is interpreted as an **octal** value
|
||||||
* ''0x...'' is interpreted as a **hex** value
|
* ''0x...'' is interpreted as a **hex** value
|
||||||
* ''0X...'' also interpreted as a **hex**
|
* ''0X...'' also interpreted as a **hex**
|
||||||
* ''<BASE>#...'' is interpreted as a number according to the **specified base** ''<BASE>'', e.g., ''2#00111011'' (see below)
|
* ''<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,
|
If you have a constant set in a variable, like,
|
||||||
<code>
|
<code>
|
||||||
x=03254
|
x=03254
|
||||||
</code>
|
</code>
|
||||||
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 an octal value. If you want it to be interpreted as a decimal value, you need to expand the parameter and specify base 10:
|
||||||
<code>
|
<code>
|
||||||
# this is interpreted as a decimal:
|
# this is interpreted as a decimal:
|
||||||
echo $(( 10#$x ))
|
echo $(( 10#$x ))
|
||||||
|
|
||||||
@ -37,18 +37,18 @@ echo $(( x ))
|
|||||||
|
|
||||||
# this is an invalid digit for base 10 (the "x")...:
|
# this is an invalid digit for base 10 (the "x")...:
|
||||||
echo $(( 10#x ))
|
echo $(( 10#x ))
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Different bases =====
|
===== Different bases =====
|
||||||
|
|
||||||
For a constant, the base can be specified using the form
|
For a constant, the base can be specified using the form
|
||||||
<code>
|
<code>
|
||||||
<BASE>#<DIGITS...>
|
<BASE>#<DIGITS...>
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Regardless of the specified base, the arithmetic expressions will, if ever displayed, be **displayed in decimal**!
|
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):
|
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''
|
* ''0 ... 9''
|
||||||
* ''a ... z''
|
* ''a ... z''
|
||||||
* ''A ... Z''
|
* ''A ... Z''
|
||||||
@ -56,7 +56,7 @@ When no base is specified, the base 10 (decimal) is assumed, except when the pre
|
|||||||
* ''_''
|
* ''_''
|
||||||
|
|
||||||
Let's quickly invent a new number system with base 43 to show what I mean:
|
Let's quickly invent a new number system with base 43 to show what I mean:
|
||||||
<code>
|
<code>
|
||||||
$ echo $((43#1))
|
$ echo $((43#1))
|
||||||
1
|
1
|
||||||
|
|
||||||
@ -71,7 +71,7 @@ $ echo $((43#G))
|
|||||||
|
|
||||||
$ echo $((43#H))
|
$ echo $((43#H))
|
||||||
bash: 43#H: value too great for base (error token is "43#H")
|
bash: 43#H: value too great for base (error token is "43#H")
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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 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.
|
||||||
|
|
||||||
@ -79,14 +79,14 @@ If you want to convert between the usual bases (octal, decimal, hex), use [[comm
|
|||||||
|
|
||||||
===== Shell variables =====
|
===== 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.:
|
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.:
|
||||||
<code>
|
<code>
|
||||||
test=string
|
test=string
|
||||||
string=3
|
string=3
|
||||||
|
|
||||||
echo $((test))
|
echo $((test))
|
||||||
# will output "3"!
|
# will output "3"!
|
||||||
</code>
|
</code>
|
||||||
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.
|
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, the notation ''1 + $X'' is equivalent to the notation ''1 + X'', both are allowed.
|
||||||
@ -99,30 +99,30 @@ Unlike command exit and return codes, arithmetic expressions evaluate to logical
|
|||||||
* if the arithmetic expression brings up a value not 0 (arithmetic true), it returns 0 (shell true)
|
* 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)
|
* 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:
|
That means, the following ''if''-clause will execute the ''else''-thread:
|
||||||
<code>
|
<code>
|
||||||
if ((0)); then
|
if ((0)); then
|
||||||
echo "true"
|
echo "true"
|
||||||
else
|
else
|
||||||
echo "false"
|
echo "false"
|
||||||
fi
|
fi
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Operators =====
|
===== Operators =====
|
||||||
|
|
||||||
==== Assignment ====
|
==== Assignment ====
|
||||||
|
|
||||||
^Operator^Description^
|
^Operator^Description^
|
||||||
|''<ID> = <EXPR>''|normal assignment|
|
|''<ID> = <EXPR>''|normal assignment|
|
||||||
|''<ID> *= <EXPR>''|equivalent to ''<ID> = <ID> * <EXPR>'', see [[syntax:arith_expr#calculations | calculation operators]]|
|
|''<ID> *= <EXPR>''|equivalent to ''<ID> = <ID> * <EXPR>'', see [[syntax:arith_expr#calculations | calculation operators]]|
|
||||||
|''<ID> /= <EXPR>''|equivalent to ''<ID> = <ID> / <EXPR>'', see [[syntax:arith_expr#calculations | calculation operators]]|
|
|''<ID> /= <EXPR>''|equivalent to ''<ID> = <ID> / <EXPR>'', see [[syntax:arith_expr#calculations | calculation operators]]|
|
||||||
|''<ID> %= <EXPR>''|equivalent to ''<ID> = <ID> % <EXPR>'', see [[syntax:arith_expr#calculations | calculation operators]]|
|
|''<ID> %= <EXPR>''|equivalent to ''<ID> = <ID> % <EXPR>'', see [[syntax:arith_expr#calculations | calculation operators]]|
|
||||||
|''<ID> += <EXPR>''|equivalent to ''<ID> = <ID> + <EXPR>'', see [[syntax:arith_expr#calculations | calculation operators]]|
|
|''<ID> += <EXPR>''|equivalent to ''<ID> = <ID> + <EXPR>'', see [[syntax:arith_expr#calculations | calculation operators]]|
|
||||||
|''<ID> -= <EXPR>''|equivalent to ''<ID> = <ID> - <EXPR>'', see [[syntax:arith_expr#calculations | calculation operators]]|
|
|''<ID> -= <EXPR>''|equivalent to ''<ID> = <ID> - <EXPR>'', see [[syntax:arith_expr#calculations | calculation operators]]|
|
||||||
|''<ID> <nowiki><<=</nowiki> <NUMBER>''|equivalent to ''<ID> = <ID> <nowiki><<</nowiki> <NUMBER>'', see [[syntax:arith_expr#bit_operations | bit operations]]|
|
|''<ID> <nowiki><<=</nowiki> <NUMBER>''|equivalent to ''<ID> = <ID> <nowiki><<</nowiki> <NUMBER>'', see [[syntax:arith_expr#bit_operations | bit operations]]|
|
||||||
|''<ID> <nowiki>>>=</nowiki> <NUMBER>''|equivalent to ''<ID> = <ID> <nowiki>>></nowiki> <NUMBER>'', see [[syntax:arith_expr#bit_operations | bit operations]]|
|
|''<ID> <nowiki>>>=</nowiki> <NUMBER>''|equivalent to ''<ID> = <ID> <nowiki>>></nowiki> <NUMBER>'', see [[syntax:arith_expr#bit_operations | bit operations]]|
|
||||||
|''<ID> &= <EXPR>''|equivalent to ''<ID> = <ID> & <EXPR>'', see [[syntax:arith_expr#bit_operations | bit operations]]|
|
|''<ID> &= <EXPR>''|equivalent to ''<ID> = <ID> & <EXPR>'', see [[syntax:arith_expr#bit_operations | bit operations]]|
|
||||||
|''<ID> ^= <EXPR>''|equivalent to ''<ID> = <ID> ^ <EXPR>'', see [[syntax:arith_expr#bit_operations | bit operations]]|
|
|''<ID> ^= <EXPR>''|equivalent to ''<ID> = <ID> ^ <EXPR>'', see [[syntax:arith_expr#bit_operations | bit operations]]|
|
||||||
|''<ID> <nowiki>|=</nowiki> <EXPR>''|equivalent to ''<ID> = <ID> <nowiki>|</nowiki> <EXPR>'', see [[syntax:arith_expr#bit_operations | bit operations]]|
|
|''<ID> <nowiki>|=</nowiki> <EXPR>''|equivalent to ''<ID> = <ID> <nowiki>|</nowiki> <EXPR>'', see [[syntax:arith_expr#bit_operations | bit operations]]|
|
||||||
|
|
||||||
==== Calculations ====
|
==== Calculations ====
|
||||||
|
|
||||||
@ -132,25 +132,25 @@ fi
|
|||||||
|''%''|remainder (modulo)|
|
|''%''|remainder (modulo)|
|
||||||
|''+''|addition|
|
|''+''|addition|
|
||||||
|''-''|subtraction|
|
|''-''|subtraction|
|
||||||
|''<nowiki>**</nowiki>''|exponentiation|
|
|''<nowiki>**</nowiki>''|exponentiation|
|
||||||
|
|
||||||
==== Comparisons ====
|
==== Comparisons ====
|
||||||
|
|
||||||
^Operator^Description^
|
^Operator^Description^
|
||||||
|''<''|comparison: less than|
|
|''<''|comparison: less than|
|
||||||
|''>''|comparison: greater than|
|
|''>''|comparison: greater than|
|
||||||
|''<nowiki><=</nowiki>''|comparison: less than or equal|
|
|''<nowiki><=</nowiki>''|comparison: less than or equal|
|
||||||
|''<nowiki>>=</nowiki>''|comparison: greater than or equal|
|
|''<nowiki>>=</nowiki>''|comparison: greater than or equal|
|
||||||
|''<nowiki>==</nowiki>''|equality|
|
|''<nowiki>==</nowiki>''|equality|
|
||||||
|''!=''|inequality|
|
|''!=''|inequality|
|
||||||
|
|
||||||
==== Bit operations ====
|
==== Bit operations ====
|
||||||
|
|
||||||
^Operator^Description^
|
^Operator^Description^
|
||||||
|''~''|bitwise negation|
|
|''~''|bitwise negation|
|
||||||
|''<nowiki><<</nowiki>''|bitwise shifting (left)|
|
|''<nowiki><<</nowiki>''|bitwise shifting (left)|
|
||||||
|''<nowiki>>></nowiki>''|bitwise shifting (right)|
|
|''<nowiki>>></nowiki>''|bitwise shifting (right)|
|
||||||
|''&''|bitwise AND|
|
|''&''|bitwise AND|
|
||||||
|''^''|bitwise exclusive OR (XOR)|
|
|''^''|bitwise exclusive OR (XOR)|
|
||||||
|''|''|bitwise OR|
|
|''|''|bitwise OR|
|
||||||
|
|
||||||
@ -158,8 +158,8 @@ fi
|
|||||||
|
|
||||||
^Operator^Description^
|
^Operator^Description^
|
||||||
|''!''|logical negation|
|
|''!''|logical negation|
|
||||||
|''<nowiki>&&</nowiki>''|logical AND|
|
|''<nowiki>&&</nowiki>''|logical AND|
|
||||||
|''<nowiki>||</nowiki>''|logical OR|
|
|''<nowiki>||</nowiki>''|logical OR|
|
||||||
|
|
||||||
==== Misc ====
|
==== Misc ====
|
||||||
|
|
||||||
@ -170,35 +170,35 @@ fi
|
|||||||
| ''%%--%%id'' | **pre-decrement** of the variable ''id'' (not required by POSIX(r)) |
|
| ''%%--%%id'' | **pre-decrement** of the variable ''id'' (not required by POSIX(r)) |
|
||||||
| ''+'' | unary plus |
|
| ''+'' | unary plus |
|
||||||
| ''-'' | unary minus |
|
| ''-'' | unary minus |
|
||||||
| ''<EXPR> ? <EXPR> : <EXPR>'' | conditional (ternary) operator \\ <condition> ? <result-if-true> : <result-if-false> |
|
| ''<EXPR> ? <EXPR> : <EXPR>'' | conditional (ternary) operator \\ <condition> ? <result-if-true> : <result-if-false> |
|
||||||
| ''<EXPR> , <EXPR>'' | expression list |
|
| ''<EXPR> , <EXPR>'' | expression list |
|
||||||
| ''( <EXPR> )'' | subexpression (to force precedence) |
|
| ''( <EXPR> )'' | subexpression (to force precedence) |
|
||||||
|
|
||||||
|
|
||||||
===== Precedence =====
|
===== Precedence =====
|
||||||
|
|
||||||
The operator precedence is as follows (highest -> lowest):
|
The operator precedence is as follows (highest -> lowest):
|
||||||
|
|
||||||
* Postfix (''id++'', ''<nowiki>id--</nowiki>'')
|
* Postfix (''id++'', ''<nowiki>id--</nowiki>'')
|
||||||
* Prefix (''++id'', ''<nowiki>--id</nowiki>'')
|
* Prefix (''++id'', ''<nowiki>--id</nowiki>'')
|
||||||
* Unary minus and plus (''-'', ''+'')
|
* Unary minus and plus (''-'', ''+'')
|
||||||
* Logical and bitwise negation (''!'', ''~'')
|
* Logical and bitwise negation (''!'', ''~'')
|
||||||
* Exponentiation (''<nowiki>**</nowiki>'')
|
* Exponentiation (''<nowiki>**</nowiki>'')
|
||||||
* Multiplication, division, remainder (''*'', ''/'', ''%'')
|
* Multiplication, division, remainder (''*'', ''/'', ''%'')
|
||||||
* Addition, subtraction (''+'', ''-'')
|
* Addition, subtraction (''+'', ''-'')
|
||||||
* Bitwise shifts (''<nowiki><<</nowiki>'', ''<nowiki>>></nowiki>'')
|
* Bitwise shifts (''<nowiki><<</nowiki>'', ''<nowiki>>></nowiki>'')
|
||||||
* Comparison (''<'', ''>'', ''<nowiki><=</nowiki>'', ''<nowiki>>=</nowiki>'')
|
* Comparison (''<'', ''>'', ''<nowiki><=</nowiki>'', ''<nowiki>>=</nowiki>'')
|
||||||
* (In-)equality (''<nowiki>==</nowiki>'', ''!='')
|
* (In-)equality (''<nowiki>==</nowiki>'', ''!='')
|
||||||
* Bitwise AND (''&'')
|
* Bitwise AND (''&'')
|
||||||
* Bitwise XOR (''^'')
|
* Bitwise XOR (''^'')
|
||||||
* Bitwise OR (''|'')
|
* Bitwise OR (''|'')
|
||||||
* Logical AND (''<nowiki>&&</nowiki>'')
|
* Logical AND (''<nowiki>&&</nowiki>'')
|
||||||
* Logical OR (''<nowiki>||</nowiki>'')
|
* Logical OR (''<nowiki>||</nowiki>'')
|
||||||
* Ternary operator (''<EXPR> ? <EXPR> : <EXPR>'')
|
* Ternary operator (''<EXPR> ? <EXPR> : <EXPR>'')
|
||||||
* Assignments (''='', ''*='', ''/='', ''%='', ''+='', ''-='', ''<nowiki><<=</nowiki>'', ''<nowiki>>>=</nowiki>'', ''&='', ''^='', ''|='')
|
* Assignments (''='', ''*='', ''/='', ''%='', ''+='', ''-='', ''<nowiki><<=</nowiki>'', ''<nowiki>>>=</nowiki>'', ''&='', ''^='', ''|='')
|
||||||
* Expression list operator (''<EXPR> , <EXPR>'')
|
* 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.
|
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 =====
|
===== Arithmetic expressions and return codes =====
|
||||||
|
|
||||||
@ -211,7 +211,7 @@ That's why all commands and keywords that do arithmetic operations attempt to **
|
|||||||
* if the arithmetic operation evaluates to 1 ("TRUE"), the return code is 0 ("SUCCESS")
|
* 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:
|
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:
|
||||||
<code bash>
|
<code bash>
|
||||||
MY_TEST_FLAG=0
|
MY_TEST_FLAG=0
|
||||||
|
|
||||||
if ((MY_TEST_FLAG)); then
|
if ((MY_TEST_FLAG)); then
|
||||||
@ -219,21 +219,21 @@ if ((MY_TEST_FLAG)); then
|
|||||||
else
|
else
|
||||||
echo "MY_TEST_FLAG is OFF"
|
echo "MY_TEST_FLAG is OFF"
|
||||||
fi
|
fi
|
||||||
</code>
|
</code>
|
||||||
<WRAP center round important>
|
<WRAP center round important>
|
||||||
Beware that ''set -e'' can change the runtime behavior of scripts. For example,
|
Beware that ''set -e'' can change the runtime behavior of scripts. For example,
|
||||||
|
|
||||||
This non-equivalence of code behavior deserves some attention.
|
This non-equivalence of code behavior deserves some attention.
|
||||||
Consider what happens if v happens to be zero in the expression below:
|
Consider what happens if v happens to be zero in the expression below:
|
||||||
|
|
||||||
<code bash>((v += 0))
|
<code bash>((v += 0))
|
||||||
echo $?</code>
|
echo $?</code>
|
||||||
1
|
1
|
||||||
|
|
||||||
("FAILURE")
|
("FAILURE")
|
||||||
|
|
||||||
<code bash>v=$((v + 0))
|
<code bash>v=$((v + 0))
|
||||||
echo $?</code>
|
echo $?</code>
|
||||||
0
|
0
|
||||||
|
|
||||||
("SUCCESS")
|
("SUCCESS")
|
||||||
@ -241,14 +241,14 @@ echo $?</code>
|
|||||||
The return code behavior is not equivalent to the arithmetic behavior, as has been noted.
|
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.
|
A workaround is to use a list operation that returns True, or use the second assignment style.
|
||||||
<code bash>((v += 0)) || :
|
<code bash>((v += 0)) || :
|
||||||
echo $?</code>
|
echo $?</code>
|
||||||
0
|
0
|
||||||
|
|
||||||
("SUCCESS")
|
("SUCCESS")
|
||||||
|
|
||||||
This change in code behavior was discovered once the script was run under set -e.
|
This change in code behavior was discovered once the script was run under set -e.
|
||||||
</WRAP>
|
</WRAP>
|
||||||
===== Arithmetic expressions in Bash =====
|
===== Arithmetic expressions in Bash =====
|
||||||
|
|
||||||
* [[syntax:ccmd:c_for | The C-style for-loop]]
|
* [[syntax:ccmd:c_for | The C-style for-loop]]
|
||||||
|
152
syntax/arrays.md
152
syntax/arrays.md
@ -5,13 +5,13 @@
|
|||||||
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.
|
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.
|
Here is an **abstract** representation of an array named ''NAMES''. The indexes go from 0 to 3.
|
||||||
<code>
|
<code>
|
||||||
NAMES
|
NAMES
|
||||||
0: Peter
|
0: Peter
|
||||||
1: Anna
|
1: Anna
|
||||||
2: Greg
|
2: Greg
|
||||||
3: Jan
|
3: Jan
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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''.
|
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''.
|
||||||
|
|
||||||
@ -25,17 +25,17 @@ Bash supports two different types of ksh-like one-dimensional arrays. **Multidim
|
|||||||
|
|
||||||
==== Referencing ====
|
==== 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: ''<nowiki>[[:alpha:]_][[:alnum:]_]*</nowiki>''. The parameter name may be followed by an optional subscript enclosed in square brackets to refer to a member of the array.
|
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: ''<nowiki>[[:alpha:]_][[:alnum:]_]*</nowiki>''. 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 [[commands/builtin/unset | 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.
|
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 [[commands/builtin/unset | 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 [[syntax:arith_expr | arithmetic expressions]], [[syntax:pe | parameter expansions]], 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.
|
With few exceptions, names of this form may be used anywhere ordinary parameter names are valid, such as within [[syntax:arith_expr | arithmetic expressions]], [[syntax:pe | parameter expansions]], 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.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
# "x" is an ordinary non-array parameter.
|
# "x" is an ordinary non-array parameter.
|
||||||
$ x=hi; printf '%s ' "$x" "${x[0]}"; echo "${_[0]}"
|
$ x=hi; printf '%s ' "$x" "${x[0]}"; echo "${_[0]}"
|
||||||
hi hi hi
|
hi hi hi
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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]]).
|
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]]).
|
||||||
|
|
||||||
@ -69,9 +69,9 @@ Storing values in arrays is quite as simple as storing values in normal variable
|
|||||||
|
|
||||||
As of now, arrays can't be exported.
|
As of now, arrays can't be exported.
|
||||||
==== Getting values ====
|
==== Getting values ====
|
||||||
<note>
|
<note>
|
||||||
For completeness and details on several parameter expansion variants, see the [[syntax:pe|article about parameter expansion]] and check the notes about arrays.
|
For completeness and details on several parameter expansion variants, see the [[syntax:pe|article about parameter expansion]] and check the notes about arrays.
|
||||||
</note>
|
</note>
|
||||||
|
|
||||||
^Syntax ^Description ^
|
^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[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 |
|
||||||
@ -99,24 +99,24 @@ The [[commands/builtin/unset | unset]] builtin command is used to destroy (unset
|
|||||||
|
|
||||||
It is best to [[commands/builtin/unset#portability_considerations | explicitly specify -v]] when unsetting variables with unset.
|
It is best to [[commands/builtin/unset#portability_considerations | explicitly specify -v]] when unsetting variables with unset.
|
||||||
|
|
||||||
<note warning>
|
<note warning>
|
||||||
Specifying unquoted array elements as arguments to any command, such as with the syntax above **may cause [[syntax:expansion:globs | pathname expansion]] to occur** due to the presence of glob characters.
|
Specifying unquoted array elements as arguments to any command, such as with the syntax above **may cause [[syntax:expansion:globs | pathname expansion]] 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
|
Example: You are in a directory with a file named ''x1'', and you want to destroy an array element ''x[1]'', with
|
||||||
<code>
|
<code>
|
||||||
unset x[1]
|
unset x[1]
|
||||||
</code>
|
</code>
|
||||||
then pathname expansion will expand to the filename ''x1'' and break your processing!
|
then pathname expansion will expand to the filename ''x1'' and break your processing!
|
||||||
|
|
||||||
Even worse, if ''nullglob'' is set, your array/index will disappear.
|
Even worse, if ''nullglob'' is set, your array/index will disappear.
|
||||||
|
|
||||||
To avoid this, **always quote** the array name and index:
|
To avoid this, **always quote** the array name and index:
|
||||||
<code>
|
<code>
|
||||||
unset -v 'x[1]'
|
unset -v 'x[1]'
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
This applies generally to all commands which take variable names as arguments. Single quotes preferred.
|
This applies generally to all commands which take variable names as arguments. Single quotes preferred.
|
||||||
</note>
|
</note>
|
||||||
|
|
||||||
===== Usage =====
|
===== Usage =====
|
||||||
|
|
||||||
@ -127,25 +127,25 @@ Numerical indexed arrays are easy to understand and easy to use. The [[#purpose|
|
|||||||
Now, some examples and comments for you.
|
Now, some examples and comments for you.
|
||||||
|
|
||||||
Let's say we have an array ''sentence'' which is initialized as follows:
|
Let's say we have an array ''sentence'' which is initialized as follows:
|
||||||
<code>
|
<code>
|
||||||
sentence=(Be liberal in what you accept, and conservative in what you send)
|
sentence=(Be liberal in what you accept, and conservative in what you send)
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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:
|
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:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
$ echo ${#sentence[@]}
|
$ echo ${#sentence[@]}
|
||||||
12
|
12
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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)**:
|
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)**:
|
||||||
<code>
|
<code>
|
||||||
((n_elements=${#sentence[@]}, max_index=n_elements - 1))
|
((n_elements=${#sentence[@]}, max_index=n_elements - 1))
|
||||||
|
|
||||||
for ((i = 0; i <= max_index; i++)); do
|
for ((i = 0; i <= max_index; i++)); do
|
||||||
echo "Element $i: '${sentence[i]}'"
|
echo "Element $i: '${sentence[i]}'"
|
||||||
done
|
done
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
You always have to remember that, it seems newbies have problems sometimes. Please understand that **numerical array indexing begins at 0 (zero)**!
|
You always have to remember that, it seems newbies have problems sometimes. Please understand that **numerical array indexing begins at 0 (zero)**!
|
||||||
|
|
||||||
@ -153,48 +153,48 @@ The method above, walking through an array by just knowing its number of element
|
|||||||
|
|
||||||
Now, suppose that you want to replace your array ''sentence'' with the values in the [[#purpose|previously-declared array]] ''NAMES'' . You might think you could just do
|
Now, suppose that you want to replace your array ''sentence'' with the values in the [[#purpose|previously-declared array]] ''NAMES'' . You might think you could just do
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
$ unset sentence ; declare -a sentence=NAMES
|
$ unset sentence ; declare -a sentence=NAMES
|
||||||
$ echo ${#sentence[@]}
|
$ echo ${#sentence[@]}
|
||||||
1
|
1
|
||||||
# omit calculating max_index as above, and iterate as one-liner
|
# omit calculating max_index as above, and iterate as one-liner
|
||||||
$ for ((i = 0; i < ${#sentence[@]}; i++)); do echo "Element $i: '${sentence[i]}'" ; done
|
$ for ((i = 0; i < ${#sentence[@]}; i++)); do echo "Element $i: '${sentence[i]}'" ; done
|
||||||
Element 0: 'NAMES'
|
Element 0: 'NAMES'
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Obviously that's wrong. What about
|
Obviously that's wrong. What about
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
$ unset sentence ; declare -a sentence=${NAMES}
|
$ unset sentence ; declare -a sentence=${NAMES}
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
? Again, wrong:
|
? Again, wrong:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
$ echo ${#sentence[*]}
|
$ echo ${#sentence[*]}
|
||||||
1
|
1
|
||||||
$ for ((i = 0; i < ${#sentence[@]}; i++)); do echo "Element $i: '${sentence[i]}'" ; done
|
$ for ((i = 0; i < ${#sentence[@]}; i++)); do echo "Element $i: '${sentence[i]}'" ; done
|
||||||
Element 0: 'Peter'
|
Element 0: 'Peter'
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
So what's the **right** way? The (slightly ugly) answer is, reuse the enumeration syntax:
|
So what's the **right** way? The (slightly ugly) answer is, reuse the enumeration syntax:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
$ unset sentence ; declare -a sentence=("${NAMES[@]}")
|
$ unset sentence ; declare -a sentence=("${NAMES[@]}")
|
||||||
$ echo ${#sentence[@]}
|
$ echo ${#sentence[@]}
|
||||||
4
|
4
|
||||||
$ for ((i = 0; i < ${#sentence[@]}; i++)); do echo "Element $i: '${sentence[i]}'" ; done
|
$ for ((i = 0; i < ${#sentence[@]}; i++)); do echo "Element $i: '${sentence[i]}'" ; done
|
||||||
Element 0: 'Peter'
|
Element 0: 'Peter'
|
||||||
Element 1: 'Anna'
|
Element 1: 'Anna'
|
||||||
Element 2: 'Greg'
|
Element 2: 'Greg'
|
||||||
Element 3: 'Jan'
|
Element 3: 'Jan'
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== Associative (Bash 4) ====
|
==== 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:
|
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:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
# declare -A, introduced with Bash 4 to declare an associative array
|
# declare -A, introduced with Bash 4 to declare an associative array
|
||||||
declare -A sentence
|
declare -A sentence
|
||||||
|
|
||||||
@ -202,44 +202,44 @@ sentence[Begin]='Be liberal in what'
|
|||||||
sentence[Middle]='you accept, and conservative'
|
sentence[Middle]='you accept, and conservative'
|
||||||
sentence[End]='in what you send'
|
sentence[End]='in what you send'
|
||||||
sentence['Very end']=...
|
sentence['Very end']=...
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
__**Beware:**__ don't rely on the fact that the elements are ordered in memory like they were declared, it could look like this:
|
__**Beware:**__ don't rely on the fact that the elements are ordered in memory like they were declared, it could look like this:
|
||||||
<code>
|
<code>
|
||||||
# output from 'set' command
|
# output from 'set' command
|
||||||
sentence=([End]="in what you send" [Middle]="you accept, and conservative " [Begin]="Be liberal in what " ["Very end"]="...")
|
sentence=([End]="in what you send" [Middle]="you accept, and conservative " [Begin]="Be liberal in what " ["Very end"]="...")
|
||||||
</code>
|
</code>
|
||||||
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:
|
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:
|
||||||
<code>
|
<code>
|
||||||
for element in Begin Middle End "Very end"; do
|
for element in Begin Middle End "Very end"; do
|
||||||
printf "%s" "${sentence[$element]}"
|
printf "%s" "${sentence[$element]}"
|
||||||
done
|
done
|
||||||
printf "\n"
|
printf "\n"
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
|
|
||||||
**A nice code example:** Checking for duplicate files using an associative array indexed with the SHA sum of the files:
|
**A nice code example:** Checking for duplicate files using an associative array indexed with the SHA sum of the files:
|
||||||
<code>
|
<code>
|
||||||
# Thanks to Tramp in #bash for the idea and the code
|
# Thanks to Tramp in #bash for the idea and the code
|
||||||
|
|
||||||
unset flist; declare -A flist;
|
unset flist; declare -A flist;
|
||||||
while read -r sum fname; do
|
while read -r sum fname; do
|
||||||
if [[ ${flist[$sum]} ]]; then
|
if [[ ${flist[$sum]} ]]; then
|
||||||
printf 'rm -- "%s" # Same as >%s<\n' "$fname" "${flist[$sum]}"
|
printf 'rm -- "%s" # Same as >%s<\n' "$fname" "${flist[$sum]}"
|
||||||
else
|
else
|
||||||
flist[$sum]="$fname"
|
flist[$sum]="$fname"
|
||||||
fi
|
fi
|
||||||
done < <(find . -type f -exec sha256sum {} +) >rmdups
|
done < <(find . -type f -exec sha256sum {} +) >rmdups
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== Integer arrays ====
|
==== 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.
|
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.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
~ $ ( declare -ia 'a=(2+4 [2]=2+2 [a[2]]="a[2]")' 'a+=(42 [a[4]]+=3)'; declare -p a )
|
~ $ ( declare -ia 'a=(2+4 [2]=2+2 [a[2]]="a[2]")' 'a+=(42 [a[4]]+=3)'; declare -p a )
|
||||||
declare -ai a='([0]="6" [2]="4" [4]="7" [5]="42")'
|
declare -ai a='([0]="6" [2]="4" [4]="7" [5]="42")'
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
''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]''.
|
''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]''.
|
||||||
|
|
||||||
@ -251,25 +251,25 @@ Lastly, the element whose index is the value of ''a[4]'' (''4''), gets ''3'' add
|
|||||||
|
|
||||||
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.
|
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>
|
<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.
|
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>
|
</note>
|
||||||
|
|
||||||
==== Indirection ====
|
==== 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.
|
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.
|
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.
|
||||||
<code>
|
<code>
|
||||||
isSubset() {
|
isSubset() {
|
||||||
local -a 'xkeys=("${!'"$1"'[@]}")' 'ykeys=("${!'"$2"'[@]}")'
|
local -a 'xkeys=("${!'"$1"'[@]}")' 'ykeys=("${!'"$2"'[@]}")'
|
||||||
set -- "${@/%/[key]}"
|
set -- "${@/%/[key]}"
|
||||||
|
|
||||||
(( ${#xkeys[@]} <= ${#ykeys[@]} )) || return 1
|
(( ${#xkeys[@]} <= ${#ykeys[@]} )) || return 1
|
||||||
|
|
||||||
local key
|
local key
|
||||||
for key in "${xkeys[@]}"; do
|
for key in "${xkeys[@]}"; do
|
||||||
[[ ${!2+_} && ${!1} == ${!2} ]] || return 1
|
[[ ${!2+_} && ${!1} == ${!2} ]] || return 1
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -291,10 +291,10 @@ main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
main
|
main
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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.
|
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.
|
||||||
<code>
|
<code>
|
||||||
callFuncs() {
|
callFuncs() {
|
||||||
# Set up indirect references as positional parameters to minimize local name collisions.
|
# Set up indirect references as positional parameters to minimize local name collisions.
|
||||||
set -- "${@:1:3}" ${2+'a["$1"]' "$1"'["$2"]'}
|
set -- "${@:1:3}" ${2+'a["$1"]' "$1"'["$2"]'}
|
||||||
@ -326,23 +326,23 @@ main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
main "$@"
|
main "$@"
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Bugs and Portability Considerations =====
|
===== 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.
|
* 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.
|
* 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, 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.<code>
|
* 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.<code>
|
||||||
$ 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.
|
$ 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)
|
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.
|
$ 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)
|
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 -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
|
ksh: f: line 1: cannot append index array to associative array a
|
||||||
</code>
|
</code>
|
||||||
* 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.
|
* 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.) <code>
|
* 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.) <code>
|
||||||
$ 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
|
$ 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')
|
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.
|
$ 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.
|
||||||
@ -353,16 +353,16 @@ typeset a[0]=bork
|
|||||||
typeset a[1]=blarg
|
typeset a[1]=blarg
|
||||||
typeset a[2]=zooj
|
typeset a[2]=zooj
|
||||||
typeset a[3]=blah
|
typeset a[3]=blah
|
||||||
</code>
|
</code>
|
||||||
* 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]]. <code>
|
* 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]]. <code>
|
||||||
$ : ${_[$(echo $RANDOM >&2)1]:=$(echo hi >&2)}
|
$ : ${_[$(echo $RANDOM >&2)1]:=$(echo hi >&2)}
|
||||||
13574
|
13574
|
||||||
hi
|
hi
|
||||||
14485
|
14485
|
||||||
</code>
|
</code>
|
||||||
* In Zsh, arrays are indexed starting at 1 in its default mode. Emulation modes are required in order to get any kind of portability.
|
* 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''.
|
* 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. <code>
|
* 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. <code>
|
||||||
$ ksh -c 'set -A arr -- foo bar bork baz; typeset -p arr' # Classic array assignment syntax
|
$ ksh -c 'set -A arr -- foo bar bork baz; typeset -p arr' # Classic array assignment syntax
|
||||||
typeset -a arr=(foo bar bork baz)
|
typeset -a arr=(foo bar bork baz)
|
||||||
$ ksh -c 'set -sA arr -- foo bar bork baz; typeset -p arr' # Native sorting!
|
$ ksh -c 'set -sA arr -- foo bar bork baz; typeset -p arr' # Native sorting!
|
||||||
@ -373,7 +373,7 @@ typeset arr[2]=baz
|
|||||||
typeset arr[3]=bar
|
typeset arr[3]=bar
|
||||||
typeset arr[7]=bork
|
typeset arr[7]=bork
|
||||||
typeset arr[8]=foo
|
typeset arr[8]=foo
|
||||||
</code>
|
</code>
|
||||||
* 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.
|
* 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.
|
* 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.
|
* 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.
|
||||||
@ -382,14 +382,14 @@ typeset arr[8]=foo
|
|||||||
|
|
||||||
==== Bugs ====
|
==== 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. <code>
|
* **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. <code>
|
||||||
$ touch '[1]=a'; bash -c 'a=([1]=*); echo "${a[@]}"'
|
$ touch '[1]=a'; bash -c 'a=([1]=*); echo "${a[@]}"'
|
||||||
[1]=a
|
[1]=a
|
||||||
</code> mksh has a similar but even worse problem in that the entire subscript is considered a glob. <code>
|
</code> mksh has a similar but even worse problem in that the entire subscript is considered a glob. <code>
|
||||||
$ touch 1=a; mksh -c 'a=([123]=*); print -r -- "${a[@]}"'
|
$ touch 1=a; mksh -c 'a=([123]=*); print -r -- "${a[@]}"'
|
||||||
1=a
|
1=a
|
||||||
</code>
|
</code>
|
||||||
* **Fixed in 4.3** In addition to the above globbing issue, assignments preceding "declare" have an additional effect on brace and pathname expansion. <code>
|
* **Fixed in 4.3** In addition to the above globbing issue, assignments preceding "declare" have an additional effect on brace and pathname expansion. <code>
|
||||||
$ set -x; foo=bar declare arr=( {1..10} )
|
$ set -x; foo=bar declare arr=( {1..10} )
|
||||||
+ foo=bar
|
+ foo=bar
|
||||||
+ declare 'arr=(1)' 'arr=(2)' 'arr=(3)' 'arr=(4)' 'arr=(5)' 'arr=(6)' 'arr=(7)' 'arr=(8)' 'arr=(9)' 'arr=(10)'
|
+ declare 'arr=(1)' 'arr=(2)' 'arr=(3)' 'arr=(4)' 'arr=(5)' 'arr=(6)' 'arr=(7)' 'arr=(8)' 'arr=(9)' 'arr=(10)'
|
||||||
@ -401,25 +401,25 @@ $ declare x[y]=*
|
|||||||
$ foo=bar declare x[y]=*
|
$ foo=bar declare x[y]=*
|
||||||
+ foo=bar
|
+ foo=bar
|
||||||
+ declare xy=foo
|
+ declare xy=foo
|
||||||
</code> 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.
|
</code> 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. <code>
|
* **Fixed in 4.3** Indirection combined with another modifier expands arrays to a single word. <code>
|
||||||
$ a=({a..c}) b=a[@]; printf '<%s> ' "${!b}"; echo; printf '<%s> ' "${!b/%/foo}"; echo
|
$ a=({a..c}) b=a[@]; printf '<%s> ' "${!b}"; echo; printf '<%s> ' "${!b/%/foo}"; echo
|
||||||
<a> <b> <c>
|
<a> <b> <c>
|
||||||
<a b cfoo>
|
<a b cfoo>
|
||||||
</code>
|
</code>
|
||||||
* **Fixed in 4.3** Process substitutions are evaluated within array indexes. Zsh and ksh don't do this in any arithmetic context. <code>
|
* **Fixed in 4.3** Process substitutions are evaluated within array indexes. Zsh and ksh don't do this in any arithmetic context. <code>
|
||||||
# print "moo"
|
# print "moo"
|
||||||
dev=fd=1 _[1<(echo moo >&2)]=
|
dev=fd=1 _[1<(echo moo >&2)]=
|
||||||
|
|
||||||
# Fork bomb
|
# Fork bomb
|
||||||
${dev[${dev='dev[1>(${dev[dev]})]'}]}
|
${dev[${dev='dev[1>(${dev[dev]})]'}]}
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== Evaluation order ====
|
==== Evaluation order ====
|
||||||
|
|
||||||
Here are some of the nasty details of array assignment evaluation order. You can use this [[https://gist.github.com/ormaaj/4942297 | testcase code]] to generate these results.
|
Here are some of the nasty details of array assignment evaluation order. You can use this [[https://gist.github.com/ormaaj/4942297 | testcase code]] to generate these results.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
Each testcase prints evaluation order for indexed array assignment
|
Each testcase prints evaluation order for indexed array assignment
|
||||||
contexts. Each context is tested for expansions (represented by digits) and
|
contexts. Each context is tested for expansions (represented by digits) and
|
||||||
arithmetic (letters), ordered from left to right within the expression. The
|
arithmetic (letters), ordered from left to right within the expression. The
|
||||||
@ -473,7 +473,7 @@ zsh: 5.0.2
|
|||||||
1 2 b a
|
1 2 b a
|
||||||
1 2 b 3 c 2 b 4 5 e
|
1 2 b 3 c 2 b 4 5 e
|
||||||
1 2 b 3 2 b 4 5
|
1 2 b 3 2 b 4 5
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== See also =====
|
===== See also =====
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
====== Basic grammar rules of Bash ======
|
====== Basic grammar rules of Bash ======
|
||||||
|
|
||||||
{{keywords>bash shell scripting grammar syntax language}}
|
{{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.
|
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.
|
||||||
|
|
||||||
@ -8,29 +8,29 @@ If you don't know the commands used in the following examples, just trust the ex
|
|||||||
|
|
||||||
===== Simple Commands =====
|
===== Simple Commands =====
|
||||||
Bash manual says:
|
Bash manual says:
|
||||||
<code>
|
<code>
|
||||||
A simple command is a sequence of optional variable assignments followed by blank-separated words and redirections,
|
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
|
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.
|
zero. The remaining words are passed as arguments to the invoked command.
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Sounds harder than it actually is. It is what you do daily. You enter simple commands with parameters, and the shell executes them.
|
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:
|
Every complex Bash operation can be split into simple commands:
|
||||||
<code>
|
<code>
|
||||||
ls
|
ls
|
||||||
ls > list.txt
|
ls > list.txt
|
||||||
ls -l
|
ls -l
|
||||||
LC_ALL=C ls
|
LC_ALL=C ls
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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).
|
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.
|
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%>
|
<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 [[syntax:grammar:parser_exec | expand and execute a simple command]].
|
The simple command construct is the **base** for all higher constructs. Everything you execute, from pipelines to functions, finally ends up in (many) simple commands. That's why Bash only has one method to [[syntax:grammar:parser_exec | expand and execute a simple command]].
|
||||||
</wrap>
|
</wrap>
|
||||||
|
|
||||||
|
|
||||||
===== Pipelines =====
|
===== Pipelines =====
|
||||||
@ -41,16 +41,16 @@ FIXME Missing an additional article about pipelines and pipelining
|
|||||||
**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.
|
**Don't get confused** about the name "pipeline." It's a grammatic name for a construct. Such a pipeline isn't necessarily a pair of commands where stdout/stdin is connected via a real pipe.
|
||||||
|
|
||||||
Pipelines are one or more [[basicgrammar##simple_commands | simple commands]] (separated by the ''|'' symbol connects their input and output), for example:
|
Pipelines are one or more [[basicgrammar##simple_commands | simple commands]] (separated by the ''|'' symbol connects their input and output), for example:
|
||||||
<code>ls /etc | wc -l</code>
|
<code>ls /etc | wc -l</code>
|
||||||
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.
|
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.
|
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'':
|
In this example, the commands in the if stanza will be executed if the pattern "^root:" is **not** found in ''/etc/passwd'':
|
||||||
<code>
|
<code>
|
||||||
if ! grep '^root:' /etc/passwd; then
|
if ! grep '^root:' /etc/passwd; then
|
||||||
echo "No root user defined... eh?"
|
echo "No root user defined... eh?"
|
||||||
fi
|
fi
|
||||||
</code>
|
</code>
|
||||||
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.
|
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''".
|
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''".
|
||||||
|
|
||||||
@ -61,29 +61,29 @@ The shell option ''[[internals:shell_options#lastpipe | lastpipe]]'' will execut
|
|||||||
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.
|
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:
|
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:
|
||||||
<code>
|
<code>
|
||||||
# time updatedb
|
# time updatedb
|
||||||
real 3m21.288s
|
real 3m21.288s
|
||||||
user 0m3.114s
|
user 0m3.114s
|
||||||
sys 0m4.744s
|
sys 0m4.744s
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
|
|
||||||
===== Lists =====
|
===== Lists =====
|
||||||
FIXME Missing an additional article about list operators
|
FIXME Missing an additional article about list operators
|
||||||
|
|
||||||
A list is a sequence of one or more [[basicgrammar#pipelines | pipelines]] separated by one of the operators '';'', ''&'', ''&&'', or ''││'', and optionally terminated by one of '';'', ''&'', or ''<newline>''.
|
A list is a sequence of one or more [[basicgrammar#pipelines | pipelines]] separated by one of the operators '';'', ''&'', ''&&'', or ''││'', and optionally terminated by one of '';'', ''&'', or ''<newline>''.
|
||||||
|
|
||||||
=> It's a group of **pipelines** separated or terminated by **tokens** that all have **different meanings** for Bash.
|
=> 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!
|
Your whole Bash script technically is one big single list!
|
||||||
|
|
||||||
^Operator^Description^
|
^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> **<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|
|
|''<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|
|
|''<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 0 (TRUE), then ''<PIPELINE2>'' is executed (AND-List)|
|
||||||
|''<PIPELINE1> **<nowiki>||</nowiki>** <PIPELINE2>''|''<PIPELINE1>'' is executed and **only** if its exit code was **not** 0 (FALSE), then ''<PIPELINE2>'' is executed (OR-List)|
|
|''<PIPELINE1> **<nowiki>||</nowiki>** <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".
|
**Note:** POSIX calls this construct a "compound lists".
|
||||||
|
|
||||||
@ -100,17 +100,17 @@ Essentially, everything else that's not described in this article. Compound comm
|
|||||||
|
|
||||||
See the following table for a short overview (no details - just an overview):
|
See the following table for a short overview (no details - just an overview):
|
||||||
^Compound command syntax^Description^
|
^Compound command syntax^Description^
|
||||||
|''( <LIST> )''|Execute ''<LIST>'' in an extra subshell => [[syntax:ccmd:grouping_subshell | article]]|
|
|''( <LIST> )''|Execute ''<LIST>'' in an extra subshell => [[syntax:ccmd:grouping_subshell | article]]|
|
||||||
|''{ <LIST> ; }''|Execute ''<LIST>'' as separate group (but not in a subshell) => [[syntax:ccmd:grouping_plain | article]]|
|
|''{ <LIST> ; }''|Execute ''<LIST>'' as separate group (but not in a subshell) => [[syntax:ccmd:grouping_plain | article]]|
|
||||||
|''<nowiki>((</nowiki> <EXPRESSION> ))''|Evaluate the arithmetic expression ''<EXPRESSION>'' => [[syntax:ccmd:arithmetic_eval | article]]|
|
|''<nowiki>((</nowiki> <EXPRESSION> ))''|Evaluate the arithmetic expression ''<EXPRESSION>'' => [[syntax:ccmd:arithmetic_eval | article]]|
|
||||||
|''<nowiki>[[</nowiki> <EXPRESSION> ]]''|Evaluate the conditional expression ''<EXPRESSION>'' (aka "the new test command") => [[syntax:ccmd:conditional_expression | article]]|
|
|''<nowiki>[[</nowiki> <EXPRESSION> ]]''|Evaluate the conditional expression ''<EXPRESSION>'' (aka "the new test command") => [[syntax:ccmd:conditional_expression | article]]|
|
||||||
|''for <NAME> in <WORDS> ; do <LIST> ; done''|Executes ''<LIST>'' while setting the variable ''<NAME>'' to one of ''<WORDS>'' on every iteration (classic for-loop) => [[syntax:ccmd:classic_for | article]]|
|
|''for <NAME> in <WORDS> ; do <LIST> ; done''|Executes ''<LIST>'' while setting the variable ''<NAME>'' to one of ''<WORDS>'' on every iteration (classic for-loop) => [[syntax:ccmd:classic_for | article]]|
|
||||||
|''for <nowiki>((</nowiki> <EXPR1> ; <EXPR2> ; <EXPR3> )) ; do <LIST> ; done''|C-style for-loop (driven by arithmetic expressions) => [[syntax:ccmd:c_for | article]]|
|
|''for <nowiki>((</nowiki> <EXPR1> ; <EXPR2> ; <EXPR3> )) ; do <LIST> ; done''|C-style for-loop (driven by arithmetic expressions) => [[syntax:ccmd:c_for | article]]|
|
||||||
|''select <NAME> in <WORDS> ; do <LIST> ; done''|Provides simple menus => [[syntax:ccmd:user_select | article]]|
|
|''select <NAME> in <WORDS> ; do <LIST> ; done''|Provides simple menus => [[syntax:ccmd:user_select | article]]|
|
||||||
|''case <WORD> in <PATTERN>) <LIST> ;; ... esac''|Decisions based on pattern matching - executing ''<LIST>'' on match => [[syntax:ccmd:case | article]]|
|
|''case <WORD> in <PATTERN>) <LIST> ;; ... esac''|Decisions based on pattern matching - executing ''<LIST>'' on match => [[syntax:ccmd:case | article]]|
|
||||||
|''if <LIST> ; then <LIST> ; else <LIST> ; fi''|The if clause: makes decisions based on exit codes => [[syntax:ccmd:if_clause | article]]|
|
|''if <LIST> ; then <LIST> ; else <LIST> ; fi''|The if clause: makes decisions based on exit codes => [[syntax:ccmd:if_clause | article]]|
|
||||||
|''while <LIST1> ; do <LIST2> ; done''|Execute ''<LIST2>'' while ''<LIST1>'' returns TRUE (exit code) => [[syntax:ccmd:while_loop | article]]|
|
|''while <LIST1> ; do <LIST2> ; done''|Execute ''<LIST2>'' while ''<LIST1>'' returns TRUE (exit code) => [[syntax:ccmd:while_loop | article]]|
|
||||||
|''until <LIST1> ; do <LIST2> ; done''|Execute ''<LIST2>'' until ''<LIST1>'' returns TRUE (exit code) => [[syntax:ccmd:until_loop | article]]|
|
|''until <LIST1> ; do <LIST2> ; done''|Execute ''<LIST2>'' until ''<LIST1>'' returns TRUE (exit code) => [[syntax:ccmd:until_loop | article]]|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -122,34 +122,34 @@ A shell function definition makes a [[basicgrammar#compound_commands | compound
|
|||||||
|
|
||||||
The definition is easy (one of many possibilities):
|
The definition is easy (one of many possibilities):
|
||||||
|
|
||||||
''<NAME> () <COMPOUND_COMMAND> <REDIRECTIONS>''
|
''<NAME> () <COMPOUND_COMMAND> <REDIRECTIONS>''
|
||||||
|
|
||||||
which is usually used with the ''{...; }'' compound command, and thus looks like:
|
which is usually used with the ''{...; }'' compound command, and thus looks like:
|
||||||
<code>
|
<code>
|
||||||
print_help() { echo "Sorry, no help available"; }
|
print_help() { echo "Sorry, no help available"; }
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
As above, a function definition can have any [[basicgrammar#compound_commands | compound command]] as a body. Structures like
|
As above, a function definition can have any [[basicgrammar#compound_commands | compound command]] as a body. Structures like
|
||||||
<code>
|
<code>
|
||||||
countme() for ((x=1;x<=9;x++)); do echo $x; done
|
countme() for ((x=1;x<=9;x++)); do echo $x; done
|
||||||
</code>
|
</code>
|
||||||
are unusual, but perfectly valid, since the for loop construct is a compound command!
|
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:
|
If **redirection** is specified, the redirection is not performed when the function is defined. It is performed when the function runs:
|
||||||
<code>
|
<code>
|
||||||
# this will NOT perform the redirection (at definition time)
|
# this will NOT perform the redirection (at definition time)
|
||||||
f() { echo ok ; } > file
|
f() { echo ok ; } > file
|
||||||
|
|
||||||
# NOW the redirection will be performed (during EXECUTION of the function)
|
# NOW the redirection will be performed (during EXECUTION of the function)
|
||||||
f
|
f
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Bash allows three equivalent forms of the function definition:
|
Bash allows three equivalent forms of the function definition:
|
||||||
<code>
|
<code>
|
||||||
NAME () <COMPOUND_COMMAND> <REDIRECTIONS>
|
NAME () <COMPOUND_COMMAND> <REDIRECTIONS>
|
||||||
function NAME () <COMPOUND_COMMAND> <REDIRECTIONS>
|
function NAME () <COMPOUND_COMMAND> <REDIRECTIONS>
|
||||||
function NAME <COMPOUND_COMMAND> <REDIRECTIONS>
|
function NAME <COMPOUND_COMMAND> <REDIRECTIONS>
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
The space between ''NAME'' and ''()'' is optional, usually you see it without the space.
|
The space between ''NAME'' and ''()'' is optional, usually you see it without the space.
|
||||||
|
|
||||||
@ -159,7 +159,7 @@ __**Note:**__ Before version ''2.05-alpha1'', Bash only recognized the definitio
|
|||||||
|
|
||||||
To execute a function like a regular shell script you put it together like this:
|
To execute a function like a regular shell script you put it together like this:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# Add shebang
|
# Add shebang
|
||||||
|
|
||||||
@ -173,29 +173,29 @@ mycmd()
|
|||||||
mycmd "$1" # Execute command immediately after defining function
|
mycmd "$1" # Execute command immediately after defining function
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
**Just informational(1):**
|
**Just informational(1):**
|
||||||
|
|
||||||
Internally, for forking, Bash stores function definitions in environment variables. Variables with the content "//() ....//".
|
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:
|
Something similar to the following works without "officially" declaring a function:
|
||||||
<code>
|
<code>
|
||||||
$ export testfn="() { echo test; }"
|
$ export testfn="() { echo test; }"
|
||||||
$ bash -c testfn
|
$ bash -c testfn
|
||||||
test
|
test
|
||||||
$
|
$
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
**Just informational(2):**
|
**Just informational(2):**
|
||||||
|
|
||||||
It is possible to create function names containing slashes:
|
It is possible to create function names containing slashes:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
/bin/ls() {
|
/bin/ls() {
|
||||||
echo LS FAKE
|
echo LS FAKE
|
||||||
}
|
}
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
The elements of this name aren't subject to a path search.
|
The elements of this name aren't subject to a path search.
|
||||||
|
|
||||||
@ -221,32 +221,32 @@ FIXME more...
|
|||||||
|
|
||||||
----
|
----
|
||||||
__A (very) simple command__
|
__A (very) simple command__
|
||||||
<code>
|
<code>
|
||||||
echo "Hello world..."
|
echo "Hello world..."
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
__All of the following are simple commands__
|
__All of the following are simple commands__
|
||||||
<code>x=5</code>
|
<code>x=5</code>
|
||||||
<code>>tmpfile</code>
|
<code>>tmpfile</code>
|
||||||
<code>{x}<"$x" _=${x=<(echo moo)} <&0$(cat <&"$x" >&2)</code>
|
<code>{x}<"$x" _=${x=<(echo moo)} <&0$(cat <&"$x" >&2)</code>
|
||||||
----
|
----
|
||||||
__A common compound command__
|
__A common compound command__
|
||||||
<code>
|
<code>
|
||||||
if [ -d /data/mp3 ]; then
|
if [ -d /data/mp3 ]; then
|
||||||
cp mymusic.mp3 /data/mp3
|
cp mymusic.mp3 /data/mp3
|
||||||
fi
|
fi
|
||||||
</code>
|
</code>
|
||||||
* the [[basicgrammar#compound_commands | compound command]] for the ''if'' clause
|
* the [[basicgrammar#compound_commands | compound command]] for the ''if'' clause
|
||||||
* the [[basicgrammar#lists | list]] that ''if'' **checks** actually contains the [[basicgrammar#simple_commands | simple command]] ''[ -d /data/mp3 ]''
|
* the [[basicgrammar#lists | list]] that ''if'' **checks** actually contains the [[basicgrammar#simple_commands | simple command]] ''[ -d /data/mp3 ]''
|
||||||
* the [[basicgrammar#lists | list]] that ''if'' **executes** contains a simple command (''cp mymusic.mp3 /data/mp3'')
|
* the [[basicgrammar#lists | list]] that ''if'' **executes** contains a simple command (''cp mymusic.mp3 /data/mp3'')
|
||||||
|
|
||||||
|
|
||||||
Let's invert test command exit code, only one thing changes:
|
Let's invert test command exit code, only one thing changes:
|
||||||
<code>
|
<code>
|
||||||
if ! [ -d /data/mp3 ]; then
|
if ! [ -d /data/mp3 ]; then
|
||||||
cp mymusic.mp3 /data/mp3
|
cp mymusic.mp3 /data/mp3
|
||||||
fi
|
fi
|
||||||
</code>
|
</code>
|
||||||
* the [[basicgrammar#lists | list]] that ''if'' **checks** contains a [[basicgrammar#pipelines | pipeline]] now (because of the ''!'')
|
* the [[basicgrammar#lists | list]] that ''if'' **checks** contains a [[basicgrammar#pipelines | pipeline]] now (because of the ''!'')
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
====== Arithmetic evaluation (command) ======
|
====== Arithmetic evaluation (command) ======
|
||||||
|
|
||||||
===== Synopsis =====
|
===== Synopsis =====
|
||||||
<code>
|
<code>
|
||||||
(( <EXPRESSION> ))
|
(( <EXPRESSION> ))
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Description =====
|
===== Description =====
|
||||||
This command evaluates the [[syntax:arith_expr | arithmetic expression]] ''<EXPRESSION>''.
|
This command evaluates the [[syntax:arith_expr | arithmetic expression]] ''<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 [[syntax:arith_expr#arithmetic_expressions_and_return_codes | this section]].
|
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 [[syntax:arith_expr#arithmetic_expressions_and_return_codes | this section]].
|
||||||
|
|
||||||
|
@ -1,35 +1,35 @@
|
|||||||
====== The C-style for-loop ======
|
====== The C-style for-loop ======
|
||||||
|
|
||||||
===== Synopsis =====
|
===== Synopsis =====
|
||||||
<code>
|
<code>
|
||||||
for (( <EXPR1> ; <EXPR2> ; <EXPR3> )); do
|
for (( <EXPR1> ; <EXPR2> ; <EXPR3> )); do
|
||||||
<LIST>
|
<LIST>
|
||||||
done
|
done
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
# as a special case: without semicolon after ((...))
|
# as a special case: without semicolon after ((...))
|
||||||
for (( <EXPR1> ; <EXPR2> ; <EXPR3> )) do
|
for (( <EXPR1> ; <EXPR2> ; <EXPR3> )) do
|
||||||
<LIST>
|
<LIST>
|
||||||
done
|
done
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
# alternative, historical and undocumented syntax
|
# alternative, historical and undocumented syntax
|
||||||
for (( <EXPR1> ; <EXPR2> ; <EXPR3> )) {
|
for (( <EXPR1> ; <EXPR2> ; <EXPR3> )) {
|
||||||
<LIST>
|
<LIST>
|
||||||
}
|
}
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Description =====
|
===== Description =====
|
||||||
|
|
||||||
The C-style for-loop is a [[syntax/basicgrammar#compound_commands | compound command]] 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 C-style for-loop is a [[syntax/basicgrammar#compound_commands | compound command]] 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 ''<nowiki>((;;))</nowiki>'' syntax at the top of the loop is not an ordinary [[syntax/ccmd/arithmetic_eval | arithmetic compound command]], but is part of the C-style for-loop's own syntax. The three sections separated by semicolons are [[syntax:arith_expr | arithmetic expression]] 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.
|
The ''<nowiki>((;;))</nowiki>'' syntax at the top of the loop is not an ordinary [[syntax/ccmd/arithmetic_eval | arithmetic compound command]], but is part of the C-style for-loop's own syntax. The three sections separated by semicolons are [[syntax:arith_expr | arithmetic expression]] 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.
|
* ''<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.
|
* ''<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.
|
* ''<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).
|
:!: If one of these arithmetic expressions in the for-loop is empty, it behaves as if it would be 1 (**TRUE** in arithmetic context).
|
||||||
|
|
||||||
@ -38,72 +38,72 @@ The ''<nowiki>((;;))</nowiki>'' syntax at the top of the loop is not
|
|||||||
* Forced immediately to the next iteration using the [[commands/builtin/continuebreak | continue]] builtin, optionally as the ''continue N'' analog to ''break N''.
|
* Forced immediately to the next iteration using the [[commands/builtin/continuebreak | continue]] builtin, optionally as the ''continue N'' analog to ''break N''.
|
||||||
|
|
||||||
The equivalent construct using a [[syntax/ccmd/while_loop | while loop]] and the [[syntax:ccmd:arithmetic_eval | arithmetic expression compound command]] would be structured as:
|
The equivalent construct using a [[syntax/ccmd/while_loop | while loop]] and the [[syntax:ccmd:arithmetic_eval | arithmetic expression compound command]] would be structured as:
|
||||||
<code>
|
<code>
|
||||||
(( <EXPR1> ))
|
(( <EXPR1> ))
|
||||||
while (( <EXPR2> )); do
|
while (( <EXPR2> )); do
|
||||||
<LIST>
|
<LIST>
|
||||||
(( <EXPR3> ))
|
(( <EXPR3> ))
|
||||||
done
|
done
|
||||||
</code>
|
</code>
|
||||||
The equivalent ''while'' construct isn't exactly the same, because both, the ''for'' and the ''while'' loop behave differently in case you use the [[commands/builtin/continuebreak | continue]] command.
|
The equivalent ''while'' construct isn't exactly the same, because both, the ''for'' and the ''while'' loop behave differently in case you use the [[commands/builtin/continuebreak | continue]] command.
|
||||||
|
|
||||||
|
|
||||||
==== Alternate syntax ====
|
==== Alternate syntax ====
|
||||||
|
|
||||||
Bash, Ksh93, Mksh, and Zsh also provide an alternate syntax for the ''for'' loop - enclosing the loop body in ''{<nowiki>...</nowiki>}'' instead of ''do <nowiki>...</nowiki> done'':
|
Bash, Ksh93, Mksh, and Zsh also provide an alternate syntax for the ''for'' loop - enclosing the loop body in ''{<nowiki>...</nowiki>}'' instead of ''do <nowiki>...</nowiki> done'':
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
for ((x=1; x<=3; x++))
|
for ((x=1; x<=3; x++))
|
||||||
{
|
{
|
||||||
echo $x
|
echo $x
|
||||||
}
|
}
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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 [[syntax/ccmd/case#portability_considerations | case..esac]].
|
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 [[syntax/ccmd/case#portability_considerations | case..esac]].
|
||||||
|
|
||||||
==== Return status ====
|
==== Return status ====
|
||||||
|
|
||||||
The return status is that of the last command executed from ''<LIST>'', or ''FALSE'' if any of the arithmetic expressions failed.
|
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 =====
|
===== Alternatives and best practice =====
|
||||||
<div center round todo 60%>TODO: Show some alternate usages involving functions and local variables for initialization.</div>
|
<div center round todo 60%>TODO: Show some alternate usages involving functions and local variables for initialization.</div>
|
||||||
===== Examples =====
|
===== Examples =====
|
||||||
|
|
||||||
==== Simple counter ====
|
==== 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.
|
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''
|
* It **initializes** ''x = 0''
|
||||||
* Before every iteration it **checks** if ''x ≤ 100''
|
* Before every iteration it **checks** if ''x ≤ 100''
|
||||||
* After every iteration it **changes** ''x++''
|
* After every iteration it **changes** ''x++''
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
for ((x = 0 ; x <= 100 ; x++)); do
|
for ((x = 0 ; x <= 100 ; x++)); do
|
||||||
echo "Counter: $x"
|
echo "Counter: $x"
|
||||||
done
|
done
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== Stepping counter ====
|
==== 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**.
|
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**.
|
||||||
<code>
|
<code>
|
||||||
for ((x = 0 ; x <= 100 ; x += 10)); do
|
for ((x = 0 ; x <= 100 ; x += 10)); do
|
||||||
echo "Counter: $x"
|
echo "Counter: $x"
|
||||||
done
|
done
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== Bits analyzer ====
|
==== 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).
|
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).
|
||||||
<code>
|
<code>
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Example written for http://wiki.bash-hackers.org/syntax/ccmd/c_for#bits_analyzer
|
# Example written for http://wiki.bash-hackers.org/syntax/ccmd/c_for#bits_analyzer
|
||||||
# Based on TheBonsai's original.
|
# Based on TheBonsai's original.
|
||||||
|
|
||||||
function toBin {
|
function toBin {
|
||||||
typeset m=$1 n=2 x='x[(n*=2)>m]'
|
typeset m=$1 n=2 x='x[(n*=2)>m]'
|
||||||
for ((x = x; n /= 2;)); do
|
for ((x = x; n /= 2;)); do
|
||||||
printf %d $(( m & n && 1))
|
printf %d $(( m & n && 1))
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,7 +113,7 @@ function main {
|
|||||||
if (( $(ksh -c 'printf %..2d $1' _ "$1") == ( result = $(toBin "$1") ) )); then
|
if (( $(ksh -c 'printf %..2d $1' _ "$1") == ( result = $(toBin "$1") ) )); then
|
||||||
printf '%s is %s in base 2!\n' "$1" "$result"
|
printf '%s is %s in base 2!\n' "$1" "$result"
|
||||||
else
|
else
|
||||||
echo 'Oops, something went wrong with our calculation.' >&2
|
echo 'Oops, something went wrong with our calculation.' >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
@ -121,21 +121,21 @@ function main {
|
|||||||
main "${1:-123}"
|
main "${1:-123}"
|
||||||
|
|
||||||
# vim: set fenc=utf-8 ff=unix ft=sh :
|
# vim: set fenc=utf-8 ff=unix ft=sh :
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
<div hide>
|
<div hide>
|
||||||
<code>
|
<code>
|
||||||
testbyte=123
|
testbyte=123
|
||||||
for (( n = 128 ; n >= 1 ; n /= 2 )); do
|
for (( n = 128 ; n >= 1 ; n /= 2 )); do
|
||||||
if (( testbyte & n )); then
|
if (( testbyte & n )); then
|
||||||
printf %d 1
|
printf %d 1
|
||||||
else
|
else
|
||||||
printf %s 0
|
printf %s 0
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
echo
|
echo
|
||||||
</code>
|
</code>
|
||||||
</div>
|
</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...
|
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...
|
||||||
|
|
||||||
@ -145,12 +145,12 @@ We arrive at 128 for ''n'' through the recursive arithmetic expression stored in
|
|||||||
|
|
||||||
This counts up and down from ''0'' to ''${1:-5}'', ''${2:-4}'' times, demonstrating more complicated arithmetic expressions with multiple variables.
|
This counts up and down from ''0'' to ''${1:-5}'', ''${2:-4}'' times, demonstrating more complicated arithmetic expressions with multiple variables.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
for (( incr = 1, n=0, times = ${2:-4}, step = ${1:-5}; (n += incr) % step || (incr *= -1, --times);)); do
|
for (( incr = 1, n=0, times = ${2:-4}, step = ${1:-5}; (n += incr) % step || (incr *= -1, --times);)); do
|
||||||
printf '%*s\n' "$((n+1))" "$n"
|
printf '%*s\n' "$((n+1))" "$n"
|
||||||
done
|
done
|
||||||
</code>
|
</code>
|
||||||
<code> ~ $ bash <(xclip -o)
|
<code> ~ $ bash <(xclip -o)
|
||||||
1
|
1
|
||||||
2
|
2
|
||||||
3
|
3
|
||||||
@ -170,7 +170,7 @@ done
|
|||||||
3
|
3
|
||||||
2
|
2
|
||||||
1
|
1
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Portability considerations =====
|
===== Portability considerations =====
|
||||||
|
|
||||||
@ -179,7 +179,7 @@ done
|
|||||||
|
|
||||||
===== Bugs =====
|
===== Bugs =====
|
||||||
|
|
||||||
* //Fixed in 4.3//. <del>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.</del>
|
* //Fixed in 4.3//. <del>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.</del>
|
||||||
|
|
||||||
===== See also =====
|
===== See also =====
|
||||||
|
|
||||||
|
@ -1,29 +1,29 @@
|
|||||||
====== The case statement ======
|
====== The case statement ======
|
||||||
|
|
||||||
===== Synopsis =====
|
===== Synopsis =====
|
||||||
<code>
|
<code>
|
||||||
case <WORD> in
|
case <WORD> in
|
||||||
[(] <PATTERN1> ) <LIST1> ;; # or ;& or ;;& in Bash 4
|
[(] <PATTERN1> ) <LIST1> ;; # or ;& or ;;& in Bash 4
|
||||||
[(] <PATTERN2> ) <LIST2> ;;
|
[(] <PATTERN2> ) <LIST2> ;;
|
||||||
[(] <PATTERN3> | <PATTERN4> ) <LIST3-4> ;;
|
[(] <PATTERN3> | <PATTERN4> ) <LIST3-4> ;;
|
||||||
...
|
...
|
||||||
[(] <PATTERNn>) <LISTn> [;;]
|
[(] <PATTERNn>) <LISTn> [;;]
|
||||||
esac
|
esac
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Description =====
|
===== Description =====
|
||||||
The ''case''-statement can execute commands based on a [[syntax:pattern | pattern matching]] decision. The word ''<WORD>'' is matched against every pattern ''<PATTERNn>'' and on a match, the associated [[syntax:basicgrammar#lists | list]] ''<LISTn>'' is executed. Every commandlist is terminated by ''<nowiki>;;</nowiki>''. This rule is optional for the very last commandlist (i.e., you can omit the ''<nowiki>;;</nowiki>'' before the ''esac''). Every ''<PATTERNn>'' is separated from it's associated ''<LISTn>'' by a '')'', and is optionally preceded by a ''(''.
|
The ''case''-statement can execute commands based on a [[syntax:pattern | pattern matching]] decision. The word ''<WORD>'' is matched against every pattern ''<PATTERNn>'' and on a match, the associated [[syntax:basicgrammar#lists | list]] ''<LISTn>'' is executed. Every commandlist is terminated by ''<nowiki>;;</nowiki>''. This rule is optional for the very last commandlist (i.e., you can omit the ''<nowiki>;;</nowiki>'' 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.
|
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:
|
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:
|
||||||
<code>
|
<code>
|
||||||
var="test word"
|
var="test word"
|
||||||
|
|
||||||
case $var in
|
case $var in
|
||||||
...
|
...
|
||||||
esac
|
esac
|
||||||
</code>
|
</code>
|
||||||
This is similar to the behavior of the [[syntax:ccmd:conditional_expression | conditional expression command ("new test command")]] (also no word splitting for expansions).
|
This is similar to the behavior of the [[syntax:ccmd:conditional_expression | conditional expression command ("new test command")]] (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.)
|
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.)
|
||||||
@ -34,7 +34,7 @@ The ''case'' statement is one of the most difficult commands to indent clearly,
|
|||||||
|
|
||||||
===== Examples =====
|
===== Examples =====
|
||||||
Another one of my stupid examples...
|
Another one of my stupid examples...
|
||||||
<code>
|
<code>
|
||||||
printf '%s ' 'Which fruit do you like most?'
|
printf '%s ' 'Which fruit do you like most?'
|
||||||
read -${BASH_VERSION+e}r fruit
|
read -${BASH_VERSION+e}r fruit
|
||||||
|
|
||||||
@ -52,30 +52,30 @@ case $fruit in
|
|||||||
*)
|
*)
|
||||||
echo "Unknown fruit - sure it isn't toxic?"
|
echo "Unknown fruit - sure it isn't toxic?"
|
||||||
esac
|
esac
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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.
|
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.
|
||||||
<code bash>
|
<code bash>
|
||||||
# Set radeon power management
|
# Set radeon power management
|
||||||
function clk {
|
function clk {
|
||||||
typeset base=/sys/class/drm/card0/device
|
typeset base=/sys/class/drm/card0/device
|
||||||
[[ -r ${base}/hwmon/hwmon0/temp1_input && -r ${base}/power_profile ]] || return 1
|
[[ -r ${base}/hwmon/hwmon0/temp1_input && -r ${base}/power_profile ]] || return 1
|
||||||
|
|
||||||
case $1 in
|
case $1 in
|
||||||
low|high|default)
|
low|high|default)
|
||||||
printf '%s\n' "temp: $(<${base}/hwmon/hwmon0/temp1_input)C" "old profile: $(<${base}/power_profile)"
|
printf '%s\n' "temp: $(<${base}/hwmon/hwmon0/temp1_input)C" "old profile: $(<${base}/power_profile)"
|
||||||
echo "$1" >${base}/power_profile
|
echo "$1" >${base}/power_profile
|
||||||
echo "new profile: $(<${base}/power_profile)"
|
echo "new profile: $(<${base}/power_profile)"
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "Usage: $FUNCNAME [ low | high | default ]"
|
echo "Usage: $FUNCNAME [ low | high | default ]"
|
||||||
printf '%s\n' "temp: $(<${base}/hwmon/hwmon0/temp1_input)C" "current profile: $(<${base}/power_profile)"
|
printf '%s\n' "temp: $(<${base}/hwmon/hwmon0/temp1_input)C" "current profile: $(<${base}/power_profile)"
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
A template for experiments with ''case'' logic, showing shared code between blocks using '';&'', and the non-short-circuiting '';;&'' operator:
|
A template for experiments with ''case'' logic, showing shared code between blocks using '';&'', and the non-short-circuiting '';;&'' operator:
|
||||||
<code bash>
|
<code bash>
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
f() {
|
f() {
|
||||||
@ -85,9 +85,9 @@ f() {
|
|||||||
for x; do
|
for x; do
|
||||||
case $x in
|
case $x in
|
||||||
$1)
|
$1)
|
||||||
local "$x"'+=(1)' ;;&
|
local "$x"'+=(1)' ;;&
|
||||||
$2)
|
$2)
|
||||||
local "$x"'+=(2)' ;&
|
local "$x"'+=(2)' ;&
|
||||||
$3)
|
$3)
|
||||||
local "$x"'+=(3)' ;;
|
local "$x"'+=(3)' ;;
|
||||||
$1|$2)
|
$1|$2)
|
||||||
@ -107,13 +107,13 @@ f a b c
|
|||||||
# a: 1,4
|
# a: 1,4
|
||||||
# b: 2,3
|
# b: 2,3
|
||||||
# c: 3
|
# c: 3
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Portability considerations =====
|
===== Portability considerations =====
|
||||||
|
|
||||||
* Only the '';;'' delimiter is specified by POSIX.
|
* Only the '';;'' delimiter is specified by POSIX.
|
||||||
* zsh and mksh use the '';|'' control operator instead of Bash's '';;&''. Mksh has '';;&'' for Bash compatability (undocumented).
|
* 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 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 [[syntax/ccmd/classic_for | for loops]], but Bash doesn't support this syntax for ''case..esac''.
|
* 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 [[syntax/ccmd/classic_for | for loops]], but Bash doesn't support this syntax for ''case..esac''.
|
||||||
===== See also =====
|
===== See also =====
|
||||||
* [[http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_09_04_05|POSIX case conditional construct]]
|
* [[http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_09_04_05|POSIX case conditional construct]]
|
||||||
|
@ -1,32 +1,32 @@
|
|||||||
====== The classic for-loop ======
|
====== The classic for-loop ======
|
||||||
|
|
||||||
===== Synopsis =====
|
===== Synopsis =====
|
||||||
<code>
|
<code>
|
||||||
for <NAME>; do
|
for <NAME>; do
|
||||||
<LIST>
|
<LIST>
|
||||||
done
|
done
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
for <NAME> in <WORDS>; do
|
for <NAME> in <WORDS>; do
|
||||||
<LIST>
|
<LIST>
|
||||||
done
|
done
|
||||||
</code>
|
</code>
|
||||||
alternative, historical and undocumented syntax ((http://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xcu_chap02.html#tag_23_02_09_12))
|
alternative, historical and undocumented syntax ((http://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xcu_chap02.html#tag_23_02_09_12))
|
||||||
<code>
|
<code>
|
||||||
for <NAME>; {
|
for <NAME>; {
|
||||||
<LIST>
|
<LIST>
|
||||||
}
|
}
|
||||||
|
|
||||||
for <NAME> in <WORDS>; {
|
for <NAME> in <WORDS>; {
|
||||||
<LIST>
|
<LIST>
|
||||||
}
|
}
|
||||||
|
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Description =====
|
===== 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.
|
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.
|
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.
|
||||||
@ -36,53 +36,53 @@ If you use the loop-variable inside the for-loop and it can contain spaces, you
|
|||||||
* terminated (broken) by the ''break'' command, optionally as ''break N'' to break ''N'' levels of nested loops
|
* 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''
|
* 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 ''{<nowiki>...</nowiki>}'' instead of ''do <nowiki>...</nowiki> done'':
|
Bash knows an alternative syntax for the ''for'' loop, enclosing the loop body in ''{<nowiki>...</nowiki>}'' instead of ''do <nowiki>...</nowiki> done'':
|
||||||
<code bash>
|
<code bash>
|
||||||
for x in 1 2 3
|
for x in 1 2 3
|
||||||
{
|
{
|
||||||
echo $x
|
echo $x
|
||||||
}
|
}
|
||||||
</code>
|
</code>
|
||||||
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).
|
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 ====
|
==== 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"!).
|
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 =====
|
===== Examples =====
|
||||||
==== Iterate over array elements ====
|
==== Iterate over array elements ====
|
||||||
|
|
||||||
With some array syntax (see [[syntax:arrays]]) you can easily "feed" the for-loop to iterate over all elements in an array (by mass-expanding all elements):
|
With some array syntax (see [[syntax:arrays]]) you can easily "feed" the for-loop to iterate over all elements in an array (by mass-expanding all elements):
|
||||||
<code bash>
|
<code bash>
|
||||||
for element in "${myarray[@]}"; do
|
for element in "${myarray[@]}"; do
|
||||||
echo "Element: $element"
|
echo "Element: $element"
|
||||||
done
|
done
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Another way is to mass-expand all used indexes and access the array by index:
|
Another way is to mass-expand all used indexes and access the array by index:
|
||||||
<code bash>
|
<code bash>
|
||||||
for index in "${!myarray[@]}"; do
|
for index in "${!myarray[@]}"; do
|
||||||
echo "Element[$index]: ${myarray[$index]}"
|
echo "Element[$index]: ${myarray[$index]}"
|
||||||
done
|
done
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== List positional parameters ====
|
==== List positional parameters ====
|
||||||
You can use this [[syntax:basicgrammar#shell_function_definitions | function]] to test how arguments to a command will be interpreted and parsed, and finally used:
|
You can use this [[syntax:basicgrammar#shell_function_definitions | function]] to test how arguments to a command will be interpreted and parsed, and finally used:
|
||||||
<code bash>
|
<code bash>
|
||||||
argtest() {
|
argtest() {
|
||||||
n=1
|
n=1
|
||||||
for arg; do
|
for arg; do
|
||||||
echo "Argument $((n++)): \"$arg\""
|
echo "Argument $((n++)): \"$arg\""
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
|
|
||||||
==== Loop through a directory ====
|
==== 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:
|
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:
|
||||||
<code bash>
|
<code bash>
|
||||||
for fn in *; do
|
for fn in *; do
|
||||||
if [ -h "$fn" ]; then
|
if [ -h "$fn" ]; then
|
||||||
echo -n "Symlink: "
|
echo -n "Symlink: "
|
||||||
@ -95,19 +95,19 @@ for fn in *; do
|
|||||||
fi
|
fi
|
||||||
echo "$fn"
|
echo "$fn"
|
||||||
done
|
done
|
||||||
</code>
|
</code>
|
||||||
Stupid example, I know ;-)
|
Stupid example, I know ;-)
|
||||||
|
|
||||||
==== Loop over lines of output ====
|
==== 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:
|
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:
|
||||||
|
|
||||||
<code bash>
|
<code bash>
|
||||||
IFS=$'\n'
|
IFS=$'\n'
|
||||||
for f in $(ls); do
|
for f in $(ls); do
|
||||||
echo $f
|
echo $f
|
||||||
done
|
done
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
This is just an example. In //general//
|
This is just an example. In //general//
|
||||||
* it's not a good idea to parse ''ls(1)'' output
|
* it's not a good idea to parse ''ls(1)'' output
|
||||||
@ -115,33 +115,33 @@ This is just an example. In //general//
|
|||||||
|
|
||||||
==== Nested for-loops ====
|
==== 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:
|
It's of course possible to use another for-loop as ''<LIST>''. Here, counting from 0 to 99 in a weird way:
|
||||||
|
|
||||||
<code bash>
|
<code bash>
|
||||||
for x in 0 1 2 3 4 5 6 7 8 9; do
|
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
|
for y in 0 1 2 3 4 5 6 7 8 9; do
|
||||||
echo $x$y
|
echo $x$y
|
||||||
done
|
done
|
||||||
done
|
done
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== Loop over a number range ====
|
==== Loop over a number range ====
|
||||||
|
|
||||||
Beginning in Bash 4, you can also use "sequence expression" form of [[syntax:expansion:brace|brace expansion]] syntax when looping over numbers, and this form does not create leading zeroes unless you ask for them:
|
Beginning in Bash 4, you can also use "sequence expression" form of [[syntax:expansion:brace|brace expansion]] syntax when looping over numbers, and this form does not create leading zeroes unless you ask for them:
|
||||||
|
|
||||||
<code bash>
|
<code bash>
|
||||||
# 100 numbers, no leading zeroes
|
# 100 numbers, no leading zeroes
|
||||||
for x in {0..99}; do
|
for x in {0..99}; do
|
||||||
echo $x
|
echo $x
|
||||||
done
|
done
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
<code bash>
|
<code bash>
|
||||||
# Every other number, width 3
|
# Every other number, width 3
|
||||||
for x in {000..99..2}; do
|
for x in {000..99..2}; do
|
||||||
echo $x
|
echo $x
|
||||||
done
|
done
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
|
@ -1,40 +1,40 @@
|
|||||||
====== Grouping commands ======
|
====== Grouping commands ======
|
||||||
|
|
||||||
===== Synopsis =====
|
===== Synopsis =====
|
||||||
<code>{ <LIST>; }</code>
|
<code>{ <LIST>; }</code>
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
{
|
{
|
||||||
<LIST>
|
<LIST>
|
||||||
}
|
}
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Description =====
|
===== Description =====
|
||||||
|
|
||||||
The [[syntax:basicgrammar#lists|list]] ''<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! ((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))((The main reason is the fact that in shell grammar, the curly braces are not control operators but reserved words -- TheBonsai))
|
The [[syntax:basicgrammar#lists|list]] ''<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! ((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))((The main reason is the fact that in shell grammar, the curly braces are not control operators but reserved words -- TheBonsai))
|
||||||
|
|
||||||
This is known as a **group command**. The return status is the [[scripting:basics#exit_codes|exit status (exit code)]] of the list.
|
This is known as a **group command**. The return status is the [[scripting:basics#exit_codes|exit status (exit code)]] of the list.
|
||||||
|
|
||||||
The input and output **filedescriptors** are cumulative:
|
The input and output **filedescriptors** are cumulative:
|
||||||
<code>
|
<code>
|
||||||
{
|
{
|
||||||
echo "PASSWD follows"
|
echo "PASSWD follows"
|
||||||
cat /etc/passwd
|
cat /etc/passwd
|
||||||
echo
|
echo
|
||||||
echo "GROUPS follows"
|
echo "GROUPS follows"
|
||||||
cat /etc/group
|
cat /etc/group
|
||||||
} >output.txt
|
} >output.txt
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
This compound command also usually is the body of a [[syntax:basicgrammar#shell_function_definitions | function definition]], though not the only compound command that's valid there:
|
This compound command also usually is the body of a [[syntax:basicgrammar#shell_function_definitions | function definition]], though not the only compound command that's valid there:
|
||||||
<code>
|
<code>
|
||||||
print_help() {
|
print_help() {
|
||||||
echo "Options:"
|
echo "Options:"
|
||||||
echo "-h This help text"
|
echo "-h This help text"
|
||||||
echo "-f FILE Use config file FILE"
|
echo "-f FILE Use config file FILE"
|
||||||
echo "-u USER Run as user USER"
|
echo "-u USER Run as user USER"
|
||||||
}
|
}
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Examples =====
|
===== Examples =====
|
||||||
==== A Try-Catch block ====
|
==== A Try-Catch block ====
|
||||||
|
@ -1,30 +1,30 @@
|
|||||||
====== Grouping commands in a subshell ======
|
====== Grouping commands in a subshell ======
|
||||||
|
|
||||||
===== Synopsis =====
|
===== Synopsis =====
|
||||||
<code>
|
<code>
|
||||||
( <LIST> )
|
( <LIST> )
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
|
|
||||||
===== Description =====
|
===== Description =====
|
||||||
The [[syntax:basicgrammar#lists|list]] ''<LIST>'' is executed in a separate shell - a subprocess. No changes to the environment (variables etc...) are reflected in the "main shell".
|
The [[syntax:basicgrammar#lists|list]] ''<LIST>'' is executed in a separate shell - a subprocess. No changes to the environment (variables etc...) are reflected in the "main shell".
|
||||||
|
|
||||||
===== Examples =====
|
===== Examples =====
|
||||||
|
|
||||||
Execute a command in a different directory.
|
Execute a command in a different directory.
|
||||||
<code bash>
|
<code bash>
|
||||||
echo "$PWD"
|
echo "$PWD"
|
||||||
( cd /usr; echo "$PWD" )
|
( cd /usr; echo "$PWD" )
|
||||||
echo "$PWD" # Still in the original directory.
|
echo "$PWD" # Still in the original directory.
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Portability considerations =====
|
===== Portability considerations =====
|
||||||
|
|
||||||
* The subshell compound command is specified by POSIX.
|
* The subshell compound command is specified by POSIX.
|
||||||
* Avoid ambiguous syntax.
|
* Avoid ambiguous syntax.
|
||||||
<code bash>
|
<code bash>
|
||||||
(((1+1))) # Equivalent to: (( (1+1) ))
|
(((1+1))) # Equivalent to: (( (1+1) ))
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== See also =====
|
===== See also =====
|
||||||
* [[syntax:ccmd:grouping_plain | grouping commands]]
|
* [[syntax:ccmd:grouping_plain | grouping commands]]
|
||||||
|
@ -1,75 +1,75 @@
|
|||||||
====== The if-clause ======
|
====== The if-clause ======
|
||||||
|
|
||||||
===== Synopsis =====
|
===== Synopsis =====
|
||||||
<code>
|
<code>
|
||||||
if <LIST>; then
|
if <LIST>; then
|
||||||
<LIST>
|
<LIST>
|
||||||
fi
|
fi
|
||||||
</code>
|
</code>
|
||||||
<code>
|
<code>
|
||||||
if <LIST>; then
|
if <LIST>; then
|
||||||
<LIST>
|
<LIST>
|
||||||
else
|
else
|
||||||
<LIST>
|
<LIST>
|
||||||
fi
|
fi
|
||||||
</code>
|
</code>
|
||||||
<code>
|
<code>
|
||||||
if <LIST>; then
|
if <LIST>; then
|
||||||
<LIST>
|
<LIST>
|
||||||
elif <LIST>; then
|
elif <LIST>; then
|
||||||
<LIST>
|
<LIST>
|
||||||
else
|
else
|
||||||
<LIST>
|
<LIST>
|
||||||
fi
|
fi
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Description =====
|
===== Description =====
|
||||||
The ''if''-clause can control the script's flow (what's executed) by looking at the exit codes of other commands.
|
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 [[syntax:basicgrammar#lists | command lists]], thus they can contain the whole palette from [[syntax:basicgrammar#simple_commands | simple commands]] over [[syntax:basicgrammar#pipelines | pipelines]] to [[syntax:basicgrammar#compound_commands | compound commands]] (and their combination) as condition.
|
All commandsets ''<LIST>'' are interpreted as [[syntax:basicgrammar#lists | command lists]], thus they can contain the whole palette from [[syntax:basicgrammar#simple_commands | simple commands]] over [[syntax:basicgrammar#pipelines | pipelines]] to [[syntax:basicgrammar#compound_commands | compound commands]] (and their combination) as condition.
|
||||||
|
|
||||||
==== Operation ====
|
==== 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.
|
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.
|
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 =====
|
===== Examples =====
|
||||||
**Check if a specific user exists in /etc/passwd :-)**
|
**Check if a specific user exists in /etc/passwd :-)**
|
||||||
<code>
|
<code>
|
||||||
if grep ^myuser: /etc/passwd >/dev/null 2>&1; then
|
if grep ^myuser: /etc/passwd >/dev/null 2>&1; then
|
||||||
echo "Yes, it seems I'm real"
|
echo "Yes, it seems I'm real"
|
||||||
else
|
else
|
||||||
echo "Uh - am I a ghost?"
|
echo "Uh - am I a ghost?"
|
||||||
fi
|
fi
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
**Mount with check**
|
**Mount with check**
|
||||||
<code>
|
<code>
|
||||||
if ! mount /mnt/backup >/dev/null 2>&1; then
|
if ! mount /mnt/backup >/dev/null 2>&1; then
|
||||||
echo "FATAL: backup mount failed" >&2
|
echo "FATAL: backup mount failed" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
**Multiple commands as condition**
|
**Multiple commands as condition**
|
||||||
|
|
||||||
It's perfectly valid to do:
|
It's perfectly valid to do:
|
||||||
<code>
|
<code>
|
||||||
if echo "I'm testing!"; [ -e /some/file ]; then
|
if echo "I'm testing!"; [ -e /some/file ]; then
|
||||||
...
|
...
|
||||||
fi
|
fi
|
||||||
</code>
|
</code>
|
||||||
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 ]'')
|
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 as condition**
|
||||||
|
|
||||||
A complete pipe can also be used as condition. It's very similar to the example above (multiple commands):
|
A complete pipe can also be used as condition. It's very similar to the example above (multiple commands):
|
||||||
<code>
|
<code>
|
||||||
if echo "Hello world!" | grep -i hello >/dev/null 2>&1; then
|
if echo "Hello world!" | grep -i hello >/dev/null 2>&1; then
|
||||||
echo "You just said 'hello', yeah?"
|
echo "You just said 'hello', yeah?"
|
||||||
fi
|
fi
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Portability considerations =====
|
===== Portability considerations =====
|
||||||
|
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
====== The until loop ======
|
====== The until loop ======
|
||||||
|
|
||||||
===== Synopsis =====
|
===== Synopsis =====
|
||||||
<code>
|
<code>
|
||||||
until <LIST1> ; do
|
until <LIST1> ; do
|
||||||
<LIST2>
|
<LIST2>
|
||||||
done
|
done
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Description =====
|
===== Description =====
|
||||||
The until-loop is relatively simple in what it does: it executes the [[syntax:basicgrammar#lists | command list]] ''<LIST1>'' and if the exit code of it was **not** 0 (FALSE) it executes ''<LIST2>''. This happens again and again until ''<LIST1>'' returns TRUE.
|
The until-loop is relatively simple in what it does: it executes the [[syntax:basicgrammar#lists | command list]] ''<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 [[syntax:ccmd:while_loop | while loop]].
|
This is exactly the opposite of the [[syntax:ccmd:while_loop | while loop]].
|
||||||
|
|
||||||
@ -18,7 +18,7 @@ This is exactly the opposite of the [[syntax:ccmd:while_loop | while loop]].
|
|||||||
|
|
||||||
==== Return status ====
|
==== Return status ====
|
||||||
|
|
||||||
The return status is the one of the last command executed in ''<LIST2>'', or ''0'' (''TRUE'') if none was executed.
|
The return status is the one of the last command executed in ''<LIST2>'', or ''0'' (''TRUE'') if none was executed.
|
||||||
|
|
||||||
===== Examples =====
|
===== Examples =====
|
||||||
|
|
||||||
|
@ -1,46 +1,46 @@
|
|||||||
====== User selections ======
|
====== User selections ======
|
||||||
|
|
||||||
===== Synopsis =====
|
===== Synopsis =====
|
||||||
<code>
|
<code>
|
||||||
select <NAME>; do
|
select <NAME>; do
|
||||||
<LIST>
|
<LIST>
|
||||||
done
|
done
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
select <NAME> in <WORDS>; do
|
select <NAME> in <WORDS>; do
|
||||||
<LIST>
|
<LIST>
|
||||||
done
|
done
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
# alternative, historical and undocumented syntax
|
# alternative, historical and undocumented syntax
|
||||||
|
|
||||||
select <NAME>
|
select <NAME>
|
||||||
{
|
{
|
||||||
<LIST>
|
<LIST>
|
||||||
}
|
}
|
||||||
|
|
||||||
select <NAME> in <WORDS>
|
select <NAME> in <WORDS>
|
||||||
{
|
{
|
||||||
<LIST>
|
<LIST>
|
||||||
}
|
}
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Description =====
|
===== 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 [[syntax:basicgrammar#lists | list]] ''<LIST>'' is executed.
|
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 [[syntax:basicgrammar#lists | list]] ''<LIST>'' is executed.
|
||||||
|
|
||||||
If no ''in <WORDS>'' is given, then the positional parameters are taken as words (as if ''in "$@"'' was written).
|
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''.
|
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 ''{<nowiki>...</nowiki>}'' instead of ''do <nowiki>...</nowiki> done'':
|
Bash knows an alternative syntax for the ''select'' command, enclosing the loop body in ''{<nowiki>...</nowiki>}'' instead of ''do <nowiki>...</nowiki> done'':
|
||||||
<code>
|
<code>
|
||||||
select x in 1 2 3
|
select x in 1 2 3
|
||||||
{
|
{
|
||||||
echo $x
|
echo $x
|
||||||
}
|
}
|
||||||
</code>
|
</code>
|
||||||
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).
|
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 =====
|
===== Examples =====
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
====== The while-loop ======
|
====== The while-loop ======
|
||||||
|
|
||||||
===== Synopsis =====
|
===== Synopsis =====
|
||||||
<code>
|
<code>
|
||||||
while <LIST1> ; do
|
while <LIST1> ; do
|
||||||
<LIST2>
|
<LIST2>
|
||||||
done
|
done
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Description =====
|
===== Description =====
|
||||||
The while-loop is relatively simple in what it does: it executes the [[syntax:basicgrammar#lists | command list]] ''<LIST1>'' and if the exit code of it was 0 (TRUE) it executes ''<LIST2>''. This happens again and again until ''<LIST1>'' returns FALSE.
|
The while-loop is relatively simple in what it does: it executes the [[syntax:basicgrammar#lists | command list]] ''<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 [[syntax:ccmd:until_loop | until loop]].
|
This is exactly the opposite of the [[syntax:ccmd:until_loop | until loop]].
|
||||||
|
|
||||||
@ -18,7 +18,7 @@ This is exactly the opposite of the [[syntax:ccmd:until_loop | until loop]].
|
|||||||
|
|
||||||
==== Return status ====
|
==== Return status ====
|
||||||
|
|
||||||
The return status is the one of the last command executed in ''<LIST2>'', or ''0'' (''TRUE'') if none was executed.
|
The return status is the one of the last command executed in ''<LIST2>'', or ''0'' (''TRUE'') if none was executed.
|
||||||
|
|
||||||
===== Examples =====
|
===== Examples =====
|
||||||
|
|
||||||
|
@ -1,44 +1,44 @@
|
|||||||
====== Arithmetic expansion ======
|
====== Arithmetic expansion ======
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
$(( <EXPRESSION> ))
|
$(( <EXPRESSION> ))
|
||||||
|
|
||||||
$[ <EXPRESSION> ]
|
$[ <EXPRESSION> ]
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
The [[syntax:arith_expr | arithmetic expression]] ''<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.
|
The [[syntax:arith_expr | arithmetic expression]] ''<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 ''<nowiki>$(( ... ))</nowiki>''!
|
Please **do not use the second form ''$[ ... ]''**! It's deprecated. The preferred and standardized form is ''<nowiki>$(( ... ))</nowiki>''!
|
||||||
|
|
||||||
Example
|
Example
|
||||||
<code bash>
|
<code bash>
|
||||||
function printSum {
|
function printSum {
|
||||||
typeset -A args
|
typeset -A args
|
||||||
typeset name
|
typeset name
|
||||||
for name in first second; do
|
for name in first second; do
|
||||||
[[ -t 0 ]] && printf 'Enter %s positive integer: ' "$name" >&2
|
[[ -t 0 ]] && printf 'Enter %s positive integer: ' "$name" >&2
|
||||||
read -r ${BASH_VERSION+-e} "args[$name]"
|
read -r ${BASH_VERSION+-e} "args[$name]"
|
||||||
[[ ${args[$name]} == +([[:digit:]]) ]] || return 1 # Validation is extremely important whenever user input is used in arithmetic.
|
[[ ${args[$name]} == +([[:digit:]]) ]] || return 1 # Validation is extremely important whenever user input is used in arithmetic.
|
||||||
done
|
done
|
||||||
printf 'The sum is %d.' $((${args[first]} + ${args[second]}))
|
printf 'The sum is %d.' $((${args[first]} + ${args[second]}))
|
||||||
}
|
}
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
**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 [[syntax:ccmd:arithmetic_eval | arithmetic evaluation compound command]]:
|
**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 [[syntax:ccmd:arithmetic_eval | arithmetic evaluation compound command]]:
|
||||||
|
|
||||||
<code bash>
|
<code bash>
|
||||||
printf %s 'Enter a number: ' >&2
|
printf %s 'Enter a number: ' >&2
|
||||||
read -r number
|
read -r number
|
||||||
if ((number == 1234)); then
|
if ((number == 1234)); then
|
||||||
echo 'Good guess'
|
echo 'Good guess'
|
||||||
else
|
else
|
||||||
echo 'Haha... :-P'
|
echo 'Haha... :-P'
|
||||||
fi
|
fi
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
**Variables** used inside the arithmetic expansion, as in all arithmetic contexts, can be used with or without variable expansion:
|
**Variables** used inside the arithmetic expansion, as in all arithmetic contexts, can be used with or without variable expansion:
|
||||||
|
|
||||||
<code bash>
|
<code bash>
|
||||||
x=1
|
x=1
|
||||||
|
|
||||||
echo $((x)) # Good.
|
echo $((x)) # Good.
|
||||||
@ -48,7 +48,7 @@ echo $((x[0])) # Good.
|
|||||||
echo $((${x[0]})) # Ok. Nested expansion again.
|
echo $((${x[0]})) # Ok. Nested expansion again.
|
||||||
echo $((${x[$((${x[!$x]}-$x))]})) # Same as above but more ridiculous.
|
echo $((${x[$((${x[!$x]}-$x))]})) # Same as above but more ridiculous.
|
||||||
echo $(($x[0])) # Error. This expands to $((1[0])), an invalid expression.
|
echo $(($x[0])) # Error. This expands to $((1[0])), an invalid expression.
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Bugs and Portability considerations =====
|
===== 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''.
|
* 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''.
|
||||||
|
@ -1,33 +1,33 @@
|
|||||||
====== Brace expansion ======
|
====== Brace expansion ======
|
||||||
|
|
||||||
{{keywords>bash shell scripting expansion substitution text list brace}}
|
{{keywords>bash shell scripting expansion substitution text list brace}}
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
{string1,string2,...,stringN}
|
{string1,string2,...,stringN}
|
||||||
{<START>..<END>}
|
{<START>..<END>}
|
||||||
|
|
||||||
{<START>..<END>..<INCR>} (Bash 4)
|
{<START>..<END>..<INCR>} (Bash 4)
|
||||||
|
|
||||||
<PREFIX>{........}
|
<PREFIX>{........}
|
||||||
|
|
||||||
{........}<SUFFIX>
|
{........}<SUFFIX>
|
||||||
|
|
||||||
<PREFIX>{........}<SUFFIX>
|
<PREFIX>{........}<SUFFIX>
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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.
|
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.
|
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
|
:!: It is the very first step in expansion-handling, it's important to understand that. When you use
|
||||||
<code>
|
<code>
|
||||||
echo {a,b}$PATH
|
echo {a,b}$PATH
|
||||||
</code>
|
</code>
|
||||||
then the brace expansion **does not expand the variable** - this is done in a **later step**. Brace expansion just makes it being:
|
then the brace expansion **does not expand the variable** - this is done in a **later step**. Brace expansion just makes it being:
|
||||||
<code>
|
<code>
|
||||||
echo a$PATH b$PATH
|
echo a$PATH b$PATH
|
||||||
</code>
|
</code>
|
||||||
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: <code>eval echo {$a..$b}</code> For instance, when embedded inside a for loop : <code>for i in $(eval echo {$a..$b})</code> 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 [[commands:builtin:declare|declaration commands]]: <code>declare -a 'pics=(img{'"$a..$b"'}.png)'; mv "${pics[@]}" ../imgs</code> 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.
|
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: <code>eval echo {$a..$b}</code> For instance, when embedded inside a for loop : <code>for i in $(eval echo {$a..$b})</code> 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 [[commands:builtin:declare|declaration commands]]: <code>declare -a 'pics=(img{'"$a..$b"'}.png)'; mv "${pics[@]}" ../imgs</code> 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**.
|
The brace expansion is present in two basic forms, **string lists** and **ranges**.
|
||||||
|
|
||||||
@ -35,18 +35,18 @@ It can be switched on and off under runtime by using the ''set'' builtin and the
|
|||||||
|
|
||||||
===== String lists =====
|
===== String lists =====
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
{string1,string2,...,stringN}
|
{string1,string2,...,stringN}
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Without the optional prefix and suffix strings, the result is just a space-separated list of the given strings:
|
Without the optional prefix and suffix strings, the result is just a space-separated list of the given strings:
|
||||||
<code>
|
<code>
|
||||||
$ echo {I,want,my,money,back}
|
$ echo {I,want,my,money,back}
|
||||||
I want my money back
|
I want my money back
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
With prefix or suffix strings, the result is a space-separated list of **all possible combinations** of prefix or suffix specified strings:
|
With prefix or suffix strings, the result is a space-separated list of **all possible combinations** of prefix or suffix specified strings:
|
||||||
<code>
|
<code>
|
||||||
$ echo _{I,want,my,money,back}
|
$ echo _{I,want,my,money,back}
|
||||||
_I _want _my _money _back
|
_I _want _my _money _back
|
||||||
|
|
||||||
@ -55,54 +55,54 @@ I_ want_ my_ money_ back_
|
|||||||
|
|
||||||
$ echo _{I,want,my,money,back}-
|
$ echo _{I,want,my,money,back}-
|
||||||
_I- _want- _my- _money- _back-
|
_I- _want- _my- _money- _back-
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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}''".
|
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 =====
|
===== Ranges =====
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
{<START>..<END>}
|
{<START>..<END>}
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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
|
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)
|
* integers (optionally zero padded, optionally with a given increment)
|
||||||
* characters
|
* characters
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
$ echo {5..12}
|
$ echo {5..12}
|
||||||
5 6 7 8 9 10 11 12
|
5 6 7 8 9 10 11 12
|
||||||
|
|
||||||
$ echo {c..k}
|
$ echo {c..k}
|
||||||
c d e f g h i j k
|
c d e f g h i j k
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
When you mix these both types, brace expansion is **not** performed:
|
When you mix these both types, brace expansion is **not** performed:
|
||||||
<code>
|
<code>
|
||||||
$ echo {5..k}
|
$ echo {5..k}
|
||||||
{5..k}
|
{5..k}
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
When you zero pad one of the numbers (or both) in a range, then the generated range is zero padded, too:
|
When you zero pad one of the numbers (or both) in a range, then the generated range is zero padded, too:
|
||||||
<code>
|
<code>
|
||||||
$ echo {01..10}
|
$ echo {01..10}
|
||||||
01 02 03 04 05 06 07 08 09 10
|
01 02 03 04 05 06 07 08 09 10
|
||||||
</code>
|
</code>
|
||||||
There's a chapter of Bash 4 brace expansion changes at [[#new_in_bash_4.0 | the end of this article]].
|
There's a chapter of Bash 4 brace expansion changes at [[#new_in_bash_4.0 | the end of this article]].
|
||||||
|
|
||||||
Similar to the expansion using stringlists, you can add prefix and suffix strings:
|
Similar to the expansion using stringlists, you can add prefix and suffix strings:
|
||||||
<code>
|
<code>
|
||||||
$ echo 1.{0..9}
|
$ echo 1.{0..9}
|
||||||
1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9
|
1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9
|
||||||
|
|
||||||
$ echo ---{A..E}---
|
$ echo ---{A..E}---
|
||||||
---A--- ---B--- ---C--- ---D--- ---E---
|
---A--- ---B--- ---C--- ---D--- ---E---
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Combining and nesting =====
|
===== 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:
|
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:
|
||||||
<code>
|
<code>
|
||||||
$ echo {A..Z}{0..9}
|
$ 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
|
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
|
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
|
||||||
@ -114,16 +114,16 @@ 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
|
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
|
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
|
Y3 Y4 Y5 Y6 Y7 Y8 Y9 Z0 Z1 Z2 Z3 Z4 Z5 Z6 Z7 Z8 Z9
|
||||||
</code>
|
</code>
|
||||||
Hey.. that **saves you writing** 260 strings!
|
Hey.. that **saves you writing** 260 strings!
|
||||||
|
|
||||||
Brace expansions can be nested, but too much of it usually makes you losing overview a bit ;-)
|
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:
|
Here's a sample to generate the alphabet, first the uppercase letters, then the lowercase ones:
|
||||||
<code>
|
<code>
|
||||||
$ echo {{A..Z},{a..z}}
|
$ 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
|
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
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Common use and examples =====
|
===== Common use and examples =====
|
||||||
|
|
||||||
@ -132,15 +132,15 @@ In this example, ''wget'' is used to download documentation that is split over s
|
|||||||
|
|
||||||
''wget'' won't see your braces. It will see **6 different URLs** to download.
|
''wget'' won't see your braces. It will see **6 different URLs** to download.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
wget http://docs.example.com/documentation/slides_part{1,2,3,4,5,6}.html
|
wget http://docs.example.com/documentation/slides_part{1,2,3,4,5,6}.html
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Of course it's possible, and even easier, to do that with a sequence:
|
Of course it's possible, and even easier, to do that with a sequence:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
wget http://docs.example.com/documentation/slides_part{1..6}.html
|
wget http://docs.example.com/documentation/slides_part{1..6}.html
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -148,88 +148,88 @@ wget http://docs.example.com/documentation/slides_part{1..6}.html
|
|||||||
|
|
||||||
Your life is hard? Let's ease it a bit - that's what shells are here for.
|
Your life is hard? Let's ease it a bit - that's what shells are here for.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
mkdir /home/bash/test/{foo,bar,baz,cat,dog}
|
mkdir /home/bash/test/{foo,bar,baz,cat,dog}
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
==== Generate numbers with a prefix 001 002 ... ====
|
==== Generate numbers with a prefix 001 002 ... ====
|
||||||
* Using a prefix:
|
* Using a prefix:
|
||||||
<code>
|
<code>
|
||||||
for i in 0{1..9} 10; do printf "%s\n" "$i";done
|
for i in 0{1..9} 10; do printf "%s\n" "$i";done
|
||||||
</code>
|
</code>
|
||||||
If you need to create words with the number embedded, you can use nested brace:
|
If you need to create words with the number embedded, you can use nested brace:
|
||||||
<code>
|
<code>
|
||||||
printf "%s\n" img{00{1..9},0{10..99},{100..999}}.png
|
printf "%s\n" img{00{1..9},0{10..99},{100..999}}.png
|
||||||
</code>
|
</code>
|
||||||
* Formatting the numbers with printf:
|
* Formatting the numbers with printf:
|
||||||
<code>
|
<code>
|
||||||
echo $(printf "img%02d.png " {1..99})
|
echo $(printf "img%02d.png " {1..99})
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
See the [[#news_in_bash_4.0 | text below]] for a new Bash 4 method.
|
See the [[#news_in_bash_4.0 | text below]] for a new Bash 4 method.
|
||||||
|
|
||||||
==== Repeating arguments or words ====
|
==== Repeating arguments or words ====
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
somecommand -v -v -v -v -v
|
somecommand -v -v -v -v -v
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Can be written as
|
Can be written as
|
||||||
<code>
|
<code>
|
||||||
somecommand -v{,,,,}
|
somecommand -v{,,,,}
|
||||||
</code>
|
</code>
|
||||||
...which is a kind of a hack, but hey, it works.
|
...which is a kind of a hack, but hey, it works.
|
||||||
|
|
||||||
<div round info>
|
<div round info>
|
||||||
=== More fun ===
|
=== 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.
|
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.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
function braceify {
|
function braceify {
|
||||||
[[ $1 == +([[:digit:]]) ]] || return
|
[[ $1 == +([[:digit:]]) ]] || return
|
||||||
typeset -a a
|
typeset -a a
|
||||||
read -ra a < <(factor "$1")
|
read -ra a < <(factor "$1")
|
||||||
eval "echo $(printf '{$(printf ,%%.s {1..%s})}' "${a[@]:1}")"
|
eval "echo $(printf '{$(printf ,%%.s {1..%s})}' "${a[@]:1}")"
|
||||||
}
|
}
|
||||||
|
|
||||||
printf 'eval printf "$arg"%s' "$(braceify 1000000)"
|
printf 'eval printf "$arg"%s' "$(braceify 1000000)"
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
"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:
|
"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:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
eval printf "$arg"{,,}{,,}{,,}{,,}{,,}{,,}{,,,,,}{,,,,,}{,,,,,}{,,,,,}{,,,,,}{,,,,,}
|
eval printf "$arg"{,,}{,,}{,,}{,,}{,,}{,,}{,,,,,}{,,,,,}{,,,,,}{,,,,,}{,,,,,}{,,,,,}
|
||||||
</code>
|
</code>
|
||||||
</div>
|
</div>
|
||||||
===== New in Bash 4.0 =====
|
===== New in Bash 4.0 =====
|
||||||
|
|
||||||
==== Zero padded number expansion ====
|
==== 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:
|
Prefix either of the numbers in a numeric range with ''0'' to pad the expanded numbers with the correct amount of zeros:
|
||||||
<code>
|
<code>
|
||||||
$ echo {0001..5}
|
$ echo {0001..5}
|
||||||
0001 0002 0003 0004 0005
|
0001 0002 0003 0004 0005
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== Increment ====
|
==== Increment ====
|
||||||
It is now possible to specify an increment using ranges:
|
It is now possible to specify an increment using ranges:
|
||||||
<code>
|
<code>
|
||||||
{<START>..<END>..<INCR>}
|
{<START>..<END>..<INCR>}
|
||||||
</code>
|
</code>
|
||||||
''<INCR>'' is numeric, you can use a negative integer but the correct sign is deduced from the order of ''<START>'' and ''<END>'' anyways.
|
''<INCR>'' is numeric, you can use a negative integer but the correct sign is deduced from the order of ''<START>'' and ''<END>'' anyways.
|
||||||
<code>
|
<code>
|
||||||
$ echo {1..10..2}
|
$ echo {1..10..2}
|
||||||
1 3 5 7 9
|
1 3 5 7 9
|
||||||
$ echo {10..1..2}
|
$ echo {10..1..2}
|
||||||
10 8 6 4 2
|
10 8 6 4 2
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Interesting feature: The increment specification also works for letter-ranges:
|
Interesting feature: The increment specification also works for letter-ranges:
|
||||||
<code>
|
<code>
|
||||||
$ echo {a..z..3}
|
$ echo {a..z..3}
|
||||||
a d g j m p s v y
|
a d g j m p s v y
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== See also =====
|
===== See also =====
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
====== Command substitution ======
|
====== Command substitution ======
|
||||||
|
|
||||||
{{keywords>bash shell scripting expansion substitution text variable output execute stdout save result return value}}
|
{{keywords>bash shell scripting expansion substitution text variable output execute stdout save result return value}}
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
$( <COMMANDS> )
|
$( <COMMANDS> )
|
||||||
|
|
||||||
` <COMMANDS> `
|
` <COMMANDS> `
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
@ -17,22 +17,22 @@ In later steps, **if not quoted**, the results undergo [[syntax:expansion:wordsp
|
|||||||
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!
|
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 [[syntax:ccmd:grouping_subshell | call an explicit subshell]] ''(COMMAND)'' inside the command substitution ''$()'', then take care, this way is **wrong**:
|
When you [[syntax:ccmd:grouping_subshell | call an explicit subshell]] ''(COMMAND)'' inside the command substitution ''$()'', then take care, this way is **wrong**:
|
||||||
<code>
|
<code>
|
||||||
$((COMMAND))
|
$((COMMAND))
|
||||||
</code>
|
</code>
|
||||||
Why? because it collides with the syntax for [[syntax:expansion:arith | arithmetic expansion]]. You need to separate the command substitution from the inner ''(COMMAND)'':
|
Why? because it collides with the syntax for [[syntax:expansion:arith | arithmetic expansion]]. You need to separate the command substitution from the inner ''(COMMAND)'':
|
||||||
<code>
|
<code>
|
||||||
$( (COMMAND) )
|
$( (COMMAND) )
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Specialities =====
|
===== Specialities =====
|
||||||
|
|
||||||
When the inner command is only an input redirection, and nothing else, for example
|
When the inner command is only an input redirection, and nothing else, for example
|
||||||
<code>
|
<code>
|
||||||
$( <FILE )
|
$( <FILE )
|
||||||
# or
|
# or
|
||||||
` <FILE `
|
` <FILE `
|
||||||
</code>
|
</code>
|
||||||
then Bash attempts to read the given file and act just if the given command was ''cat FILE''.
|
then Bash attempts to read the given file and act just if the given command was ''cat FILE''.
|
||||||
|
|
||||||
|
|
||||||
@ -44,37 +44,37 @@ In general you really should only use the form ''$()'', it's escaping-neutral, i
|
|||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
echo `echo `ls`` # INCORRECT
|
echo `echo `ls`` # INCORRECT
|
||||||
echo `echo \`ls\`` # CORRECT
|
echo `echo \`ls\`` # CORRECT
|
||||||
echo $(echo $(ls)) # CORRECT
|
echo $(echo $(ls)) # CORRECT
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
**__Parsing__**
|
**__Parsing__**
|
||||||
|
|
||||||
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:
|
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:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
echo "$(echo "$(ls)")" # nested double-quotes - no problem
|
echo "$(echo "$(ls)")" # nested double-quotes - no problem
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
**__Constructs you should avoid__**
|
**__Constructs you should avoid__**
|
||||||
|
|
||||||
It's not all shiny with ''$()'', at least for my current Bash (''3.1.17(1)-release''. :!: __**Update:** Fixed since ''3.2-beta'' together with a misinterpretion of '))' being recognized as arithmetic expansion [by redduck666]__). This command seems to incorrectly close the substitution step and echo prints "ls" and ")":
|
It's not all shiny with ''$()'', at least for my current Bash (''3.1.17(1)-release''. :!: __**Update:** Fixed since ''3.2-beta'' together with a misinterpretion of '))' being recognized as arithmetic expansion [by redduck666]__). This command seems to incorrectly close the substitution step and echo prints "ls" and ")":
|
||||||
<code>
|
<code>
|
||||||
echo $(
|
echo $(
|
||||||
# some comment ending with a )
|
# some comment ending with a )
|
||||||
ls
|
ls
|
||||||
)
|
)
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
It seems that every closing ")" confuses this construct. Also a (very uncommon ;-)) construct like:
|
It seems that every closing ")" confuses this construct. Also a (very uncommon ;-)) construct like:
|
||||||
<code>
|
<code>
|
||||||
echo $(read VAR; case "$var" in foo) blah ;; esac) # spits out some error, when it sees the ";;"
|
echo $(read VAR; case "$var" in foo) blah ;; esac) # spits out some error, when it sees the ";;"
|
||||||
|
|
||||||
# fixes it:
|
# fixes it:
|
||||||
echo $(read VAR; case "$var" in (foo) blah ;; esac) # will work, but just let it be, please ;-)
|
echo $(read VAR; case "$var" in (foo) blah ;; esac) # will work, but just let it be, please ;-)
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
**__Conclusion:__**
|
**__Conclusion:__**
|
||||||
|
|
||||||
@ -88,21 +88,21 @@ In general, the ''$()'' should be the preferred method:
|
|||||||
===== Examples =====
|
===== Examples =====
|
||||||
|
|
||||||
**To get the date:**
|
**To get the date:**
|
||||||
<code>
|
<code>
|
||||||
DATE="$(date)"
|
DATE="$(date)"
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
**To copy a file and get ''cp'' error output:**
|
**To copy a file and get ''cp'' error output:**
|
||||||
<code>
|
<code>
|
||||||
COPY_OUTPUT="$(cp file.txt /some/where 2>&1)"
|
COPY_OUTPUT="$(cp file.txt /some/where 2>&1)"
|
||||||
</code>
|
</code>
|
||||||
Attention: Here, you need to redirect ''cp'' ''STDERR'' to its ''STDOUT'' target, because command substitution only catches ''STDOUT''!
|
Attention: Here, you need to redirect ''cp'' ''STDERR'' to its ''STDOUT'' target, because command substitution only catches ''STDOUT''!
|
||||||
|
|
||||||
**Catch stdout and preserve trailing newlines:**
|
**Catch stdout and preserve trailing newlines:**
|
||||||
<code>
|
<code>
|
||||||
var=$(echo -n $'\n'); echo -n "$var"; # $var == ""
|
var=$(echo -n $'\n'); echo -n "$var"; # $var == ""
|
||||||
var=$(echo -n $'\n'; echo -n x); var="${var%x}"; echo -n "$var" # $var == "\n"
|
var=$(echo -n $'\n'; echo -n x); var="${var%x}"; echo -n "$var" # $var == "\n"
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
This adds "x" to the output, which prevents the trailing newlines of the previous commands' output from being deleted by $().
|
This adds "x" to the output, which prevents the trailing newlines of the previous commands' output from being deleted by $().
|
||||||
|
|
||||||
|
@ -3,9 +3,9 @@
|
|||||||
===== General =====
|
===== 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''):
|
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''):
|
||||||
<code>
|
<code>
|
||||||
grep "changes:" *.log
|
grep "changes:" *.log
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
The base syntax for the pathname expansion is the [[syntax:pattern | pattern matching]] syntax. The pattern you describe is matched against all existing filenames and the matching ones are substituted. Since this substitution happens **after [[syntax:expansion:wordsplit | word splitting]]**, all resulting filenames are literal and treated as separate words, no matter how many spaces or other ''IFS''-characters they contain.
|
The base syntax for the pathname expansion is the [[syntax:pattern | pattern matching]] syntax. The pattern you describe is matched against all existing filenames and the matching ones are substituted. Since this substitution happens **after [[syntax:expansion:wordsplit | word splitting]]**, all resulting filenames are literal and treated as separate words, no matter how many spaces or other ''IFS''-characters they contain.
|
||||||
|
|
||||||
@ -27,37 +27,37 @@ The base syntax for the pathname expansion is the [[syntax:pattern | pattern mat
|
|||||||
* when the shell option ''[[internals:shell_options#nocaseglob | nocaseglob]]'' is set, the match is performed case-insensitive
|
* when the shell option ''[[internals:shell_options#nocaseglob | nocaseglob]]'' is set, the match is performed case-insensitive
|
||||||
* when the shell option ''[[internals:shell_options#dotglob | dotglob]]'' is set, wildcard-characters can match a dot at the beginning of a filename
|
* when the shell option ''[[internals:shell_options#dotglob | dotglob]]'' is set, wildcard-characters can match a dot at the beginning of a filename
|
||||||
* when the shell option ''[[internals:shell_options#dirspell | dirspell]]'' is set, Bash performs spelling corrections when matching directory names
|
* when the shell option ''[[internals:shell_options#dirspell | dirspell]]'' is set, Bash performs spelling corrections when matching directory names
|
||||||
* when the shell option ''[[internals:shell_options#globstar | globstar]]'' is set, the glob ''<nowiki>**</nowiki>'' will recursively match all files and directories. This glob isn't "configurable", i.e. you **can't** do something like ''<nowiki>**</nowiki>.c'' to recursively get all ''*.c'' filenames.
|
* when the shell option ''[[internals:shell_options#globstar | globstar]]'' is set, the glob ''<nowiki>**</nowiki>'' will recursively match all files and directories. This glob isn't "configurable", i.e. you **can't** do something like ''<nowiki>**</nowiki>.c'' to recursively get all ''*.c'' filenames.
|
||||||
* when the shell option ''[[internals:shell_options#globasciiranges | 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
|
* when the shell option ''[[internals:shell_options#globasciiranges | 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 [[syntax:shellvars#GLOBIGNORE|GLOBIGNORE]] can be set to a colon-separated list of patterns to be removed from the list before it is returned
|
* the variable [[syntax:shellvars#GLOBIGNORE|GLOBIGNORE]] can be set to a colon-separated list of patterns to be removed from the list before it is returned
|
||||||
|
|
||||||
==== nullglob ====
|
==== nullglob ====
|
||||||
|
|
||||||
Normally, when no glob specified matches an existing filename, no pathname expansion is performed, and the globs are __**not**__ removed:
|
Normally, when no glob specified matches an existing filename, no pathname expansion is performed, and the globs are __**not**__ removed:
|
||||||
<code>
|
<code>
|
||||||
$ echo "Textfiles here:" *.txt
|
$ echo "Textfiles here:" *.txt
|
||||||
Textfiles here: *.txt
|
Textfiles here: *.txt
|
||||||
</code>
|
</code>
|
||||||
In this example, no files matched the pattern, so the glob was left intact (a literal asterisk, followed by dot-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 [[syntax:ccmd:classic_for | for-loop]] using the pathname expansion:
|
This can be very annoying, for example when you drive a [[syntax:ccmd:classic_for | for-loop]] using the pathname expansion:
|
||||||
<code>
|
<code>
|
||||||
for filename in *.txt; do
|
for filename in *.txt; do
|
||||||
echo "=== BEGIN: $filename ==="
|
echo "=== BEGIN: $filename ==="
|
||||||
cat "$filename"
|
cat "$filename"
|
||||||
echo "=== END: $filename ==="
|
echo "=== END: $filename ==="
|
||||||
done
|
done
|
||||||
</code>
|
</code>
|
||||||
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.
|
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 ''[[internals:shell_options#nullglob | 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.
|
Now, when the shell option ''[[internals:shell_options#nullglob | 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:
|
So in our first example:
|
||||||
<code>
|
<code>
|
||||||
$ shopt -s nullglob
|
$ shopt -s nullglob
|
||||||
$ echo "Textfiles here:" *.txt
|
$ echo "Textfiles here:" *.txt
|
||||||
Textfiles here:
|
Textfiles here:
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
and the glob is gone.
|
and the glob is gone.
|
||||||
|
|
||||||
@ -73,7 +73,7 @@ and the glob is gone.
|
|||||||
|
|
||||||
For example, to match something beginning with either 'S' or 'K' followed by two numbers, followed by at least 3 more characters:
|
For example, to match something beginning with either 'S' or 'K' followed by two numbers, followed by at least 3 more characters:
|
||||||
|
|
||||||
<code>[SK][0-9][0-9]???*</code>
|
<code>[SK][0-9][0-9]???*</code>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
====== Expansions and substitutions ======
|
====== Expansions and substitutions ======
|
||||||
|
|
||||||
{{keywords>bash shell scripting expansion substitution text variable filename macro wildcard}}
|
{{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).
|
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:
|
The most simple example of this behaviour is a referenced variable:
|
||||||
<code>
|
<code>
|
||||||
mystring="Hello world"
|
mystring="Hello world"
|
||||||
echo "$mystring"
|
echo "$mystring"
|
||||||
</code>
|
</code>
|
||||||
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!
|
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., [[syntax:quoting | the quotes that marked contiguous words]], 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**.
|
After all these expansions and substitutions are done, all quotes that are not meant literally (i.e., [[syntax:quoting | the quotes that marked contiguous words]], 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**.
|
||||||
@ -24,7 +24,7 @@ Saw a possible expansion syntax but don't know what it is? Here's a small list.
|
|||||||
* ''*.txt''
|
* ''*.txt''
|
||||||
* ''page_1?.html''
|
* ''page_1?.html''
|
||||||
* [[syntax:expansion:arith | Arithmetic expansion]]
|
* [[syntax:expansion:arith | Arithmetic expansion]]
|
||||||
* ''<nowiki>$(( EXPRESSION ))</nowiki>''
|
* ''<nowiki>$(( EXPRESSION ))</nowiki>''
|
||||||
* ''$[ EXPRESSION ]''
|
* ''$[ EXPRESSION ]''
|
||||||
* [[syntax:expansion:cmdsubst | Command substitution]]
|
* [[syntax:expansion:cmdsubst | Command substitution]]
|
||||||
* ''$( COMMAND )''
|
* ''$( COMMAND )''
|
||||||
@ -38,8 +38,8 @@ Saw a possible expansion syntax but don't know what it is? Here's a small list.
|
|||||||
* ''{X..Y}''
|
* ''{X..Y}''
|
||||||
* ''{X..Y..Z}''
|
* ''{X..Y..Z}''
|
||||||
* [[syntax:expansion:proc_subst | Process substitution]]
|
* [[syntax:expansion:proc_subst | Process substitution]]
|
||||||
* ''<( COMMAND )''
|
* ''<( COMMAND )''
|
||||||
* ''>( COMMAND )''
|
* ''>( COMMAND )''
|
||||||
|
|
||||||
===== Order =====
|
===== 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!).
|
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!).
|
||||||
@ -58,8 +58,8 @@ The order is (from first to last):
|
|||||||
[[syntax:expansion:proc_subst | Process substitution]] is performed **simultaneously** with [[syntax:pe | parameter expansion]], [[syntax:expansion:cmdsubst | command substitution]] and [[syntax:expansion:arith | arithmetic expansion]]. It is only performed when the underlying operating system supports it.
|
[[syntax:expansion:proc_subst | Process substitution]] is performed **simultaneously** with [[syntax:pe | parameter expansion]], [[syntax:expansion:cmdsubst | command substitution]] and [[syntax:expansion:arith | arithmetic expansion]]. It is only performed when the underlying operating system supports it.
|
||||||
|
|
||||||
The 3 steps [[syntax:pe | parameter expansion]], [[syntax:expansion:arith | arithmetic expansion]] and [[syntax:expansion:cmdsubst | command substitution]] happen at the same time in a left-to-right fashion on nthe commandline. This means
|
The 3 steps [[syntax:pe | parameter expansion]], [[syntax:expansion:arith | arithmetic expansion]] and [[syntax:expansion:cmdsubst | command substitution]] happen at the same time in a left-to-right fashion on nthe commandline. This means
|
||||||
<code>
|
<code>
|
||||||
i=1
|
i=1
|
||||||
echo $i $((i++)) $i
|
echo $i $((i++)) $i
|
||||||
</code>
|
</code>
|
||||||
will output ''1 1 2'' and not ''1 1 1''.
|
will output ''1 1 2'' and not ''1 1 1''.
|
||||||
|
@ -1,29 +1,29 @@
|
|||||||
====== Process substitution ======
|
====== Process substitution ======
|
||||||
|
|
||||||
{{keywords>bash shell scripting expansion substitution text stdin stdout save capture}}
|
{{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.
|
Process substitution is a form of redirection where the input or output of a process (some sequence of commands) appear as a temporary file.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
<( <LIST> )
|
<( <LIST> )
|
||||||
|
|
||||||
>( <LIST> )
|
>( <LIST> )
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Process substitution is performed **simultaneously** with [[syntax:pe | parameter expansion]], [[syntax:expansion:cmdsubst | command substitution]] and [[syntax:expansion:arith | arithmetic expansion]].
|
Process substitution is performed **simultaneously** with [[syntax:pe | parameter expansion]], [[syntax:expansion:cmdsubst | command substitution]] and [[syntax:expansion:arith | arithmetic expansion]].
|
||||||
|
|
||||||
The [[syntax:basicgrammar#lists | command list]] ''<LIST>'' is executed and its
|
The [[syntax:basicgrammar#lists | command list]] ''<LIST>'' is executed and its
|
||||||
* standard output filedescriptor in the ''<( ... )'' form or
|
* standard output filedescriptor in the ''<( ... )'' form or
|
||||||
* standard input filedescriptor in the ''>( ... )'' form
|
* 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.
|
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).
|
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 ====
|
==== Scope ====
|
||||||
<note important>
|
<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.
|
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 [[https://unix.stackexchange.com/questions/425456/conditional-process-substitution|stackexchange]] and [[https://stackoverflow.com/questions/46660020/bash-what-is-the-scope-of-the-process-substitution|stackoverflow]]; the latter discussion contains a script that can test the scoping behavior case-by-case
|
See [[https://unix.stackexchange.com/questions/425456/conditional-process-substitution|stackexchange]] and [[https://stackoverflow.com/questions/46660020/bash-what-is-the-scope-of-the-process-substitution|stackoverflow]]; the latter discussion contains a script that can test the scoping behavior case-by-case
|
||||||
</note>
|
</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.
|
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.
|
||||||
|
|
||||||
@ -32,30 +32,30 @@ In essence, process substitutions expanded to variables within functions remain
|
|||||||
===== Examples =====
|
===== Examples =====
|
||||||
|
|
||||||
This code is useless, but it demonstrates how it works:
|
This code is useless, but it demonstrates how it works:
|
||||||
<code bash>
|
<code bash>
|
||||||
$ echo <(ls)
|
$ echo <(ls)
|
||||||
/dev/fd/63
|
/dev/fd/63
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
The **output** of the ''ls''-program can then be accessed by reading the file ''/dev/fd/63''.
|
The **output** of the ''ls''-program can then be accessed by reading the file ''/dev/fd/63''.
|
||||||
|
|
||||||
Consider the following:
|
Consider the following:
|
||||||
<code bash>
|
<code bash>
|
||||||
diff <(ls "$first_directory") <(ls "$second_directory")
|
diff <(ls "$first_directory") <(ls "$second_directory")
|
||||||
</code>
|
</code>
|
||||||
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
|
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
|
||||||
<code bash>
|
<code bash>
|
||||||
diff /dev/fd/63 /dev/fd/64
|
diff /dev/fd/63 /dev/fd/64
|
||||||
</code>
|
</code>
|
||||||
where those files are written to and destroyed automatically.
|
where those files are written to and destroyed automatically.
|
||||||
==== Avoiding subshells ====
|
==== Avoiding subshells ====
|
||||||
<WRAP center round info 60%>
|
<WRAP center round info 60%>
|
||||||
See Also: [[http://mywiki.wooledge.org/BashFAQ/024 | 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?//
|
See Also: [[http://mywiki.wooledge.org/BashFAQ/024 | 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>
|
</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:
|
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:
|
||||||
|
|
||||||
<code bash>
|
<code bash>
|
||||||
counter=0
|
counter=0
|
||||||
|
|
||||||
find /etc -print0 | while IFS= read -rd '' _; do
|
find /etc -print0 | while IFS= read -rd '' _; do
|
||||||
@ -63,41 +63,41 @@ find /etc -print0 | while IFS= read -rd '' _; do
|
|||||||
done
|
done
|
||||||
|
|
||||||
echo "$counter files" # prints "0 files"
|
echo "$counter files" # prints "0 files"
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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"!
|
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):
|
Process substitution helps us avoid the pipe operator (the reason for the subshell):
|
||||||
|
|
||||||
<code bash>
|
<code bash>
|
||||||
counter=0
|
counter=0
|
||||||
|
|
||||||
while IFS= read -rN1 _; do
|
while IFS= read -rN1 _; do
|
||||||
((counter++))
|
((counter++))
|
||||||
done < <(find /etc -printf ' ')
|
done < <(find /etc -printf ' ')
|
||||||
|
|
||||||
echo "$counter files"
|
echo "$counter files"
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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 [[syntax:redirection#here_documents | here documents]].
|
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 [[syntax:redirection#here_documents | here documents]].
|
||||||
|
|
||||||
<code bash>
|
<code bash>
|
||||||
: < <(COMMAND) # Good.
|
: < <(COMMAND) # Good.
|
||||||
: <<(...) # Wrong. Will be parsed as a heredoc. Bash fails when it comes across the unquoted metacharacter ''(''
|
: <<(...) # 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.
|
: ><(...) # 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.
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== Process substitution assigned to a parameter ====
|
==== 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.
|
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.
|
||||||
|
|
||||||
<code bash>
|
<code bash>
|
||||||
f() {
|
f() {
|
||||||
cat "$1" >"$x"
|
cat "$1" >"$x"
|
||||||
}
|
}
|
||||||
|
|
||||||
x=>(tr '[:lower:]' '[:upper:]') f <(echo 'hi there')
|
x=>(tr '[:lower:]' '[:upper:]') f <(echo 'hi there')
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
See the above section on [[#scope]]
|
See the above section on [[#scope]]
|
||||||
|
|
||||||
@ -108,12 +108,12 @@ See the above section on [[#scope]]
|
|||||||
* Process substitution is implemented by Bash, Zsh, Ksh{88,93}, but not (yet) pdksh derivatives (mksh). Coprocesses may be used instead.
|
* 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 [[dict:terms:special_file | 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.
|
* Process substitution is supported only on systems that support either named pipes (FIFO - a [[dict:terms:special_file | 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 evaluates process substitutions within array indices, but not other arithmetic contexts. Ksh and Zsh do not. (Possible Bug)
|
||||||
<code bash>
|
<code bash>
|
||||||
# print "moo"
|
# print "moo"
|
||||||
dev=fd=1 _[1<(echo moo >&2)]=
|
dev=fd=1 _[1<(echo moo >&2)]=
|
||||||
# fork bomb
|
# fork bomb
|
||||||
${dev[${dev='dev[1>(${dev[dev]})]'}]}
|
${dev[${dev='dev[1>(${dev[dev]})]'}]}
|
||||||
</code>
|
</code>
|
||||||
* Issues with wait, race conditions, etc: https://groups.google.com/forum/?fromgroups=#!topic/comp.unix.shell/GqLNzUA4ulA
|
* Issues with wait, race conditions, etc: https://groups.google.com/forum/?fromgroups=#!topic/comp.unix.shell/GqLNzUA4ulA
|
||||||
|
|
||||||
===== See also =====
|
===== See also =====
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
====== Tilde expansion ======
|
====== Tilde expansion ======
|
||||||
|
|
||||||
{{keywords>bash shell scripting expansion substitution tilde home homedir shortcut}}
|
{{keywords>bash shell scripting expansion substitution tilde home homedir shortcut}}
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
~
|
~
|
||||||
~/...
|
~/...
|
||||||
|
|
||||||
@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
~-
|
~-
|
||||||
~-/...
|
~-/...
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
The tilde expansion is used to expand to several specific pathnames:
|
The tilde expansion is used to expand to several specific pathnames:
|
||||||
* home directories
|
* home directories
|
||||||
@ -29,35 +29,35 @@ Tilde expansion is also performed everytime a variable is assigned:
|
|||||||
* after the **first** ''='': ''TARGET=~moonman/share''
|
* after the **first** ''='': ''TARGET=~moonman/share''
|
||||||
* after **every** '':'' (colon) in the assigned value: ''TARGET=file:~moonman/share''
|
* after **every** '':'' (colon) in the assigned value: ''TARGET=file:~moonman/share''
|
||||||
|
|
||||||
<note info>
|
<note info>
|
||||||
As of now (Bash 4.3-alpha) the following constructs **also** works, though it's not a variable assignment:
|
As of now (Bash 4.3-alpha) the following constructs **also** works, though it's not a variable assignment:
|
||||||
<code>
|
<code>
|
||||||
echo foo=~
|
echo foo=~
|
||||||
echo foo=:~
|
echo foo=:~
|
||||||
</code>
|
</code>
|
||||||
I don't know yet, if this is a bug or intended.
|
I don't know yet, if this is a bug or intended.
|
||||||
</note>
|
</note>
|
||||||
|
|
||||||
This way you can correctly use the tilde expansion in your [[syntax:shellvars#PATH|PATH]]:
|
This way you can correctly use the tilde expansion in your [[syntax:shellvars#PATH|PATH]]:
|
||||||
<code>
|
<code>
|
||||||
PATH=~/mybins:~peter/mybins:$PATH
|
PATH=~/mybins:~peter/mybins:$PATH
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
**Spaces in the referenced pathes?** A construct like...
|
**Spaces in the referenced pathes?** A construct like...
|
||||||
<code>
|
<code>
|
||||||
~/"my directory"
|
~/"my directory"
|
||||||
</code>
|
</code>
|
||||||
...is perfectly valid and works!
|
...is perfectly valid and works!
|
||||||
|
|
||||||
===== Home directory =====
|
===== Home directory =====
|
||||||
<code>
|
<code>
|
||||||
~
|
~
|
||||||
~<NAME>
|
~<NAME>
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
This form expands to the home-directory of the current user (''~'') or the home directory of the given user (''~<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>''.
|
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:
|
To find the home directory of the current user (''~''), Bash has a precedence:
|
||||||
* expand to the value of [[syntax:shellvars#HOME|HOME]] if it's defined
|
* expand to the value of [[syntax:shellvars#HOME|HOME]] if it's defined
|
||||||
@ -66,35 +66,35 @@ That means, the variable ''HOME'' can override the "real" home directo
|
|||||||
|
|
||||||
|
|
||||||
===== Current working directory =====
|
===== Current working directory =====
|
||||||
<code>
|
<code>
|
||||||
~+
|
~+
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
This expands to the value of the [[syntax:shellvars#PWD|PWD]] variable, which holds the currect working directory:
|
This expands to the value of the [[syntax:shellvars#PWD|PWD]] variable, which holds the currect working directory:
|
||||||
<code>
|
<code>
|
||||||
echo "CWD is $PWD"
|
echo "CWD is $PWD"
|
||||||
</code>
|
</code>
|
||||||
is equivalent to (note it **must** be a separate word!):
|
is equivalent to (note it **must** be a separate word!):
|
||||||
<code>
|
<code>
|
||||||
echo "CWD is" ~+
|
echo "CWD is" ~+
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
|
|
||||||
===== Previous working directory =====
|
===== Previous working directory =====
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
~-
|
~-
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
This expands to the value of the [[syntax:shellvars#OLDPWD|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.
|
This expands to the value of the [[syntax:shellvars#OLDPWD|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.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
$ pwd
|
$ pwd
|
||||||
/home/bash
|
/home/bash
|
||||||
$ cd /etc
|
$ cd /etc
|
||||||
$ echo ~-
|
$ echo ~-
|
||||||
/home/bash
|
/home/bash
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
|
|
||||||
===== See also =====
|
===== See also =====
|
||||||
|
@ -11,9 +11,9 @@ Bash will scan the results of these expansions for special ''IFS'' characters th
|
|||||||
===== Internal Field Separator IFS =====
|
===== Internal Field Separator IFS =====
|
||||||
|
|
||||||
The ''IFS'' variable holds the characters that Bash sees as word boundaries in this step. The default contains the characters
|
The ''IFS'' variable holds the characters that Bash sees as word boundaries in this step. The default contains the characters
|
||||||
* <space>
|
* <space>
|
||||||
* <tab>
|
* <tab>
|
||||||
* <newline>
|
* <newline>
|
||||||
These characters are also assumed when IFS is **unset**. When ''IFS'' is **empty** (nullstring), no word splitting is performed at all.
|
These characters are also assumed when IFS is **unset**. When ''IFS'' is **empty** (nullstring), no word splitting is performed at all.
|
||||||
|
|
||||||
|
|
||||||
@ -25,7 +25,7 @@ The results of the expansions mentioned above are scanned for ''IFS''-characters
|
|||||||
|
|
||||||
This doesn't happen when the expansion results were **double-quoted**.
|
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 (''<nowiki>''</nowiki>'' or ''""'').
|
When a null-string (e.g., something that before expanded to >>nothing<<) is found, it is removed, unless it is quoted (''<nowiki>''</nowiki>'' or ''""'').
|
||||||
|
|
||||||
__**Again note:**__ Without any expansion beforehand, Bash won't perform word splitting! In this case, the initial token parsing is solely responsible.
|
__**Again note:**__ Without any expansion beforehand, Bash won't perform word splitting! In this case, the initial token parsing is solely responsible.
|
||||||
|
|
||||||
|
@ -2,16 +2,16 @@ FIXME work in progress...
|
|||||||
|
|
||||||
====== Parsing and execution ======
|
====== Parsing and execution ======
|
||||||
|
|
||||||
{{keywords>bash shell scripting syntax language behaviour executing execution}}
|
{{keywords>bash shell scripting syntax language behaviour executing execution}}
|
||||||
|
|
||||||
Nearly everything in [[syntax:basicgrammar | Bash grammar]] can be broken down to a "simple command". The only thing Bash has to expand, evaluate and execute is the simple command.
|
Nearly everything in [[syntax:basicgrammar | Bash grammar]] 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 =====
|
===== Simple command expansion =====
|
||||||
<div center round info 60%>
|
<div center round info 60%>
|
||||||
* http://lists.gnu.org/archive/html/bug-bash/2013-01/msg00040.html
|
* http://lists.gnu.org/archive/html/bug-bash/2013-01/msg00040.html
|
||||||
* http://lists.research.att.com/pipermail/ast-developers/2013q2/002456.html
|
* http://lists.research.att.com/pipermail/ast-developers/2013q2/002456.html
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
This step happens after the initial command line splitting.
|
This step happens after the initial command line splitting.
|
||||||
|
|
||||||
@ -28,7 +28,7 @@ If **no command name** results after expansion:
|
|||||||
* This is what happens when you enter only a variable assignment at the command prompt.
|
* 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.
|
* Assignment to readonly variables causes an error and the command exits non-zero.
|
||||||
* Redirections are performed, but do not affect the current shell environment.
|
* 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!
|
* that means, a ''> FILE'' without any command **will** be performed: the ''FILE'' will be created!
|
||||||
* The command exits
|
* The command exits
|
||||||
* with an exit code indicating the redirection error, if any
|
* with an exit code indicating the redirection error, if any
|
||||||
* with the exit code of the last command-substitution parsed, if any
|
* with the exit code of the last command-substitution parsed, if any
|
||||||
@ -41,10 +41,10 @@ Otherwise, if a command name results:
|
|||||||
* **Assignment errors** in (non-interactive) POSIX mode cause //the entire script to terminate//
|
* **Assignment errors** in (non-interactive) POSIX mode cause //the entire script to terminate//
|
||||||
|
|
||||||
The behavior regarding the variable assignment errors can be tested:
|
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>
|
<div center round info 60%>http://lists.gnu.org/archive/html/bug-bash/2013-01/msg00054.html</div>
|
||||||
|
|
||||||
**__This one exits the script completely__**
|
**__This one exits the script completely__**
|
||||||
<code>
|
<code>
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# This shell runs in POSIX mode!
|
# This shell runs in POSIX mode!
|
||||||
|
|
||||||
@ -55,11 +55,11 @@ echo PRE
|
|||||||
foo=$((8#9))
|
foo=$((8#9))
|
||||||
|
|
||||||
echo POST
|
echo POST
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
|
|
||||||
**__This one terminates only the enclosing compound command (the ''{ ...; }''):__**
|
**__This one terminates only the enclosing compound command (the ''{ ...; }''):__**
|
||||||
<code>
|
<code>
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# This shell runs in native Bash-mode!
|
# This shell runs in native Bash-mode!
|
||||||
|
|
||||||
@ -71,7 +71,7 @@ echo PRE
|
|||||||
|
|
||||||
echo POST
|
echo POST
|
||||||
|
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Simple command execution =====
|
===== Simple command execution =====
|
||||||
|
|
||||||
|
@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
===== Synopsis =====
|
===== Synopsis =====
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
coproc [NAME] command [redirections]
|
coproc [NAME] command [redirections]
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Description =====
|
===== Description =====
|
||||||
|
|
||||||
@ -20,21 +20,21 @@ The return status of a coprocess is the exit status of its command.
|
|||||||
|
|
||||||
The optional redirections are applied after the pipes have been set up. Some examples:
|
The optional redirections are applied after the pipes have been set up. Some examples:
|
||||||
|
|
||||||
<code bash>
|
<code bash>
|
||||||
# redirecting stderr in the pipe
|
# redirecting stderr in the pipe
|
||||||
$ coproc { ls thisfiledoesntexist; read; } 2>&1
|
$ coproc { ls thisfiledoesntexist; read; } 2>&1
|
||||||
[2] 23084
|
[2] 23084
|
||||||
$ IFS= read -ru ${COPROC[0]} x; printf '%s\n' "$x"
|
$ IFS= read -ru ${COPROC[0]} x; printf '%s\n' "$x"
|
||||||
ls: cannot access thisfiledoesntexist: No such file or directory
|
ls: cannot access thisfiledoesntexist: No such file or directory
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
<code bash>
|
<code bash>
|
||||||
#let the output of the coprocess go to stdout
|
#let the output of the coprocess go to stdout
|
||||||
$ { coproc mycoproc { awk '{print "foo" $0;fflush()}'; } >&3; } 3>&1
|
$ { coproc mycoproc { awk '{print "foo" $0;fflush()}'; } >&3; } 3>&1
|
||||||
[2] 23092
|
[2] 23092
|
||||||
$ echo bar >&${mycoproc[1]}
|
$ echo bar >&${mycoproc[1]}
|
||||||
$ foobar
|
$ foobar
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
@ -45,21 +45,21 @@ Here we need to save the previous file descriptor of stdout, because by the time
|
|||||||
The traditional Ksh workaround to avoid the subshell when doing ''command | while read'' is to use a coprocess. Unfortunately, Bash's behavior differs.
|
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:
|
In Ksh you would do:
|
||||||
<code bash>
|
<code bash>
|
||||||
# ksh93 or mksh/pdksh derivatives
|
# ksh93 or mksh/pdksh derivatives
|
||||||
ls |& # start a coprocess
|
ls |& # start a coprocess
|
||||||
while IFS= read -rp file; do print -r -- "$file"; done # read its output
|
while IFS= read -rp file; do print -r -- "$file"; done # read its output
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
In bash:
|
In bash:
|
||||||
<code bash>
|
<code bash>
|
||||||
#DOESN'T WORK
|
#DOESN'T WORK
|
||||||
$ coproc ls
|
$ coproc ls
|
||||||
[1] 23232
|
[1] 23232
|
||||||
$ while IFS= read -ru ${COPROC[0]} line; do printf '%s\n' "$line"; done
|
$ while IFS= read -ru ${COPROC[0]} line; do printf '%s\n' "$line"; done
|
||||||
bash: read: line: invalid file descriptor specification
|
bash: read: line: invalid file descriptor specification
|
||||||
[1]+ Done coproc COPROC ls
|
[1]+ Done coproc COPROC ls
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
By the time we start reading from the output of the coprocess, the file descriptor has been closed.
|
By the time we start reading from the output of the coprocess, the file descriptor has been closed.
|
||||||
|
|
||||||
@ -69,13 +69,13 @@ See [[http://mywiki.wooledge.org/BashFAQ/024 | this FAQ entry on Greg's wiki]] f
|
|||||||
|
|
||||||
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'':
|
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'':
|
||||||
|
|
||||||
<code bash>
|
<code bash>
|
||||||
$ coproc sed s/^/foo/
|
$ coproc sed s/^/foo/
|
||||||
[1] 22981
|
[1] 22981
|
||||||
$ echo bar >&${COPROC[1]}
|
$ echo bar >&${COPROC[1]}
|
||||||
$ read -t 3 -ru ${COPROC[0]} _; (( $? > 127 )) && echo "nothing read"
|
$ read -t 3 -ru ${COPROC[0]} _; (( $? > 127 )) && echo "nothing read"
|
||||||
nothing read
|
nothing read
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
@ -87,31 +87,31 @@ A coprocess' file descriptors are accessible only to the process from which the
|
|||||||
|
|
||||||
Here is a not-so-meaningful illustration. Suppose we want to continuously read the output of a coprocess and ''echo'' the result:
|
Here is a not-so-meaningful illustration. Suppose we want to continuously read the output of a coprocess and ''echo'' the result:
|
||||||
|
|
||||||
<code bash>
|
<code bash>
|
||||||
#NOT WORKING
|
#NOT WORKING
|
||||||
$ coproc awk '{print "foo" $0;fflush()}'
|
$ coproc awk '{print "foo" $0;fflush()}'
|
||||||
[2] 23100
|
[2] 23100
|
||||||
$ while IFS= read -ru ${COPROC[0]} x; do printf '%s\n' "$x"; done &
|
$ while IFS= read -ru ${COPROC[0]} x; do printf '%s\n' "$x"; done &
|
||||||
[3] 23104
|
[3] 23104
|
||||||
bash: line 243: read: 61: invalid file descriptor: Bad file descriptor
|
bash: line 243: read: 61: invalid file descriptor: Bad file descriptor
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
This fails because the file descriptors created by the parent are not available to the subshell created by &.
|
This fails because the file descriptors created by the parent are not available to the subshell created by &.
|
||||||
|
|
||||||
A possible workaround:
|
A possible workaround:
|
||||||
|
|
||||||
<code bash>
|
<code bash>
|
||||||
#WARNING: for illustration purpose ONLY
|
#WARNING: for illustration purpose ONLY
|
||||||
# this is not the way to make the coprocess print its output
|
# this is not the way to make the coprocess print its output
|
||||||
# to stdout, see the redirections above.
|
# to stdout, see the redirections above.
|
||||||
$ coproc awk '{print "foo" $0;fflush()}'
|
$ coproc awk '{print "foo" $0;fflush()}'
|
||||||
[2] 23109
|
[2] 23109
|
||||||
$ exec 3<&${COPROC[0]}
|
$ exec 3<&${COPROC[0]}
|
||||||
$ while IFS= read -ru 3 x; do printf '%s\n' "$x"; done &
|
$ while IFS= read -ru 3 x; do printf '%s\n' "$x"; done &
|
||||||
[3] 23110
|
[3] 23110
|
||||||
$ echo bar >&${COPROC[1]}
|
$ echo bar >&${COPROC[1]}
|
||||||
$ foobar
|
$ foobar
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Here, fd 3 is inherited.
|
Here, fd 3 is inherited.
|
||||||
|
|
||||||
@ -121,68 +121,68 @@ Here, fd 3 is inherited.
|
|||||||
|
|
||||||
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:
|
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:
|
||||||
|
|
||||||
<code bash>
|
<code bash>
|
||||||
$ coproc awk '{print "foo" $0;fflush()}'
|
$ coproc awk '{print "foo" $0;fflush()}'
|
||||||
[1] 22978
|
[1] 22978
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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:
|
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:
|
||||||
|
|
||||||
<code bash>
|
<code bash>
|
||||||
$ echo bar >&${COPROC[1]}
|
$ echo bar >&${COPROC[1]}
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
And then read its output:
|
And then read its output:
|
||||||
|
|
||||||
<code bash>
|
<code bash>
|
||||||
$ IFS= read -ru ${COPROC[0]} x; printf '%s\n' "$x"
|
$ IFS= read -ru ${COPROC[0]} x; printf '%s\n' "$x"
|
||||||
foobar
|
foobar
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
When we don't need our command anymore, we can kill it via its pid:
|
When we don't need our command anymore, we can kill it via its pid:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
$ kill $COPROC_PID
|
$ kill $COPROC_PID
|
||||||
$
|
$
|
||||||
[1]+ Terminated coproc COPROC awk '{print "foo" $0;fflush()}'
|
[1]+ Terminated coproc COPROC awk '{print "foo" $0;fflush()}'
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== Named Coprocess ====
|
==== 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.
|
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.
|
||||||
|
|
||||||
<code bash>
|
<code bash>
|
||||||
$ coproc mycoproc { awk '{print "foo" $0;fflush()}' ;}
|
$ coproc mycoproc { awk '{print "foo" $0;fflush()}' ;}
|
||||||
[1] 23058
|
[1] 23058
|
||||||
$ echo bar >&${mycoproc[1]}
|
$ echo bar >&${mycoproc[1]}
|
||||||
$ IFS= read -ru ${mycoproc[0]} x; printf '%s\n' "$x"
|
$ IFS= read -ru ${mycoproc[0]} x; printf '%s\n' "$x"
|
||||||
foobar
|
foobar
|
||||||
$ kill $mycoproc_PID
|
$ kill $mycoproc_PID
|
||||||
$
|
$
|
||||||
[1]+ Terminated coproc mycoproc { awk '{print "foo" $0;fflush()}'; }
|
[1]+ Terminated coproc mycoproc { awk '{print "foo" $0;fflush()}'; }
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== Redirecting the output of a script to a file and to the screen ====
|
==== Redirecting the output of a script to a file and to the screen ====
|
||||||
|
|
||||||
<code bash>
|
<code bash>
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# we start tee in the background
|
# we start tee in the background
|
||||||
# redirecting its output to the stdout of the script
|
# redirecting its output to the stdout of the script
|
||||||
{ coproc tee { tee logfile ;} >&3 ;} 3>&1
|
{ coproc tee { tee logfile ;} >&3 ;} 3>&1
|
||||||
# we redirect stding and stdout of the script to our coprocess
|
# we redirect stding and stdout of the script to our coprocess
|
||||||
exec >&${tee[1]} 2>&1
|
exec >&${tee[1]} 2>&1
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Portability considerations =====
|
===== Portability considerations =====
|
||||||
|
|
||||||
* The ''coproc'' keyword is not specified by POSIX(R)
|
* The ''coproc'' keyword is not specified by POSIX(R)
|
||||||
* The ''coproc'' keyword appeared in Bash version 4.0-alpha
|
* 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 ''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 ''[[commands:builtin:read | 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''.
|
* The ''-p'' option to Bash's ''[[commands:builtin:read | 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 ====
|
==== 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 ''|&''.
|
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.
|
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.
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
====== Patterns and pattern matching ======
|
====== Patterns and pattern matching ======
|
||||||
|
|
||||||
{{keywords>bash shell scripting glob globbing wildcards filename pattern matching}}
|
{{keywords>bash shell scripting glob globbing wildcards filename pattern matching}}
|
||||||
|
|
||||||
A pattern is a **string description**. Bash uses them in various ways:
|
A pattern is a **string description**. Bash uses them in various ways:
|
||||||
* [[syntax:expansion:globs | Pathname expansion]] (Globbing - matching filenames)
|
* [[syntax:expansion:globs | Pathname expansion]] (Globbing - matching filenames)
|
||||||
@ -30,12 +30,12 @@ The bracket expression ''[...]'' mentioned above has some useful applications:
|
|||||||
^Bracket expression^Description^
|
^Bracket expression^Description^
|
||||||
|''[XYZ]''|The "normal" bracket expression, matching either ''X'', ''Y'' or ''Z''|
|
|''[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**!)|
|
|''[X-Z]''|A range expression: Matching all the characters from ''X'' to ''Y'' (your current **locale**, defines how the characters are **sorted**!)|
|
||||||
|''<nowiki>[[:class:]]</nowiki>''|Matches all the characters defined by a [[https://pubs.opengroup.org/onlinepubs/009696899/basedefs/xbd_chap07.html#tag_07_03_01|POSIX(r) character class]]: ''alnum'', ''alpha'', ''ascii'', ''blank'', ''cntrl'', ''digit'', ''graph'', ''lower'', ''print'', ''punct'', ''space'', ''upper'', ''word'' and ''xdigit''|
|
|''<nowiki>[[:class:]]</nowiki>''|Matches all the characters defined by a [[https://pubs.opengroup.org/onlinepubs/009696899/basedefs/xbd_chap07.html#tag_07_03_01|POSIX(r) character class]]: ''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|
|
|''[^...]''|A negating expression: It matches all the characters that are **not** in the bracket expression|
|
||||||
|''[!...]''|Equivalent to ''[^...]''|
|
|''[!...]''|Equivalent to ''[^...]''|
|
||||||
|''<nowiki>[]...]</nowiki>'' or ''[-...]''|Used to include the characters '']'' and ''-'' into the set, they need to be the first characters after the opening bracket|
|
|''<nowiki>[]...]</nowiki>'' 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!)|
|
|''[=C=]''|Matches any character that is eqivalent to the collation weight of ''C'' (current locale!)|
|
||||||
|''<nowiki>[[.SYMBOL.]]</nowiki>''|Matches the collating symbol ''SYMBOL''|
|
|''<nowiki>[[.SYMBOL.]]</nowiki>''|Matches the collating symbol ''SYMBOL''|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -47,37 +47,37 @@ Some simple examples using normal pattern matching:
|
|||||||
* Pattern ''"Hello world"'' matches
|
* Pattern ''"Hello world"'' matches
|
||||||
* ''Hello world''
|
* ''Hello world''
|
||||||
* Pattern ''[Hh]"ello world"'' matches
|
* Pattern ''[Hh]"ello world"'' matches
|
||||||
* => ''Hello world''
|
* => ''Hello world''
|
||||||
* => ''hello world''
|
* => ''hello world''
|
||||||
* Pattern ''Hello*'' matches (for example)
|
* Pattern ''Hello*'' matches (for example)
|
||||||
* => ''Hello world''
|
* => ''Hello world''
|
||||||
* => ''Helloworld''
|
* => ''Helloworld''
|
||||||
* => ''HelloWoRlD''
|
* => ''HelloWoRlD''
|
||||||
* => ''Hello''
|
* => ''Hello''
|
||||||
* Pattern ''Hello world<nowiki>[[:punct:]]</nowiki>'' matches (for example)
|
* Pattern ''Hello world<nowiki>[[:punct:]]</nowiki>'' matches (for example)
|
||||||
* => ''Hello world!''
|
* => ''Hello world!''
|
||||||
* => ''Hello world.''
|
* => ''Hello world.''
|
||||||
* => ''Hello world+''
|
* => ''Hello world+''
|
||||||
* => ''Hello world?''
|
* => ''Hello world?''
|
||||||
* Pattern ''<nowiki>[[.backslash.]]Hello[[.vertical-line.]]world[[.exclamation-mark.]]</nowiki>'' matches (using [[https://pubs.opengroup.org/onlinepubs/009696899/basedefs/xbd_chap07.html#tag_07_03_02_04|collation symbols]])
|
* Pattern ''<nowiki>[[.backslash.]]Hello[[.vertical-line.]]world[[.exclamation-mark.]]</nowiki>'' matches (using [[https://pubs.opengroup.org/onlinepubs/009696899/basedefs/xbd_chap07.html#tag_07_03_02_04|collation symbols]])
|
||||||
* => ''\Hello|world!''
|
* => ''\Hello|world!''
|
||||||
|
|
||||||
===== Extended pattern language =====
|
===== Extended pattern language =====
|
||||||
|
|
||||||
If you set the [[internals:shell_options | shell option]] ''extglob'', Bash understands some powerful patterns. A ''<PATTERN-LIST>'' is one or more patterns, separated by the pipe-symbol (''PATTERN|PATTERN'').
|
If you set the [[internals:shell_options | shell option]] ''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 one** occurrence of the given patterns|
|
||||||
|''*(<PATTERN-LIST>)''|Matches **zero or more** occurrences 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 or more** occurrences of the given patterns|
|
||||||
|''@(<PATTERN-LIST>)''|Matches **one** of the given patterns|
|
|''@(<PATTERN-LIST>)''|Matches **one** of the given patterns|
|
||||||
|''!(<PATTERN-LIST>)''|Matches anything **except** one of the given patterns|
|
|''!(<PATTERN-LIST>)''|Matches anything **except** one of the given patterns|
|
||||||
|
|
||||||
==== Examples ====
|
==== Examples ====
|
||||||
|
|
||||||
**__Delete all but one specific file__**
|
**__Delete all but one specific file__**
|
||||||
<code>
|
<code>
|
||||||
rm -f !(survivior.txt)
|
rm -f !(survivior.txt)
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Pattern matching configuration =====
|
===== Pattern matching configuration =====
|
||||||
|
|
||||||
@ -110,7 +110,7 @@ ksh93 supports some very powerful pattern matching features in addition to those
|
|||||||
|
|
||||||
* 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.
|
* 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, <code>[[ fo0bar == @(fo[0-9]&+([[:alnum:]]))bar ]]</code> would be true while <code>[[ f00bar == @(fo[0-9]&+([[:alnum:]]))bar ]]</code> 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: <code>[[ fo0bar == !(!(fo[0-9])|!(+([[:alnum:]])))bar ]]</code>, which is technically more portable, but ugly.
|
* In ksh93, a ''pattern-list'' may be delimited by either ''&'' or ''|''. ''&'' means "all patterns must be matched" instead of "any pattern". For example, <code>[[ fo0bar == @(fo[0-9]&+([[:alnum:]]))bar ]]</code> would be true while <code>[[ f00bar == @(fo[0-9]&+([[:alnum:]]))bar ]]</code> 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: <code>[[ fo0bar == !(!(fo[0-9])|!(+([[:alnum:]])))bar ]]</code>, which is technically more portable, but ugly.
|
||||||
|
|
||||||
* ksh93's [[commands/builtin/printf|printf]] builtin can translate from shell patterns to ERE and back again using the ''%R'' and ''%P'' format specifiers respectively.
|
* ksh93's [[commands/builtin/printf|printf]] builtin can translate from shell patterns to ERE and back again using the ''%R'' and ''%P'' format specifiers respectively.
|
||||||
|
|
||||||
|
352
syntax/pe.md
352
syntax/pe.md
@ -1,6 +1,6 @@
|
|||||||
====== Parameter expansion ======
|
====== Parameter expansion ======
|
||||||
|
|
||||||
{{keywords>bash shell scripting expansion substitution text variable parameter mangle substitute change check defined null array arrays}}
|
{{keywords>bash shell scripting expansion substitution text variable parameter mangle substitute change check defined null array arrays}}
|
||||||
|
|
||||||
|
|
||||||
===== Introduction =====
|
===== Introduction =====
|
||||||
@ -39,12 +39,12 @@ Looking for a specific syntax you saw, without knowing the name?
|
|||||||
* ''${PARAMETER#PATTERN}''
|
* ''${PARAMETER#PATTERN}''
|
||||||
* ''${PARAMETER##PATTERN}''
|
* ''${PARAMETER##PATTERN}''
|
||||||
* ''${PARAMETER%PATTERN}''
|
* ''${PARAMETER%PATTERN}''
|
||||||
* ''<nowiki>${PARAMETER%%PATTERN}</nowiki>''
|
* ''<nowiki>${PARAMETER%%PATTERN}</nowiki>''
|
||||||
* [[#search_and_replace | Search and replace]]
|
* [[#search_and_replace | Search and replace]]
|
||||||
* ''${PARAMETER/PATTERN/STRING}''
|
* ''${PARAMETER/PATTERN/STRING}''
|
||||||
* ''<nowiki>${PARAMETER//PATTERN/STRING}</nowiki>''
|
* ''<nowiki>${PARAMETER//PATTERN/STRING}</nowiki>''
|
||||||
* ''${PARAMETER/PATTERN}''
|
* ''${PARAMETER/PATTERN}''
|
||||||
* ''<nowiki>${PARAMETER//PATTERN}</nowiki>''
|
* ''<nowiki>${PARAMETER//PATTERN}</nowiki>''
|
||||||
* [[#string_length | String length ]]
|
* [[#string_length | String length ]]
|
||||||
* ''${#PARAMETER}''
|
* ''${#PARAMETER}''
|
||||||
* [[#substring_expansion | Substring expansion]]
|
* [[#substring_expansion | Substring expansion]]
|
||||||
@ -70,20 +70,20 @@ Looking for a specific syntax you saw, without knowing the name?
|
|||||||
|
|
||||||
The easiest form is to just use a parameter's name within braces. This is identical to using ''$FOO'' like you see it everywhere, but has the advantage that it can be immediately followed by characters that would be interpreted as part of the parameter name otherwise. Compare these two expressions (''WORD="car"'' for example), where we want to print a word with a trailing "s":
|
The easiest form is to just use a parameter's name within braces. This is identical to using ''$FOO'' like you see it everywhere, but has the advantage that it can be immediately followed by characters that would be interpreted as part of the parameter name otherwise. Compare these two expressions (''WORD="car"'' for example), where we want to print a word with a trailing "s":
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
echo "The plural of $WORD is most likely $WORDs"
|
echo "The plural of $WORD is most likely $WORDs"
|
||||||
echo "The plural of $WORD is most likely ${WORD}s"
|
echo "The plural of $WORD is most likely ${WORD}s"
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
__Why does the first one fail?__ It prints nothing, because a parameter (variable) named "''WORDs''" is undefined and thus printed as "" (//nothing//). Without using braces for parameter expansion, Bash will interpret the sequence of all valid characters from the introducing "''$''" up to the last valid character as name of the parameter. When using braces you just force Bash to **only interpret the name inside your braces**.
|
__Why does the first one fail?__ It prints nothing, because a parameter (variable) named "''WORDs''" is undefined and thus printed as "" (//nothing//). Without using braces for parameter expansion, Bash will interpret the sequence of all valid characters from the introducing "''$''" up to the last valid character as name of the parameter. When using braces you just force Bash to **only interpret the name inside your braces**.
|
||||||
|
|
||||||
Also, please remember, that **parameter names are** (like nearly everything in UNIX(r)) **case sensitive!**
|
Also, please remember, that **parameter names are** (like nearly everything in UNIX(r)) **case sensitive!**
|
||||||
|
|
||||||
The second form with the curly braces is also needed to access positional parameters (arguments to a script) beyond ''$9'':
|
The second form with the curly braces is also needed to access positional parameters (arguments to a script) beyond ''$9'':
|
||||||
<code>
|
<code>
|
||||||
echo "Argument 1 is: $1"
|
echo "Argument 1 is: $1"
|
||||||
echo "Argument 10 is: ${10}"
|
echo "Argument 10 is: ${10}"
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== Simple usage: Arrays ====
|
==== Simple usage: Arrays ====
|
||||||
|
|
||||||
@ -101,40 +101,40 @@ For arrays you always need the braces. The arrays are expanded by individual ind
|
|||||||
|
|
||||||
In some cases, like for example
|
In some cases, like for example
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
${PARAMETER}
|
${PARAMETER}
|
||||||
|
|
||||||
${PARAMETER:0:3}
|
${PARAMETER:0:3}
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
you can instead use the form
|
you can instead use the form
|
||||||
|
|
||||||
<code>${!PARAMETER}</code>
|
<code>${!PARAMETER}</code>
|
||||||
|
|
||||||
to enter a level of indirection. The referenced parameter is not ''PARAMETER'' itself, but the parameter whose name is stored as the value of ''PARAMETER''. If the parameter ''PARAMETER'' has the value "''TEMP''", then ''${!PARAMETER}'' will expand to the value of the parameter named ''TEMP'':
|
to enter a level of indirection. The referenced parameter is not ''PARAMETER'' itself, but the parameter whose name is stored as the value of ''PARAMETER''. If the parameter ''PARAMETER'' has the value "''TEMP''", then ''${!PARAMETER}'' will expand to the value of the parameter named ''TEMP'':
|
||||||
<code>
|
<code>
|
||||||
read -rep 'Which variable do you want to inspect? ' look_var
|
read -rep 'Which variable do you want to inspect? ' look_var
|
||||||
|
|
||||||
printf 'The value of "%s" is: "%s"\n' "$look_var" "${!look_var}"
|
printf 'The value of "%s" is: "%s"\n' "$look_var" "${!look_var}"
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Of course the indirection also works with special variables:
|
Of course the indirection also works with special variables:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
# set some fake positional parameters
|
# set some fake positional parameters
|
||||||
set one two three four
|
set one two three four
|
||||||
|
|
||||||
# get the LAST argument ("#" stores the number of arguments, so "!#" will reference the LAST argument)
|
# get the LAST argument ("#" stores the number of arguments, so "!#" will reference the LAST argument)
|
||||||
echo ${!#}
|
echo ${!#}
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
You can think of this mechanism as being roughly equivalent to taking any parameter expansion that begins with the parameter name, and substituting the ''!PARAMETER'' part with the value of PARAMETER.
|
You can think of this mechanism as being roughly equivalent to taking any parameter expansion that begins with the parameter name, and substituting the ''!PARAMETER'' part with the value of PARAMETER.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
echo "${!var^^}"
|
echo "${!var^^}"
|
||||||
# ...is equivalent to
|
# ...is equivalent to
|
||||||
eval 'echo "${'"$var"'^^}"'
|
eval 'echo "${'"$var"'^^}"'
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
It was an unfortunate design decision to use the ''!'' prefix for indirection, as it introduces parsing ambiguity with other parameter expansions that begin with ''!''. Indirection is not possible in combination with any parameter expansion whose modifier requires a prefix to the parameter name. Specifically, indirection isn't possible on the ''${!var@}'', ''${!var*}'', ''${!var[@]}'', ''${!var[*]}'', and ''${#var}'' forms. This means the ''!'' prefix can't be used to retrieve the indices of an array, the length of a string, or number of elements in an array indirectly (see [[syntax/arrays#indirection]] for workarounds). Additionally, the ''!''-prefixed parameter expansion conflicts with ksh-like shells which have the more powerful "name-reference" form of indirection, where the exact same syntax is used to expand to the name of the variable being referenced.
|
It was an unfortunate design decision to use the ''!'' prefix for indirection, as it introduces parsing ambiguity with other parameter expansions that begin with ''!''. Indirection is not possible in combination with any parameter expansion whose modifier requires a prefix to the parameter name. Specifically, indirection isn't possible on the ''${!var@}'', ''${!var*}'', ''${!var[@]}'', ''${!var[*]}'', and ''${#var}'' forms. This means the ''!'' prefix can't be used to retrieve the indices of an array, the length of a string, or number of elements in an array indirectly (see [[syntax/arrays#indirection]] for workarounds). Additionally, the ''!''-prefixed parameter expansion conflicts with ksh-like shells which have the more powerful "name-reference" form of indirection, where the exact same syntax is used to expand to the name of the variable being referenced.
|
||||||
|
|
||||||
@ -160,19 +160,19 @@ These expansion operators modify the case of the letters in the expanded text.
|
|||||||
|
|
||||||
The ''^'' operator modifies the first character to uppercase, the '','' operator to lowercase. When using the double-form (''^^'' and '',,''), all characters are converted.
|
The ''^'' operator modifies the first character to uppercase, the '','' operator to lowercase. When using the double-form (''^^'' and '',,''), all characters are converted.
|
||||||
|
|
||||||
<wrap center round info 60%>
|
<wrap center round info 60%>
|
||||||
|
|
||||||
The (**currently undocumented**) operators ''~'' and ''~~'' reverse the case of the given text (in ''PARAMETER'').''~'' reverses the case of first letter of words in the variable while ''~~'' reverses case for all. Thanks to ''Bushmills'' and ''geirha'' on the Freenode IRC channel for this finding.
|
The (**currently undocumented**) operators ''~'' and ''~~'' reverse the case of the given text (in ''PARAMETER'').''~'' reverses the case of first letter of words in the variable while ''~~'' reverses case for all. Thanks to ''Bushmills'' and ''geirha'' on the Freenode IRC channel for this finding.
|
||||||
|
|
||||||
</wrap>
|
</wrap>
|
||||||
|
|
||||||
|
|
||||||
__**Example: Rename all ''*.txt'' filenames to lowercase**__
|
__**Example: Rename all ''*.txt'' filenames to lowercase**__
|
||||||
<code>
|
<code>
|
||||||
for file in *.txt; do
|
for file in *.txt; do
|
||||||
mv "$file" "${file,,}"
|
mv "$file" "${file,,}"
|
||||||
done
|
done
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
__**Note:**__ Case modification is a handy feature you can apply to a name or a title. Or is it? Case modification was an important aspect of the Bash 4 release. Bash version 4, RC1 would perform word splitting, and then case modification, resulting in title case (where every word is capitalized). It was decided to apply case modification to values, not words, for the Bash 4 release. Thanks Chet.
|
__**Note:**__ Case modification is a handy feature you can apply to a name or a title. Or is it? Case modification was an important aspect of the Bash 4 release. Bash version 4, RC1 would perform word splitting, and then case modification, resulting in title case (where every word is capitalized). It was decided to apply case modification to values, not words, for the Bash 4 release. Thanks Chet.
|
||||||
|
|
||||||
@ -188,15 +188,15 @@ For [[syntax:arrays|array]] expansion, the case modification applies to **every
|
|||||||
Assume: ''array=(This is some Text)''
|
Assume: ''array=(This is some Text)''
|
||||||
|
|
||||||
* ''echo "${array[@],}"''
|
* ''echo "${array[@],}"''
|
||||||
* => ''this is some text''
|
* => ''this is some text''
|
||||||
* ''echo "${array[@],,}"''
|
* ''echo "${array[@],,}"''
|
||||||
* => ''this is some text''
|
* => ''this is some text''
|
||||||
* ''echo "${array[@]^}"''
|
* ''echo "${array[@]^}"''
|
||||||
* => ''This Is Some Text''
|
* => ''This Is Some Text''
|
||||||
* ''echo "${array[@]^^}"''
|
* ''echo "${array[@]^^}"''
|
||||||
* => ''THIS IS SOME TEXT''
|
* => ''THIS IS SOME TEXT''
|
||||||
* ''echo "${array[2]^^}"''
|
* ''echo "${array[2]^^}"''
|
||||||
* => ''SOME''
|
* => ''SOME''
|
||||||
|
|
||||||
===== Variable name expansion =====
|
===== Variable name expansion =====
|
||||||
|
|
||||||
@ -204,13 +204,13 @@ Assume: ''array=(This is some Text)''
|
|||||||
|
|
||||||
''${!PREFIX@}''
|
''${!PREFIX@}''
|
||||||
|
|
||||||
This expands to a list of all set **variable names** beginning with the string ''PREFIX''. The elements of the list are separated by the first character in the ''IFS''-variable (<space> by default).
|
This expands to a list of all set **variable names** beginning with the string ''PREFIX''. The elements of the list are separated by the first character in the ''IFS''-variable (<space> by default).
|
||||||
|
|
||||||
This will show all defined variable names (not values!) beginning with "BASH":
|
This will show all defined variable names (not values!) beginning with "BASH":
|
||||||
<code>
|
<code>
|
||||||
$ echo ${!BASH*}
|
$ echo ${!BASH*}
|
||||||
BASH BASH_ARGC BASH_ARGV BASH_COMMAND BASH_LINENO BASH_SOURCE BASH_SUBSHELL BASH_VERSINFO BASH_VERSION
|
BASH BASH_ARGC BASH_ARGV BASH_COMMAND BASH_LINENO BASH_SOURCE BASH_SUBSHELL BASH_VERSINFO BASH_VERSION
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
This list will also include [[syntax:arrays|array names]].
|
This list will also include [[syntax:arrays|array names]].
|
||||||
|
|
||||||
@ -221,32 +221,32 @@ This list will also include [[syntax:arrays|array names]].
|
|||||||
|
|
||||||
''${PARAMETER%PATTERN}''
|
''${PARAMETER%PATTERN}''
|
||||||
|
|
||||||
''<nowiki>${PARAMETER%%PATTERN}</nowiki>''
|
''<nowiki>${PARAMETER%%PATTERN}</nowiki>''
|
||||||
|
|
||||||
This one can **expand only a part** of a parameter's value, **given a pattern to describe what to remove** from the string. The pattern is interpreted just like a pattern to describe a filename to match (globbing). See [[syntax:pattern | Pattern matching]] for more.
|
This one can **expand only a part** of a parameter's value, **given a pattern to describe what to remove** from the string. The pattern is interpreted just like a pattern to describe a filename to match (globbing). See [[syntax:pattern | Pattern matching]] for more.
|
||||||
|
|
||||||
Example string (//just a quote from a big man//):
|
Example string (//just a quote from a big man//):
|
||||||
<code>
|
<code>
|
||||||
MYSTRING="Be liberal in what you accept, and conservative in what you send"
|
MYSTRING="Be liberal in what you accept, and conservative in what you send"
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
|
|
||||||
==== From the beginning ====
|
==== From the beginning ====
|
||||||
''${PARAMETER#PATTERN}'' and ''${PARAMETER##PATTERN}''
|
''${PARAMETER#PATTERN}'' and ''${PARAMETER##PATTERN}''
|
||||||
|
|
||||||
This form is to remove the described [[syntax:pattern | pattern]] trying to **match it from the beginning of the string**.
|
This form is to remove the described [[syntax:pattern | pattern]] trying to **match it from the beginning of the string**.
|
||||||
The operator "''#''" will try to remove the shortest text matching the pattern, while "''##''" tries to do it with the longest text matching. Look at the following examples to get the idea (matched text <del>marked striked</del>, remember it will be removed!):
|
The operator "''#''" will try to remove the shortest text matching the pattern, while "''##''" tries to do it with the longest text matching. Look at the following examples to get the idea (matched text <del>marked striked</del>, remember it will be removed!):
|
||||||
^Syntax^Result^
|
^Syntax^Result^
|
||||||
|''${MYSTRING#*in}''|<del>Be liberal in</del> what you accept, and conservative in what you send|
|
|''${MYSTRING#*in}''|<del>Be liberal in</del> what you accept, and conservative in what you send|
|
||||||
|''${MYSTRING##*in}''|<del>Be liberal in what you accept, and conservative in</del> what you send|
|
|''${MYSTRING##*in}''|<del>Be liberal in what you accept, and conservative in</del> what you send|
|
||||||
|
|
||||||
==== From the end ====
|
==== From the end ====
|
||||||
''${PARAMETER%PATTERN}'' and ''<nowiki>${PARAMETER%%PATTERN}</nowiki>''
|
''${PARAMETER%PATTERN}'' and ''<nowiki>${PARAMETER%%PATTERN}</nowiki>''
|
||||||
|
|
||||||
In the second form everything will be the same, except that Bash now tries to match the pattern from the end of the string:
|
In the second form everything will be the same, except that Bash now tries to match the pattern from the end of the string:
|
||||||
^ Syntax ^ Result ^
|
^ Syntax ^ Result ^
|
||||||
| ''${MYSTRING%in*}'' | Be liberal in what you accept, and conservative <del>in what you send</del> |
|
| ''${MYSTRING%in*}'' | Be liberal in what you accept, and conservative <del>in what you send</del> |
|
||||||
| ''<nowiki>${MYSTRING%%in*}</nowiki>'' | Be liberal <del>in what you accept, and conservative in what you send</del> |
|
| ''<nowiki>${MYSTRING%%in*}</nowiki>'' | Be liberal <del>in what you accept, and conservative in what you send</del> |
|
||||||
|
|
||||||
The second form nullifies variables that begin with ''in'', by working from the end.
|
The second form nullifies variables that begin with ''in'', by working from the end.
|
||||||
==== Common use ====
|
==== Common use ====
|
||||||
@ -256,16 +256,16 @@ Well, maybe the most common use for it is to **extract parts of a filename**. Ju
|
|||||||
|
|
||||||
* **Get name without extension**
|
* **Get name without extension**
|
||||||
* ''${FILENAME%.*}''
|
* ''${FILENAME%.*}''
|
||||||
* => ''bash_hackers<del>.txt</del>''
|
* => ''bash_hackers<del>.txt</del>''
|
||||||
* **Get extension**
|
* **Get extension**
|
||||||
* ''${FILENAME##*.}''
|
* ''${FILENAME##*.}''
|
||||||
* => ''<del>bash_hackers.</del>txt''
|
* => ''<del>bash_hackers.</del>txt''
|
||||||
* **Get directory name**
|
* **Get directory name**
|
||||||
* ''${PATHNAME%/*}''
|
* ''${PATHNAME%/*}''
|
||||||
* => ''/home/bash<del>/bash_hackers.txt</del>''
|
* => ''/home/bash<del>/bash_hackers.txt</del>''
|
||||||
* **Get filename**
|
* **Get filename**
|
||||||
* ''${PATHNAME##*/}''
|
* ''${PATHNAME##*/}''
|
||||||
* => ''<del>/home/bash/</del>bash_hackers.txt''
|
* => ''<del>/home/bash/</del>bash_hackers.txt''
|
||||||
|
|
||||||
These are the syntaxes for filenames with a single extension. Depending on your needs, you might need to adjust shortest/longest match.
|
These are the syntaxes for filenames with a single extension. Depending on your needs, you might need to adjust shortest/longest match.
|
||||||
|
|
||||||
@ -278,8 +278,8 @@ Simple example, removing a trailing ''is'' from all array elements (on expansion
|
|||||||
Assume: ''array=(This is a text)''
|
Assume: ''array=(This is a text)''
|
||||||
|
|
||||||
* ''echo "${array[@]%is}"''
|
* ''echo "${array[@]%is}"''
|
||||||
* => ''Th a text''
|
* => ''Th a text''
|
||||||
* (it was: ''Th<del>is</del> <del>is</del> a text'')
|
* (it was: ''Th<del>is</del> <del>is</del> a text'')
|
||||||
|
|
||||||
All other variants of this expansion behave the same.
|
All other variants of this expansion behave the same.
|
||||||
|
|
||||||
@ -288,53 +288,53 @@ All other variants of this expansion behave the same.
|
|||||||
===== Search and replace =====
|
===== Search and replace =====
|
||||||
''${PARAMETER/PATTERN/STRING}''
|
''${PARAMETER/PATTERN/STRING}''
|
||||||
|
|
||||||
''<nowiki>${PARAMETER//PATTERN/STRING}</nowiki>''
|
''<nowiki>${PARAMETER//PATTERN/STRING}</nowiki>''
|
||||||
|
|
||||||
''<nowiki>${PARAMETER/PATTERN}</nowiki>''
|
''<nowiki>${PARAMETER/PATTERN}</nowiki>''
|
||||||
|
|
||||||
''<nowiki>${PARAMETER//PATTERN}</nowiki>''
|
''<nowiki>${PARAMETER//PATTERN}</nowiki>''
|
||||||
|
|
||||||
This one can substitute (//replace//) a substring [[syntax:pattern | matched by a pattern]], on expansion time. The matched substring will be entirely removed and the given string will be inserted. Again some example string for the tests:
|
This one can substitute (//replace//) a substring [[syntax:pattern | matched by a pattern]], on expansion time. The matched substring will be entirely removed and the given string will be inserted. Again some example string for the tests:
|
||||||
<code>
|
<code>
|
||||||
MYSTRING="Be liberal in what you accept, and conservative in what you send"
|
MYSTRING="Be liberal in what you accept, and conservative in what you send"
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
The two main forms only differ in **the number of slashes** after the parameter name: ''${PARAMETER/PATTERN/STRING}'' and ''<nowiki>${PARAMETER//PATTERN/STRING}</nowiki>''
|
The two main forms only differ in **the number of slashes** after the parameter name: ''${PARAMETER/PATTERN/STRING}'' and ''<nowiki>${PARAMETER//PATTERN/STRING}</nowiki>''
|
||||||
|
|
||||||
The first one (//one slash//) is to only substitute **the first occurrence** of the given pattern, the second one (//two slashes//) is to substitute **all occurrences** of the pattern.
|
The first one (//one slash//) is to only substitute **the first occurrence** of the given pattern, the second one (//two slashes//) is to substitute **all occurrences** of the pattern.
|
||||||
|
|
||||||
First, let's try to say "happy" instead of "conservative" in our example string:
|
First, let's try to say "happy" instead of "conservative" in our example string:
|
||||||
<code>
|
<code>
|
||||||
${MYSTRING//conservative/happy}
|
${MYSTRING//conservative/happy}
|
||||||
</code>
|
</code>
|
||||||
=> ''Be liberal in what you accept, and <del>conservative</del>happy in what you send''
|
=> ''Be liberal in what you accept, and <del>conservative</del>happy in what you send''
|
||||||
|
|
||||||
Since there is only one "conservative" in that example, it really doesn't matter which of the two forms we use.
|
Since there is only one "conservative" in that example, it really doesn't matter which of the two forms we use.
|
||||||
|
|
||||||
Let's play with the word "in", I don't know if it makes any sense, but let's substitute it with "by".
|
Let's play with the word "in", I don't know if it makes any sense, but let's substitute it with "by".
|
||||||
|
|
||||||
__**First form: Substitute first occurrence**__
|
__**First form: Substitute first occurrence**__
|
||||||
<code>${MYSTRING/in/by}</code>
|
<code>${MYSTRING/in/by}</code>
|
||||||
=> ''Be liberal <del>in</del>by what you accept, and conservative in what you send''
|
=> ''Be liberal <del>in</del>by what you accept, and conservative in what you send''
|
||||||
|
|
||||||
__**Second form: Substitute all occurrences**__
|
__**Second form: Substitute all occurrences**__
|
||||||
<code>${MYSTRING//in/by}</code>
|
<code>${MYSTRING//in/by}</code>
|
||||||
=> ''Be liberal <del>in</del>by what you accept, and conservative <del>in</del>by what you send''
|
=> ''Be liberal <del>in</del>by what you accept, and conservative <del>in</del>by what you send''
|
||||||
|
|
||||||
__**Anchoring**__
|
__**Anchoring**__
|
||||||
Additionally you can "anchor" an expression:
|
Additionally you can "anchor" an expression:
|
||||||
A ''#'' (hashmark) will indicate that your expression is matched against the beginning portion of the string, a ''%'' (percent-sign) will do it for the end portion.
|
A ''#'' (hashmark) will indicate that your expression is matched against the beginning portion of the string, a ''%'' (percent-sign) will do it for the end portion.
|
||||||
|
|
||||||
<code>MYSTRING=xxxxxxxxxx
|
<code>MYSTRING=xxxxxxxxxx
|
||||||
echo ${MYSTRING/#x/y} # RESULT: yxxxxxxxxx
|
echo ${MYSTRING/#x/y} # RESULT: yxxxxxxxxx
|
||||||
echo ${MYSTRING/%x/y} # RESULT: xxxxxxxxxy</code>
|
echo ${MYSTRING/%x/y} # RESULT: xxxxxxxxxy</code>
|
||||||
|
|
||||||
If the replacement part is completely omitted, the matches are replaced by the nullstring, i.e., they are removed. This is equivalent to specifying an empty replacement:
|
If the replacement part is completely omitted, the matches are replaced by the nullstring, i.e., they are removed. This is equivalent to specifying an empty replacement:
|
||||||
<code>
|
<code>
|
||||||
echo ${MYSTRING//conservative/}
|
echo ${MYSTRING//conservative/}
|
||||||
# is equivalent to
|
# is equivalent to
|
||||||
echo ${MYSTRING//conservative}
|
echo ${MYSTRING//conservative}
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
|
|
||||||
==== Search and replace: Arrays ====
|
==== Search and replace: Arrays ====
|
||||||
@ -346,20 +346,20 @@ A simple example, changing the (lowercase) letter ''t'' to ''d'':
|
|||||||
Assume: ''array=(This is a text)''
|
Assume: ''array=(This is a text)''
|
||||||
|
|
||||||
* ''echo "${array[@]/t/d}"''
|
* ''echo "${array[@]/t/d}"''
|
||||||
* => ''This is a dext''
|
* => ''This is a dext''
|
||||||
* ''echo "${array[@]<nowiki>//</nowiki>t/d}"''
|
* ''echo "${array[@]<nowiki>//</nowiki>t/d}"''
|
||||||
* => ''This is a dexd''
|
* => ''This is a dexd''
|
||||||
|
|
||||||
===== String length =====
|
===== String length =====
|
||||||
''${#PARAMETER}''
|
''${#PARAMETER}''
|
||||||
|
|
||||||
When you use this form, the length of the parameter's value is expanded. Again, a quote from a big man, to have a test text:
|
When you use this form, the length of the parameter's value is expanded. Again, a quote from a big man, to have a test text:
|
||||||
|
|
||||||
<code>MYSTRING="Be liberal in what you accept, and conservative in what you send"</code>
|
<code>MYSTRING="Be liberal in what you accept, and conservative in what you send"</code>
|
||||||
|
|
||||||
Using echo ''${#MYSTRING}''...
|
Using echo ''${#MYSTRING}''...
|
||||||
|
|
||||||
=> ''64''
|
=> ''64''
|
||||||
|
|
||||||
The length is reported in characters, not in bytes. Depending on your environment this may not always be the same (multibyte-characters, like in UTF8 encoding).
|
The length is reported in characters, not in bytes. Depending on your environment this may not always be the same (multibyte-characters, like in UTF8 encoding).
|
||||||
|
|
||||||
@ -376,9 +376,9 @@ Example:
|
|||||||
Assume: ''array=(This is a text)''
|
Assume: ''array=(This is a text)''
|
||||||
|
|
||||||
* ''echo ${#array[1]}''
|
* ''echo ${#array[1]}''
|
||||||
* => 2 (the word "is" has a length of 2)
|
* => 2 (the word "is" has a length of 2)
|
||||||
* ''echo ${#array[@]}''
|
* ''echo ${#array[@]}''
|
||||||
* => 4 (the array contains 4 elements)
|
* => 4 (the array contains 4 elements)
|
||||||
|
|
||||||
__**Attention:**__ The number of used elements does not need to conform to the highest index. Sparse arrays are possible in Bash, that means you can have 4 elements, but with indexes 1, 7, 20, 31. **You can't loop through such an array with a counter loop based on the number of elements!**
|
__**Attention:**__ The number of used elements does not need to conform to the highest index. Sparse arrays are possible in Bash, that means you can have 4 elements, but with indexes 1, 7, 20, 31. **You can't loop through such an array with a counter loop based on the number of elements!**
|
||||||
|
|
||||||
@ -396,28 +396,28 @@ Example string (a quote from a big man):
|
|||||||
|
|
||||||
==== Using only Offset ====
|
==== Using only Offset ====
|
||||||
In the first form, the expansion is used without a length value, note that the offset 0 is the first character:
|
In the first form, the expansion is used without a length value, note that the offset 0 is the first character:
|
||||||
<code>echo ${MYSTRING:35}</code>
|
<code>echo ${MYSTRING:35}</code>
|
||||||
=> ''<del>Be liberal in what you accept, and </del>conservative in what you send''
|
=> ''<del>Be liberal in what you accept, and </del>conservative in what you send''
|
||||||
|
|
||||||
==== Using Offset and Length ====
|
==== Using Offset and Length ====
|
||||||
In the second form we also give a length value:
|
In the second form we also give a length value:
|
||||||
<code>echo ${MYSTRING:35:12}</code>
|
<code>echo ${MYSTRING:35:12}</code>
|
||||||
=> ''<del>Be liberal in what you accept, and </del>conservative<del> in what you send</del>''
|
=> ''<del>Be liberal in what you accept, and </del>conservative<del> in what you send</del>''
|
||||||
|
|
||||||
==== Negative Offset Value ====
|
==== Negative Offset Value ====
|
||||||
If the given offset is negative, it's counted from the end of the string, i.e. an offset of -1 is the last character. In that case, the length still counts forward, of course. One special thing is to do when using a negative offset: You need to separate the (negative) number from the colon:
|
If the given offset is negative, it's counted from the end of the string, i.e. an offset of -1 is the last character. In that case, the length still counts forward, of course. One special thing is to do when using a negative offset: You need to separate the (negative) number from the colon:
|
||||||
<code>
|
<code>
|
||||||
${MYSTRING: -10:5}
|
${MYSTRING: -10:5}
|
||||||
${MYSTRING:(-10):5}
|
${MYSTRING:(-10):5}
|
||||||
</code>
|
</code>
|
||||||
Why? Because it's interpreted as the parameter expansion syntax to [[syntax:pe#use_a_default_value | use a default value]].
|
Why? Because it's interpreted as the parameter expansion syntax to [[syntax:pe#use_a_default_value | use a default value]].
|
||||||
|
|
||||||
==== Negative Length Value ====
|
==== Negative Length Value ====
|
||||||
If the ''LENGTH'' value is negative, it's used as offset from the end of the string. The expansion happens from the first to the second offset then:
|
If the ''LENGTH'' value is negative, it's used as offset from the end of the string. The expansion happens from the first to the second offset then:
|
||||||
<code>
|
<code>
|
||||||
echo "${MYSTRING:11:-17}"
|
echo "${MYSTRING:11:-17}"
|
||||||
</code>
|
</code>
|
||||||
=> ''<del>Be liberal </del>in what you accept, and conservative<del> in what you send</del>''
|
=> ''<del>Be liberal </del>in what you accept, and conservative<del> in what you send</del>''
|
||||||
|
|
||||||
This works since Bash 4.2-alpha, see also [[scripting:bashchanges]].
|
This works since Bash 4.2-alpha, see also [[scripting:bashchanges]].
|
||||||
|
|
||||||
@ -434,9 +434,9 @@ Example:
|
|||||||
Assume: ''array=(This is a text)''
|
Assume: ''array=(This is a text)''
|
||||||
|
|
||||||
* ''echo ${array[0]:2:2}''
|
* ''echo ${array[0]:2:2}''
|
||||||
* => ''is'' (the "is" in "This", array element 0)
|
* => ''is'' (the "is" in "This", array element 0)
|
||||||
* ''echo ${array[@]:1:2}''
|
* ''echo ${array[@]:1:2}''
|
||||||
* => ''is a'' (from element 1 inclusive, 2 elements are expanded, i.e. element 1 and 2)
|
* => ''is a'' (from element 1 inclusive, 2 elements are expanded, i.e. element 1 and 2)
|
||||||
|
|
||||||
===== Use a default value =====
|
===== Use a default value =====
|
||||||
|
|
||||||
@ -446,19 +446,19 @@ Assume: ''array=(This is a text)''
|
|||||||
|
|
||||||
If the parameter ''PARAMETER'' is unset (never was defined) or null (empty), this one expands to ''WORD'', otherwise it expands to the value of ''PARAMETER'', as if it just was ''${PARAMETER}''. If you omit the '':'' (colon), like shown in the second form, the default value is only used when the parameter was **unset**, not when it was empty.
|
If the parameter ''PARAMETER'' is unset (never was defined) or null (empty), this one expands to ''WORD'', otherwise it expands to the value of ''PARAMETER'', as if it just was ''${PARAMETER}''. If you omit the '':'' (colon), like shown in the second form, the default value is only used when the parameter was **unset**, not when it was empty.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
echo "Your home directory is: ${HOME:-/home/$USER}."
|
echo "Your home directory is: ${HOME:-/home/$USER}."
|
||||||
echo "${HOME:-/home/$USER} will be used to store your personal data."
|
echo "${HOME:-/home/$USER} will be used to store your personal data."
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
If ''HOME'' is unset or empty, everytime you want to print something useful, you need to put that parameter syntax in.
|
If ''HOME'' is unset or empty, everytime you want to print something useful, you need to put that parameter syntax in.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
read -p "Enter your gender (just press ENTER to not tell us): " GENDER
|
read -p "Enter your gender (just press ENTER to not tell us): " GENDER
|
||||||
echo "Your gender is ${GENDER:-a secret}."
|
echo "Your gender is ${GENDER:-a secret}."
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
It will print "Your gender is a secret." when you don't enter the gender. Note that the default value is **used on expansion time**, it is **not assigned to the parameter**.
|
It will print "Your gender is a secret." when you don't enter the gender. Note that the default value is **used on expansion time**, it is **not assigned to the parameter**.
|
||||||
|
|
||||||
@ -476,7 +476,7 @@ In other words: The basic meaning of this expansion type is applied as consisten
|
|||||||
|
|
||||||
Example code (please try the example cases yourself):
|
Example code (please try the example cases yourself):
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
|
|
||||||
####
|
####
|
||||||
# Example cases for unset/empty arrays and nullstring elements
|
# Example cases for unset/empty arrays and nullstring elements
|
||||||
@ -512,7 +512,7 @@ array=("" word)
|
|||||||
|
|
||||||
echo ${array[@]:-This array is NULL or unset}
|
echo ${array[@]:-This array is NULL or unset}
|
||||||
echo ${array[@]-This array is NULL or unset}
|
echo ${array[@]-This array is NULL or unset}
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Assign a default value =====
|
===== Assign a default value =====
|
||||||
|
|
||||||
@ -522,22 +522,22 @@ echo ${array[@]-This array is NULL or unset}
|
|||||||
|
|
||||||
This one works like the [[syntax:pe#use_a_default_value | using default values]], but the default text you give is not only expanded, but also **assigned** to the parameter, if it was unset or null. Equivalent to using a default value, when you omit the '':'' (colon), as shown in the second form, the default value will only be assigned when the parameter was **unset**.
|
This one works like the [[syntax:pe#use_a_default_value | using default values]], but the default text you give is not only expanded, but also **assigned** to the parameter, if it was unset or null. Equivalent to using a default value, when you omit the '':'' (colon), as shown in the second form, the default value will only be assigned when the parameter was **unset**.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
echo "Your home directory is: ${HOME:=/home/$USER}."
|
echo "Your home directory is: ${HOME:=/home/$USER}."
|
||||||
echo "$HOME will be used to store your personal data."
|
echo "$HOME will be used to store your personal data."
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
After the first expansion here (''${HOME:=/home/$USER}''), ''HOME'' is set and usable.
|
After the first expansion here (''${HOME:=/home/$USER}''), ''HOME'' is set and usable.
|
||||||
|
|
||||||
Let's change our code example from above:
|
Let's change our code example from above:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
read -p "Enter your gender (just press ENTER to not tell us): " GENDER
|
read -p "Enter your gender (just press ENTER to not tell us): " GENDER
|
||||||
echo "Your gender is ${GENDER:=a secret}."
|
echo "Your gender is ${GENDER:=a secret}."
|
||||||
echo "Ah, in case you forgot, your gender is really: $GENDER"
|
echo "Ah, in case you forgot, your gender is really: $GENDER"
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== Assign a default value: Arrays ====
|
==== Assign a default value: Arrays ====
|
||||||
|
|
||||||
@ -550,21 +550,21 @@ For [[syntax:arrays|arrays]] this expansion type is limited. For an individual i
|
|||||||
''${PARAMETER+WORD}''
|
''${PARAMETER+WORD}''
|
||||||
|
|
||||||
This form expands to nothing if the parameter is unset or empty. If it is set, it does not expand to the parameter's value, **but to some text you can specify**:
|
This form expands to nothing if the parameter is unset or empty. If it is set, it does not expand to the parameter's value, **but to some text you can specify**:
|
||||||
<code>
|
<code>
|
||||||
echo "The Java application was installed and can be started.${JAVAPATH:+ NOTE: JAVAPATH seems to be set}"
|
echo "The Java application was installed and can be started.${JAVAPATH:+ NOTE: JAVAPATH seems to be set}"
|
||||||
</code>
|
</code>
|
||||||
The above code will simply add a warning if ''JAVAPATH'' is set (because it could influence the startup behaviour of that imaginary application).
|
The above code will simply add a warning if ''JAVAPATH'' is set (because it could influence the startup behaviour of that imaginary application).
|
||||||
|
|
||||||
Some more unrealistic example... Ask for some flags (for whatever reason), and then, if they were set, print a warning and also print the flags:
|
Some more unrealistic example... Ask for some flags (for whatever reason), and then, if they were set, print a warning and also print the flags:
|
||||||
<code>
|
<code>
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
read -p "If you want to use special flags, enter them now: " SPECIAL_FLAGS
|
read -p "If you want to use special flags, enter them now: " SPECIAL_FLAGS
|
||||||
echo "The installation of the application is finished${SPECIAL_FLAGS:+ (NOTE: there are special flags set: $SPECIAL_FLAGS)}."
|
echo "The installation of the application is finished${SPECIAL_FLAGS:+ (NOTE: there are special flags set: $SPECIAL_FLAGS)}."
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
If you omit the colon, as shown in the second form (''${PARAMETER+WORD}''), the alternate value will be used if the parameter is set (and it can be empty)! You can use it, for example, to complain if variables you need (and that can be empty) are undefined:
|
If you omit the colon, as shown in the second form (''${PARAMETER+WORD}''), the alternate value will be used if the parameter is set (and it can be empty)! You can use it, for example, to complain if variables you need (and that can be empty) are undefined:
|
||||||
<code>
|
<code>
|
||||||
# test that with the three stages:
|
# test that with the three stages:
|
||||||
|
|
||||||
# unset foo
|
# unset foo
|
||||||
@ -576,7 +576,7 @@ if [[ ${foo+isset} = isset ]]; then
|
|||||||
else
|
else
|
||||||
echo "foo is not set..."
|
echo "foo is not set..."
|
||||||
fi
|
fi
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== Use an alternate value: Arrays ====
|
==== Use an alternate value: Arrays ====
|
||||||
|
|
||||||
@ -596,10 +596,10 @@ For some cases to play with, please see the code examples in the [[#use_a_defaul
|
|||||||
''${PARAMETER?WORD}''
|
''${PARAMETER?WORD}''
|
||||||
|
|
||||||
If the parameter ''PARAMETER'' is set/non-null, this form will simply expand it. Otherwise, the expansion of ''WORD'' will be used as appendix for an error message:
|
If the parameter ''PARAMETER'' is set/non-null, this form will simply expand it. Otherwise, the expansion of ''WORD'' will be used as appendix for an error message:
|
||||||
<code>
|
<code>
|
||||||
$ echo "The unset parameter is: ${p_unset?not set}"
|
$ echo "The unset parameter is: ${p_unset?not set}"
|
||||||
bash: p_unset: not set
|
bash: p_unset: not set
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
After printing this message,
|
After printing this message,
|
||||||
* an interactive shell has ''$?'' to a non-zero value
|
* an interactive shell has ''$?'' to a non-zero value
|
||||||
@ -616,7 +616,7 @@ are taken into account.
|
|||||||
==== Substring removal ====
|
==== Substring removal ====
|
||||||
|
|
||||||
Removing the first 6 characters from a text string:
|
Removing the first 6 characters from a text string:
|
||||||
<code>
|
<code>
|
||||||
STRING="Hello world"
|
STRING="Hello world"
|
||||||
|
|
||||||
# only print 'Hello'
|
# only print 'Hello'
|
||||||
@ -627,69 +627,69 @@ echo "${STRING#??????}"
|
|||||||
|
|
||||||
# store it into the same variable
|
# store it into the same variable
|
||||||
STRING=${STRING#??????}
|
STRING=${STRING#??????}
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Bugs and Portability considerations =====
|
===== Bugs and Portability considerations =====
|
||||||
|
|
||||||
* **Fixed in 4.2.36** ([[ftp://ftp.cwru.edu/pub/bash/bash-4.2-patches/bash42-036 | patch]]). Bash doesn't follow either POSIX or its own documentation when expanding either a quoted ''"$@"'' or ''"${arr[@]}"'' with an adjacent expansion. ''"$@$x"'' expands in the same way as ''"$*$x"'' - i.e. all parameters plus the adjacent expansion are concatenated into a single argument. As a workaround, each expansion needs to be quoted separately. Unfortunately, this bug took a very long time to notice.<code>
|
* **Fixed in 4.2.36** ([[ftp://ftp.cwru.edu/pub/bash/bash-4.2-patches/bash42-036 | patch]]). Bash doesn't follow either POSIX or its own documentation when expanding either a quoted ''"$@"'' or ''"${arr[@]}"'' with an adjacent expansion. ''"$@$x"'' expands in the same way as ''"$*$x"'' - i.e. all parameters plus the adjacent expansion are concatenated into a single argument. As a workaround, each expansion needs to be quoted separately. Unfortunately, this bug took a very long time to notice.<code>
|
||||||
~ $ set -- a b c; x=foo; printf '<%s> ' "$@$x" "$*""$x" "$@""$x"
|
~ $ set -- a b c; x=foo; printf '<%s> ' "$@$x" "$*""$x" "$@""$x"
|
||||||
<a b cfoo> <a b cfoo> <a> <b> <cfoo>
|
<a b cfoo> <a b cfoo> <a> <b> <cfoo>
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
* Almost all shells disagree about the treatment of an unquoted ''$@'', ''${arr[@]}'', ''$*'', and ''${arr[*]}'' when [[http://mywiki.wooledge.org/IFS | IFS]] is set to null. POSIX is unclear about the expected behavior. A null IFS causes both [[syntax:expansion:wordsplit | word splitting]] and [[syntax:expansion:globs | pathname expansion]] to behave randomly. Since there are few good reasons to leave ''IFS'' set to null for more than the duration of a command or two, and even fewer to expand ''$@'' and ''$*'' unquoted, this should be a rare issue. **Always quote them**!<code>
|
* Almost all shells disagree about the treatment of an unquoted ''$@'', ''${arr[@]}'', ''$*'', and ''${arr[*]}'' when [[http://mywiki.wooledge.org/IFS | IFS]] is set to null. POSIX is unclear about the expected behavior. A null IFS causes both [[syntax:expansion:wordsplit | word splitting]] and [[syntax:expansion:globs | pathname expansion]] to behave randomly. Since there are few good reasons to leave ''IFS'' set to null for more than the duration of a command or two, and even fewer to expand ''$@'' and ''$*'' unquoted, this should be a rare issue. **Always quote them**!<code>
|
||||||
touch x 'y z'
|
touch x 'y z'
|
||||||
for sh in bb {{d,b}a,{m,}k,z}sh; do
|
for sh in bb {{d,b}a,{m,}k,z}sh; do
|
||||||
echo "$sh"
|
echo "$sh"
|
||||||
"$sh" -s a 'b c' d \* </dev/fd/0
|
"$sh" -s a 'b c' d \* </dev/fd/0
|
||||||
done <<\EOF
|
done <<\EOF
|
||||||
${ZSH_VERSION+:} false && emulate sh
|
${ZSH_VERSION+:} false && emulate sh
|
||||||
IFS=
|
IFS=
|
||||||
printf '<%s> ' $*
|
printf '<%s> ' $*
|
||||||
echo
|
echo
|
||||||
printf "<%s> " $@
|
printf "<%s> " $@
|
||||||
echo
|
echo
|
||||||
EOF
|
EOF
|
||||||
</code><code>
|
</code><code>
|
||||||
bb
|
bb
|
||||||
<ab cd*>
|
<ab cd*>
|
||||||
<ab cd*>
|
<ab cd*>
|
||||||
dash
|
dash
|
||||||
<ab cd*>
|
<ab cd*>
|
||||||
<ab cd*>
|
<ab cd*>
|
||||||
bash
|
bash
|
||||||
<a> <b c> <d> <x> <y z>
|
<a> <b c> <d> <x> <y z>
|
||||||
<a> <b c> <d> <x> <y z>
|
<a> <b c> <d> <x> <y z>
|
||||||
mksh
|
mksh
|
||||||
<a b c d *>
|
<a b c d *>
|
||||||
<a b c d *>
|
<a b c d *>
|
||||||
ksh
|
ksh
|
||||||
<a> <b c> <d> <x> <y z>
|
<a> <b c> <d> <x> <y z>
|
||||||
<a> <b c> <d> <x> <y z>
|
<a> <b c> <d> <x> <y z>
|
||||||
zsh
|
zsh
|
||||||
<a> <b c> <d> <x> <y z>
|
<a> <b c> <d> <x> <y z>
|
||||||
<a> <b c> <d> <x> <y z>
|
<a> <b c> <d> <x> <y z>
|
||||||
</code>When ''IFS'' is set to a non-null value, or unset, all shells behave the same - first expanding into separate args, then applying pathname expansion and word-splitting to the results, except for zsh, which doesn't do pathname expansion in its default mode.
|
</code>When ''IFS'' is set to a non-null value, or unset, all shells behave the same - first expanding into separate args, then applying pathname expansion and word-splitting to the results, except for zsh, which doesn't do pathname expansion in its default mode.
|
||||||
|
|
||||||
* Additionally, shells disagree about various wordsplitting behaviors, the behavior of inserting delimiter characters from IFS in ''$*'', and the way adjacent arguments are concatenated, when IFS is modified in the middle of expansion through side-effects.<code>
|
* Additionally, shells disagree about various wordsplitting behaviors, the behavior of inserting delimiter characters from IFS in ''$*'', and the way adjacent arguments are concatenated, when IFS is modified in the middle of expansion through side-effects.<code>
|
||||||
for sh in bb {{d,b}a,po,{m,}k,z}sh; do
|
for sh in bb {{d,b}a,po,{m,}k,z}sh; do
|
||||||
printf '%-4s: ' "$sh"
|
printf '%-4s: ' "$sh"
|
||||||
"$sh" </dev/fd/0
|
"$sh" </dev/fd/0
|
||||||
done <<\EOF
|
done <<\EOF
|
||||||
${ZSH_VERSION+:} false && emulate sh
|
${ZSH_VERSION+:} false && emulate sh
|
||||||
set -f -- a b c
|
set -f -- a b c
|
||||||
unset -v IFS
|
unset -v IFS
|
||||||
printf '<%s> ' ${*}${IFS=}${*}${IFS:=-}"${*}"
|
printf '<%s> ' ${*}${IFS=}${*}${IFS:=-}"${*}"
|
||||||
echo
|
echo
|
||||||
EOF
|
EOF
|
||||||
</code><code>
|
</code><code>
|
||||||
bb : <a b cabc> <a-b-c>
|
bb : <a b cabc> <a-b-c>
|
||||||
dash: <a b cabc> <a-b-c>
|
dash: <a b cabc> <a-b-c>
|
||||||
bash: <a> <b> <ca> <b> <c-a b c>
|
bash: <a> <b> <ca> <b> <c-a b c>
|
||||||
posh: <a> <b> <ca b c> <a-b-c>
|
posh: <a> <b> <ca b c> <a-b-c>
|
||||||
mksh: <a> <b> <ca b c> <a-b-c>
|
mksh: <a> <b> <ca b c> <a-b-c>
|
||||||
ksh : <a> <b> <ca> <b> <c> <a b c>
|
ksh : <a> <b> <ca> <b> <c> <a b c>
|
||||||
zsh : <a> <b> <ca> <b> <c> <a-b-c>
|
zsh : <a> <b> <ca> <b> <c> <a-b-c>
|
||||||
</code>ksh93 and mksh can additionally achieve this side effect (and others) via the ''${ cmds;}'' expansion. I haven't yet tested every possible side-effect that can affect expansion halfway through expansion that way.
|
</code>ksh93 and mksh can additionally achieve this side effect (and others) via the ''${ cmds;}'' expansion. I haven't yet tested every possible side-effect that can affect expansion halfway through expansion that way.
|
||||||
|
|
||||||
* As previously mentioned, the Bash form of indirection by prefixing a parameter expansion with a ''!'' conflicts with the same syntax used by mksh, zsh, and ksh93 for a different purpose. Bash will "slightly" modify this expansion in the next version with the addition of namerefs.
|
* As previously mentioned, the Bash form of indirection by prefixing a parameter expansion with a ''!'' conflicts with the same syntax used by mksh, zsh, and ksh93 for a different purpose. Bash will "slightly" modify this expansion in the next version with the addition of namerefs.
|
||||||
|
|
||||||
@ -697,48 +697,48 @@ zsh : <a> <b> <ca> <b> <c> <a-b-c>
|
|||||||
|
|
||||||
* In ksh93, the ''_'' parameter has even more uses. It is used in the same way as ''self'' in some object-oriented languages; as a placeholder for some data local to a class; and also as the mechanism for class inheritance. In most other contexts, ''_'' is compatible with Bash.
|
* In ksh93, the ''_'' parameter has even more uses. It is used in the same way as ''self'' in some object-oriented languages; as a placeholder for some data local to a class; and also as the mechanism for class inheritance. In most other contexts, ''_'' is compatible with Bash.
|
||||||
|
|
||||||
* Bash only evaluates the subscripts of the slice expansion (''${x:y:z}'') if the parameter is set (for both nested expansions and arithmetic). For ranges, Bash evaluates as little as possible, i.e., if the first part is out of range, the second won't be evaluated. ksh93 and mksh always evaluate the subscript parts even if the parameter is unset. <code>
|
* Bash only evaluates the subscripts of the slice expansion (''${x:y:z}'') if the parameter is set (for both nested expansions and arithmetic). For ranges, Bash evaluates as little as possible, i.e., if the first part is out of range, the second won't be evaluated. ksh93 and mksh always evaluate the subscript parts even if the parameter is unset. <code>
|
||||||
$ bash -c 'n="y[\$(printf yo >&2)1]" m="y[\$(printf jo >&2)1]"; x=(); echo "${x[@]:n,6:m}"' # No output
|
$ bash -c 'n="y[\$(printf yo >&2)1]" m="y[\$(printf jo >&2)1]"; x=(); echo "${x[@]:n,6:m}"' # No output
|
||||||
$ bash -c 'n="y[\$(printf yo >&2)1]" m="y[\$(printf jo >&2)1]"; x=([5]=hi); echo "${x[@]:n,6:m}"'
|
$ bash -c 'n="y[\$(printf yo >&2)1]" m="y[\$(printf jo >&2)1]"; x=([5]=hi); echo "${x[@]:n,6:m}"'
|
||||||
yo
|
yo
|
||||||
$ bash -c 'n="y[\$(printf yo >&2)1]" m="y[\$(printf jo >&2)1]"; x=([6]=hi); echo "${x[@]:n,6:m}"'
|
$ bash -c 'n="y[\$(printf yo >&2)1]" m="y[\$(printf jo >&2)1]"; x=([6]=hi); echo "${x[@]:n,6:m}"'
|
||||||
yojo
|
yojo
|
||||||
$ bash -c 'n="y[\$(printf yo >&2)1]" m="y[\$(printf jo >&2)1]"; x=12345; echo "${x:n,5:m}"'
|
$ bash -c 'n="y[\$(printf yo >&2)1]" m="y[\$(printf jo >&2)1]"; x=12345; echo "${x:n,5:m}"'
|
||||||
yojo
|
yojo
|
||||||
$ bash -c 'n="y[\$(printf yo >&2)1]" m="y[\$(printf jo >&2)1]"; x=12345; echo "${x:n,6:m}"'
|
$ bash -c 'n="y[\$(printf yo >&2)1]" m="y[\$(printf jo >&2)1]"; x=12345; echo "${x:n,6:m}"'
|
||||||
yo
|
yo
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== Quote Nesting ====
|
==== Quote Nesting ====
|
||||||
|
|
||||||
* In most shells, when dealing with an "alternate" parameter expansion that expands to multiple words, and nesting such expansions, not all combinations of nested quoting are possible.
|
* In most shells, when dealing with an "alternate" parameter expansion that expands to multiple words, and nesting such expansions, not all combinations of nested quoting are possible.
|
||||||
<code>
|
<code>
|
||||||
# Bash
|
# Bash
|
||||||
$ typeset -a a=(meh bleh blerg) b
|
$ typeset -a a=(meh bleh blerg) b
|
||||||
$ IFS=e
|
$ IFS=e
|
||||||
$ printf "<%s> " "${b[@]-"${a[@]}" "${a[@]}"}"; echo # The entire PE is quoted so Bash considers the inner quotes redundant.
|
$ printf "<%s> " "${b[@]-"${a[@]}" "${a[@]}"}"; echo # The entire PE is quoted so Bash considers the inner quotes redundant.
|
||||||
<meh> <bleh> <blerg meh> <bleh> <blerg>
|
<meh> <bleh> <blerg meh> <bleh> <blerg>
|
||||||
$ printf "<%s> " "${b[@]-${a[@]} ${a[@]}}"; echo # The outer quotes cause the inner expansions to be considered quoted.
|
$ printf "<%s> " "${b[@]-${a[@]} ${a[@]}}"; echo # The outer quotes cause the inner expansions to be considered quoted.
|
||||||
<meh> <bleh> <blerg meh> <bleh> <blerg>
|
<meh> <bleh> <blerg meh> <bleh> <blerg>
|
||||||
$ b=(meep beep)
|
$ b=(meep beep)
|
||||||
$ printf "<%s> " "${b[@]-"${a[@]}" "${a[@]}"}" "${b[@]-${a[@]} ${a[@]}}"; echo # Again no surprises. Outer quotes quote everything recursively.
|
$ printf "<%s> " "${b[@]-"${a[@]}" "${a[@]}"}" "${b[@]-${a[@]} ${a[@]}}"; echo # Again no surprises. Outer quotes quote everything recursively.
|
||||||
<meep> <beep> <meep> <beep>
|
<meep> <beep> <meep> <beep>
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Now lets see what can happen if we leave the outside unquoted.
|
Now lets see what can happen if we leave the outside unquoted.
|
||||||
<code>
|
<code>
|
||||||
# Bash
|
# Bash
|
||||||
$ typeset -a a=(meh bleh blerg) b
|
$ typeset -a a=(meh bleh blerg) b
|
||||||
$ IFS=e
|
$ IFS=e
|
||||||
$ printf "<%s> " ${b[@]-"${a[@]}" "${a[@]}"}; echo # Inner quotes make inner expansions quoted.
|
$ printf "<%s> " ${b[@]-"${a[@]}" "${a[@]}"}; echo # Inner quotes make inner expansions quoted.
|
||||||
<meh> <bleh> <blerg meh> <bleh> <blerg>
|
<meh> <bleh> <blerg meh> <bleh> <blerg>
|
||||||
$ printf "<%s> " ${b[@]-${a[@]} ${a[@]}}; echo' # No quotes at all wordsplits / globs, like you'd expect.
|
$ printf "<%s> " ${b[@]-${a[@]} ${a[@]}}; echo' # No quotes at all wordsplits / globs, like you'd expect.
|
||||||
<m> <h> <bl> <h> <bl> <rg m> <h> <bl> <h> <bl> <rg>
|
<m> <h> <bl> <h> <bl> <rg m> <h> <bl> <h> <bl> <rg>
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
This all might be intuitive, and is the most common implementation, but this design sucks for a number of reasons. For one, it means Bash makes it absolutely impossible to expand any part of the inner region //unquoted// while leaving the outer region quoted. Quoting the outer forces quoting of the inner regions recursively (except nested command substitutions of course). Word-splitting is necessary to split words of the inner region, which cannot be done together with outer quoting. Consider the following (only slightly far-fetched) code:
|
This all might be intuitive, and is the most common implementation, but this design sucks for a number of reasons. For one, it means Bash makes it absolutely impossible to expand any part of the inner region //unquoted// while leaving the outer region quoted. Quoting the outer forces quoting of the inner regions recursively (except nested command substitutions of course). Word-splitting is necessary to split words of the inner region, which cannot be done together with outer quoting. Consider the following (only slightly far-fetched) code:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
# Bash (non-working example)
|
# Bash (non-working example)
|
||||||
|
|
||||||
unset -v IFS # make sure we have a default IFS
|
unset -v IFS # make sure we have a default IFS
|
||||||
@ -754,7 +754,7 @@ typeset -a otherArgs=(arg3 arg4)
|
|||||||
# What do you think will actually happen...
|
# What do you think will actually happen...
|
||||||
|
|
||||||
"${someCmd[@]-"$someOtherCmd" arg2 "${otherArgs[@]}"}" arg5
|
"${someCmd[@]-"$someOtherCmd" arg2 "${otherArgs[@]}"}" arg5
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
This final line is perhaps not the most obvious, but I've run into cases were this type of logic can be desirable and realistic. We can deduce what was intended:
|
This final line is perhaps not the most obvious, but I've run into cases were this type of logic can be desirable and realistic. We can deduce what was intended:
|
||||||
|
|
||||||
@ -763,40 +763,40 @@ This final line is perhaps not the most obvious, but I've run into cases were th
|
|||||||
|
|
||||||
Unfortunately, it is impossible to get the intended result in Bash (and most other shells) without taking a considerably different approach. The only way to split the literal inner parts is through word-splitting, which requires that the PE be unquoted. But, the only way to expand the outer expansion correctly without word-splitting or globbing is to quote it. Bash will actually expand the command as one of these:
|
Unfortunately, it is impossible to get the intended result in Bash (and most other shells) without taking a considerably different approach. The only way to split the literal inner parts is through word-splitting, which requires that the PE be unquoted. But, the only way to expand the outer expansion correctly without word-splitting or globbing is to quote it. Bash will actually expand the command as one of these:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
# The quoted PE produces a correct result here...
|
# The quoted PE produces a correct result here...
|
||||||
$ bash -c 'typeset -a someCmd=(myCmd arg1 "arg2 yay!" "third*arg*" 4); printf "<%s> " "${someCmd[@]-"$someOtherCmd" arg2 "${otherArgs[@]}"}" arg5; echo'
|
$ bash -c 'typeset -a someCmd=(myCmd arg1 "arg2 yay!" "third*arg*" 4); printf "<%s> " "${someCmd[@]-"$someOtherCmd" arg2 "${otherArgs[@]}"}" arg5; echo'
|
||||||
<myCmd> <arg1> <arg2 yay!> <third*arg*> <4> <arg5>
|
<myCmd> <arg1> <arg2 yay!> <third*arg*> <4> <arg5>
|
||||||
|
|
||||||
# ...but in the opposite case the first 3 arguments are glued together. There are no workarounds.
|
# ...but in the opposite case the first 3 arguments are glued together. There are no workarounds.
|
||||||
$ bash -c 'typeset -a otherArgs=(arg3 arg4); someOtherCmd=mycommand; printf "<%s> " "${someCmd[@]-"$someOtherCmd" arg2 "${otherArgs[@]}"}" arg5; echo'
|
$ bash -c 'typeset -a otherArgs=(arg3 arg4); someOtherCmd=mycommand; printf "<%s> " "${someCmd[@]-"$someOtherCmd" arg2 "${otherArgs[@]}"}" arg5; echo'
|
||||||
<mycommand arg2 arg3> <arg4> <arg5>
|
<mycommand arg2 arg3> <arg4> <arg5>
|
||||||
|
|
||||||
# UNLESS! we unquote the outer expansion allowing the inner quotes to
|
# UNLESS! we unquote the outer expansion allowing the inner quotes to
|
||||||
# affect the necessary parts while allowing word-splitting to split the literals:
|
# affect the necessary parts while allowing word-splitting to split the literals:
|
||||||
$ bash -c 'typeset -a otherArgs=(arg3 arg4); someOtherCmd=mycommand; printf "<%s> " ${someCmd[@]-"$someOtherCmd" arg2 "${otherArgs[@]}"} arg5; echo'
|
$ bash -c 'typeset -a otherArgs=(arg3 arg4); someOtherCmd=mycommand; printf "<%s> " ${someCmd[@]-"$someOtherCmd" arg2 "${otherArgs[@]}"} arg5; echo'
|
||||||
<mycommand> <arg2> <arg3> <arg4> <arg5>
|
<mycommand> <arg2> <arg3> <arg4> <arg5>
|
||||||
|
|
||||||
# Success!!!
|
# Success!!!
|
||||||
$ bash -c 'typeset -a someCmd=(myCmd arg1 "arg2 yay!" "third*arg*" 4); printf "<%s> " ${someCmd[@]-"$someOtherCmd" arg2 "${otherArgs[@]}"} arg5; echo'
|
$ bash -c 'typeset -a someCmd=(myCmd arg1 "arg2 yay!" "third*arg*" 4); printf "<%s> " ${someCmd[@]-"$someOtherCmd" arg2 "${otherArgs[@]}"} arg5; echo'
|
||||||
<myCmd> <arg1> <arg2> <yay!> <third*arg*> <4> <arg5>
|
<myCmd> <arg1> <arg2> <yay!> <third*arg*> <4> <arg5>
|
||||||
|
|
||||||
# ...Ah f^^k. (again, no workaround possible.)
|
# ...Ah f^^k. (again, no workaround possible.)
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
=== The ksh93 exception ===
|
=== The ksh93 exception ===
|
||||||
To the best of my knowledge, ksh93 is the only shell that acts differently. Rather than forcing nested expansions into quoting, a quote at the beginning and end of the nested region will cause the quote state to reverse itself within the nested part. I have no idea whether it's an intentional or documented effect, but it does solve the problem and consequently adds a lot of potential power to these expansions.
|
To the best of my knowledge, ksh93 is the only shell that acts differently. Rather than forcing nested expansions into quoting, a quote at the beginning and end of the nested region will cause the quote state to reverse itself within the nested part. I have no idea whether it's an intentional or documented effect, but it does solve the problem and consequently adds a lot of potential power to these expansions.
|
||||||
|
|
||||||
All we need to do is add two extra double-quotes:
|
All we need to do is add two extra double-quotes:
|
||||||
<code>
|
<code>
|
||||||
# ksh93 passing the two failed tests from above:
|
# ksh93 passing the two failed tests from above:
|
||||||
|
|
||||||
$ ksh -c 'otherArgs=(arg3 arg4); someOtherCmd="mycommand"; printf "<%s> " "${someCmd[@]-""$someOtherCmd" arg2 "${otherArgs[@]}""}" arg5; echo'
|
$ ksh -c 'otherArgs=(arg3 arg4); someOtherCmd="mycommand"; printf "<%s> " "${someCmd[@]-""$someOtherCmd" arg2 "${otherArgs[@]}""}" arg5; echo'
|
||||||
<mycommand> <arg2> <arg3> <arg4> <arg5>
|
<mycommand> <arg2> <arg3> <arg4> <arg5>
|
||||||
|
|
||||||
$ ksh -c 'typeset -a someCmd=(myCmd arg1 "arg2 yay!" "third*arg*" 4); printf "<%s> " "${someCmd[@]-""$someOtherCmd" arg2 "${otherArgs[@]}""}" arg5; echo'
|
$ ksh -c 'typeset -a someCmd=(myCmd arg1 "arg2 yay!" "third*arg*" 4); printf "<%s> " "${someCmd[@]-""$someOtherCmd" arg2 "${otherArgs[@]}""}" arg5; echo'
|
||||||
<myCmd> <arg1> <arg2 yay!> <third*arg*> <4> <arg5>
|
<myCmd> <arg1> <arg2 yay!> <third*arg*> <4> <arg5>
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
This can be used to control the quote state of any part of any expansion to an arbitrary depth. Sadly, it is the only shell that does this and the difference may introduce a possible compatibility problem.
|
This can be used to control the quote state of any part of any expansion to an arbitrary depth. Sadly, it is the only shell that does this and the difference may introduce a possible compatibility problem.
|
||||||
===== See also =====
|
===== See also =====
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
====== Quotes and escaping ======
|
====== Quotes and escaping ======
|
||||||
|
|
||||||
{{keywords>bash shell scripting quoting quotes escape backslash marks singlequotes doublequotes single double}}
|
{{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:
|
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''
|
* **per-character escaping** using a backslash: ''\$stuff''
|
||||||
* **weak quoting** with double-quotes: ''"stuff"''
|
* **weak quoting** with double-quotes: ''"stuff"''
|
||||||
* **strong quoting** with single-quotes: ''<nowiki>'stuff'</nowiki>''
|
* **strong quoting** with single-quotes: ''<nowiki>'stuff'</nowiki>''
|
||||||
|
|
||||||
All three forms have the very same purpose: **They give you general control over parsing, expansion and expansion results.**
|
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.
|
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 ''<nowiki>'</nowiki>'', 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:
|
:!: **ATTENTION** :!: The quote characters (''"'', double quote and ''<nowiki>'</nowiki>'', 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:
|
||||||
<code>
|
<code>
|
||||||
### NO NO NO: this passes three strings:
|
### NO NO NO: this passes three strings:
|
||||||
### (1) "my
|
### (1) "my
|
||||||
### (2) multiword
|
### (2) multiword
|
||||||
@ -26,29 +26,29 @@ command "my multiword argument"
|
|||||||
### YOU NEED ###
|
### YOU NEED ###
|
||||||
MYARG="my multiword argument"
|
MYARG="my multiword argument"
|
||||||
command "$MYARG"
|
command "$MYARG"
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Per-character escaping =====
|
===== 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:
|
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:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
echo \$HOME is set to \"$HOME\"
|
echo \$HOME is set to \"$HOME\"
|
||||||
</code>
|
</code>
|
||||||
* ''\$HOME'' won't expand because it's not in variable-expansion syntax anymore
|
* ''\$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 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:
|
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:
|
||||||
<code>
|
<code>
|
||||||
# escapestr_sed()
|
# escapestr_sed()
|
||||||
# read a stream from stdin and escape characters in text that could be interpreted as
|
# read a stream from stdin and escape characters in text that could be interpreted as
|
||||||
# special characters by sed
|
# special characters by sed
|
||||||
escape_sed() {
|
escape_sed() {
|
||||||
sed \
|
sed \
|
||||||
-e 's/\//\\\//g' \
|
-e 's/\//\\\//g' \
|
||||||
-e 's/\&/\\\&/g'
|
-e 's/\&/\\\&/g'
|
||||||
}
|
}
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
The backslash can be used to mask every character that has a special meaning to bash. __Exception:__ Inside a single-quoted string (see below).
|
The backslash can be used to mask every character that has a special meaning to bash. __Exception:__ Inside a single-quoted string (see below).
|
||||||
|
|
||||||
@ -68,14 +68,14 @@ Inside a weak-quoted string there's **no special interpretion of**:
|
|||||||
|
|
||||||
Everything else, especially [[syntax:pe | parameter expansion]], is performed!
|
Everything else, especially [[syntax:pe | parameter expansion]], is performed!
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
ls -l "*"
|
ls -l "*"
|
||||||
</code>
|
</code>
|
||||||
Will not be expanded. ''ls'' gets the literal ''*'' as argument. It will, unless you have a file named ''*'', spit out an error.
|
Will not be expanded. ''ls'' gets the literal ''*'' as argument. It will, unless you have a file named ''*'', spit out an error.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
echo "Your PATH is: $PATH"
|
echo "Your PATH is: $PATH"
|
||||||
</code>
|
</code>
|
||||||
Will work as expected. ''$PATH'' is expanded, because it's double (weak) quoted.
|
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 a backslash in double quotes ("weak quoting") occurs, there are 2 ways to deal with it
|
||||||
@ -91,13 +91,13 @@ Strong quoting is very easy to explain:
|
|||||||
|
|
||||||
Inside a single-quoted string **nothing** is interpreted, except the single-quote that closes the string.
|
Inside a single-quoted string **nothing** is interpreted, except the single-quote that closes the string.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
echo 'Your PATH is: $PATH'
|
echo 'Your PATH is: $PATH'
|
||||||
</code>
|
</code>
|
||||||
''$PATH'' won't be expanded, it's interpreted as ordinary text because it's surrounded by strong quotes.
|
''$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 "''<nowiki>'</nowiki>''" as literal text:
|
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 "''<nowiki>'</nowiki>''" as literal text:
|
||||||
<code>
|
<code>
|
||||||
# WRONG
|
# WRONG
|
||||||
echo 'Here's my test...'
|
echo 'Here's my test...'
|
||||||
|
|
||||||
@ -106,15 +106,15 @@ echo 'Here'\''s my test...'
|
|||||||
|
|
||||||
# ALTERNATIVE: It's also possible to mix-and-match quotes for readability:
|
# ALTERNATIVE: It's also possible to mix-and-match quotes for readability:
|
||||||
echo "Here's my test"
|
echo "Here's my test"
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
|
|
||||||
===== ANSI C like strings =====
|
===== ANSI C like strings =====
|
||||||
|
|
||||||
Bash provides another quoting mechanism: Strings that contain ANSI C-like escape sequences. The Syntax is:
|
Bash provides another quoting mechanism: Strings that contain ANSI C-like escape sequences. The Syntax is:
|
||||||
<code>
|
<code>
|
||||||
$'string'
|
$'string'
|
||||||
</code>
|
</code>
|
||||||
where the following escape sequences are decoded in ''string'':
|
where the following escape sequences are decoded in ''string'':
|
||||||
^ Code ^ Meaning ^
|
^ Code ^ Meaning ^
|
||||||
| ''\"'' | double-quote |
|
| ''\"'' | double-quote |
|
||||||
@ -141,13 +141,13 @@ This is especially useful when you want to pass special characters as arguments
|
|||||||
|
|
||||||
The resulting text is treated as if it were **single-quoted**. No further expansion happens.
|
The resulting text is treated as if it were **single-quoted**. No further expansion happens.
|
||||||
|
|
||||||
The ''<nowiki>$'...'</nowiki>'' syntax comes from ksh93, but is portable to most modern shells including pdksh. A [[http://austingroupbugs.net/view.php?id=249#c590 | specification]] 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).
|
The ''<nowiki>$'...'</nowiki>'' syntax comes from ksh93, but is portable to most modern shells including pdksh. A [[http://austingroupbugs.net/view.php?id=249#c590 | specification]] 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 =====
|
===== I18N/L10N =====
|
||||||
|
|
||||||
A dollar-sign followed by a double-quoted string, for example
|
A dollar-sign followed by a double-quoted string, for example
|
||||||
<code>
|
<code>
|
||||||
echo $"generating database..."
|
echo $"generating database..."
|
||||||
</code>
|
</code>
|
||||||
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.
|
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.
|
If the string was replaced (translated), the result is double quoted.
|
||||||
@ -164,68 +164,68 @@ For useful examples to localize your scripts, please see [[http://tldp.org/LDP/a
|
|||||||
==== String lists in for-loops ====
|
==== String lists in for-loops ====
|
||||||
|
|
||||||
The [[syntax:ccmd:classic_for | classic for loop]] uses a list of words to iterate through. The list can also be in a variable:
|
The [[syntax:ccmd:classic_for | classic for loop]] uses a list of words to iterate through. The list can also be in a variable:
|
||||||
<code>
|
<code>
|
||||||
mylist="DOG CAT BIRD HORSE"
|
mylist="DOG CAT BIRD HORSE"
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
**__WRONG__** way to iterate through this list:
|
**__WRONG__** way to iterate through this list:
|
||||||
<code>
|
<code>
|
||||||
for animal in "$mylist"; do
|
for animal in "$mylist"; do
|
||||||
echo $animal
|
echo $animal
|
||||||
done
|
done
|
||||||
</code>
|
</code>
|
||||||
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.
|
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.
|
||||||
|
|
||||||
**__RIGHT__** way to iterate through this list:
|
**__RIGHT__** way to iterate through this list:
|
||||||
<code>
|
<code>
|
||||||
for animal in $mylist; do
|
for animal in $mylist; do
|
||||||
echo $animal
|
echo $animal
|
||||||
done
|
done
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== Working out the test-command ====
|
==== Working out the test-command ====
|
||||||
|
|
||||||
The command ''test'' or ''[ ... ]'' ([[commands:classictest | the classic test command]]) is an ordinary command, so ordinary syntax rules apply. Let's take string comparison as an example:
|
The command ''test'' or ''[ ... ]'' ([[commands:classictest | the classic test command]]) is an ordinary command, so ordinary syntax rules apply. Let's take string comparison as an example:
|
||||||
<code>
|
<code>
|
||||||
[ WORD = WORD ]
|
[ WORD = WORD ]
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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:
|
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:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
test WORD = WORD
|
test WORD = WORD
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
|
|
||||||
When you compare variables, it's wise to quote them. Let's create a test string with spaces:
|
When you compare variables, it's wise to quote them. Let's create a test string with spaces:
|
||||||
<code>
|
<code>
|
||||||
mystring="my string"
|
mystring="my string"
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
And now check that string against the word "testword":
|
And now check that string against the word "testword":
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
[ $mystring = testword ] # WRONG!
|
[ $mystring = testword ] # WRONG!
|
||||||
</code>
|
</code>
|
||||||
This fails! These are too many arguments for the string comparison test. After expansion is performed, you really execute:
|
This fails! These are too many arguments for the string comparison test. After expansion is performed, you really execute:
|
||||||
<code>
|
<code>
|
||||||
[ my string = testword ]
|
[ my string = testword ]
|
||||||
test my string = testword
|
test my string = testword
|
||||||
</code>
|
</code>
|
||||||
Which is wrong, because ''my'' and ''string'' are two separate arguments.
|
Which is wrong, because ''my'' and ''string'' are two separate arguments.
|
||||||
|
|
||||||
So what you really want to do is:
|
So what you really want to do is:
|
||||||
<code>
|
<code>
|
||||||
[ "$mystring" = testword ] # RIGHT!
|
[ "$mystring" = testword ] # RIGHT!
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
test 'my string' = testword
|
test 'my string' = testword
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Now the command has three parameters, which makes sense for a binary (two argument) operator.
|
Now the command has three parameters, which makes sense for a binary (two argument) operator.
|
||||||
|
|
||||||
**__Hint:__** Inside the [[syntax:ccmd:conditional_expression | conditional expression]] (''<nowiki>[[ ]]</nowiki>'') Bash doesn't perform word splitting, and thus you don't need to quote your variable references - they are always seen as "one word".
|
**__Hint:__** Inside the [[syntax:ccmd:conditional_expression | conditional expression]] (''<nowiki>[[ ]]</nowiki>'') 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 =====
|
===== See also =====
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
====== Redirection ======
|
====== Redirection ======
|
||||||
|
|
||||||
<wrap left todo>Fix me: To be continued</wrap>
|
<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 [[syntax:grammar:parser_exec | simple command is about to be executed]].
|
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 [[syntax:grammar:parser_exec | simple command is about to be executed]].
|
||||||
@ -11,36 +11,36 @@ Under normal circumstances, there are 3 files open, accessible by the file descr
|
|||||||
|''stdout''|1|standard output stream (e.g. monitor)|
|
|''stdout''|1|standard output stream (e.g. monitor)|
|
||||||
|''stderr''|2|standard error output stream (usually also on 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>
|
<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!
|
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:
|
Whenever you **name** such a filedescriptor, i.e. you want to redirect this descriptor, you just use the number:
|
||||||
<code>
|
<code>
|
||||||
# this executes the cat-command and redirects its error messages (stderr) to the bit bucket
|
# this executes the cat-command and redirects its error messages (stderr) to the bit bucket
|
||||||
cat some_file.txt 2>/dev/null
|
cat some_file.txt 2>/dev/null
|
||||||
</code>
|
</code>
|
||||||
Whenever you **reference** a descriptor, to point to its current target file, then you use a "''&''" followed by a the descriptor number:
|
Whenever you **reference** a descriptor, to point to its current target file, then you use a "''&''" followed by a the descriptor number:
|
||||||
<code>
|
<code>
|
||||||
# this executes the echo-command and redirects its normal output (stdout) to the standard error target
|
# this executes the echo-command and redirects its normal output (stdout) to the standard error target
|
||||||
echo "There was an error" 1>&2
|
echo "There was an error" 1>&2
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
The redirection operation can be **anywhere** in a simple command, so these examples are equivalent:
|
The redirection operation can be **anywhere** in a simple command, so these examples are equivalent:
|
||||||
<code>
|
<code>
|
||||||
cat foo.txt bar.txt >new.txt
|
cat foo.txt bar.txt >new.txt
|
||||||
cat >new.txt foo.txt bar.txt
|
cat >new.txt foo.txt bar.txt
|
||||||
>new.txt cat foo.txt bar.txt
|
>new.txt cat foo.txt bar.txt
|
||||||
</code>
|
</code>
|
||||||
<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>
|
<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 =====
|
===== Valid redirection targets and sources =====
|
||||||
This syntax is recognized whenever a ''TARGET'' or a ''SOURCE'' specification (like below in the details descriptions) is used.
|
This syntax is recognized whenever a ''TARGET'' or a ''SOURCE'' specification (like below in the details descriptions) is used.
|
||||||
|
|
||||||
^Syntax^Description^
|
^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)|
|
|''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)|
|
|''&N''|references the current target/source of the filedescriptor ''N'' ("duplicates" the filedescriptor)|
|
||||||
|''&-''|closes the redirected filedescriptor, useful instead of ''> /dev/null'' constructs (''> &-'')|
|
|''&-''|closes the redirected filedescriptor, useful instead of ''> /dev/null'' constructs (''> &-'')|
|
||||||
|''/dev/fd/N''|duplicates the filedescriptor ''N'', if ''N'' is a valid integer|
|
|''/dev/fd/N''|duplicates the filedescriptor ''N'', if ''N'' is a valid integer|
|
||||||
|''/dev/stdin''|duplicates filedescriptor 0 (''stdin'')|
|
|''/dev/stdin''|duplicates filedescriptor 0 (''stdin'')|
|
||||||
|''/dev/stdout''|duplicates filedescriptor 1 (''stdout'')|
|
|''/dev/stdout''|duplicates filedescriptor 1 (''stdout'')|
|
||||||
@ -54,96 +54,96 @@ If a target/source specification fails to open, the whole redirection operation
|
|||||||
|
|
||||||
|
|
||||||
===== Redirecting output =====
|
===== Redirecting output =====
|
||||||
<code>
|
<code>
|
||||||
N > TARGET
|
N > TARGET
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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.
|
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 [[commands:builtin:set | the set builtin]], 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 ''>''.
|
If the option ''noclobber'' is set with [[commands:builtin:set | the set builtin]], 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 =====
|
===== Appending redirected output =====
|
||||||
<code>
|
<code>
|
||||||
N >> TARGET
|
N >> TARGET
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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.
|
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 =====
|
===== Redirecting output and error output =====
|
||||||
<code>
|
<code>
|
||||||
&> TARGET
|
&> TARGET
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
>& TARGET
|
>& TARGET
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
This special syntax redirects both, ''stdout'' and ''stderr'' to the specified target. It's **equivalent** to
|
This special syntax redirects both, ''stdout'' and ''stderr'' to the specified target. It's **equivalent** to
|
||||||
<code>
|
<code>
|
||||||
> TARGET 2>&1
|
> TARGET 2>&1
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Since Bash4, there's ''&<nowiki>>>TARGET</nowiki>'', which is equivalent to ''<nowiki>>></nowiki> TARGET 2>&1''.
|
Since Bash4, there's ''&<nowiki>>>TARGET</nowiki>'', which is equivalent to ''<nowiki>>></nowiki> TARGET 2>&1''.
|
||||||
|
|
||||||
<wrap center important>This syntax is deprecated and should not be used. See the page about [[scripting:obsolete | obsolete and deprecated syntax]].</wrap>
|
<wrap center important>This syntax is deprecated and should not be used. See the page about [[scripting:obsolete | obsolete and deprecated syntax]].</wrap>
|
||||||
|
|
||||||
|
|
||||||
===== Appending redirected output and error output =====
|
===== Appending redirected output and error output =====
|
||||||
|
|
||||||
To append the cumulative redirection of ''stdout'' and ''stderr'' to a file you simply do
|
To append the cumulative redirection of ''stdout'' and ''stderr'' to a file you simply do
|
||||||
<code>
|
<code>
|
||||||
>> FILE 2>&1
|
>> FILE 2>&1
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
&>> FILE
|
&>> FILE
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Transporting stdout and stderr through a pipe =====
|
===== Transporting stdout and stderr through a pipe =====
|
||||||
<code>
|
<code>
|
||||||
COMMAND1 2>&1 | COMMAND2
|
COMMAND1 2>&1 | COMMAND2
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
COMMAND1 |& COMMAND2
|
COMMAND1 |& COMMAND2
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
|
|
||||||
===== Redirecting input =====
|
===== Redirecting input =====
|
||||||
<code>
|
<code>
|
||||||
N < SOURCE
|
N < SOURCE
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
The input descriptor ''N'' uses ''SOURCE'' as its data source. If ''N'' is omitted, filedescriptor 0 (''stdin'') is assumed.
|
The input descriptor ''N'' uses ''SOURCE'' as its data source. If ''N'' is omitted, filedescriptor 0 (''stdin'') is assumed.
|
||||||
|
|
||||||
|
|
||||||
===== Here documents =====
|
===== Here documents =====
|
||||||
<BOOKMARK:tag_heredoc>
|
<BOOKMARK:tag_heredoc>
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
<<TAG
|
<<TAG
|
||||||
...
|
...
|
||||||
TAG
|
TAG
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
<<-TAG
|
<<-TAG
|
||||||
...
|
...
|
||||||
TAG
|
TAG
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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 ''<nowiki><<</nowiki>'' is used together with a tag ''TAG'' that's used to mark the end of input later:
|
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 ''<nowiki><<</nowiki>'' is used together with a tag ''TAG'' that's used to mark the end of input later:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
# display help
|
# display help
|
||||||
|
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
Sorry...
|
Sorry...
|
||||||
No help available yet for $PROGRAM.
|
No help available yet for $PROGRAM.
|
||||||
Hehe...
|
Hehe...
|
||||||
EOF
|
EOF
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
As you see, substitutions are possible. To be precise, the following substitutions and expansions are performed in the here-document data:
|
As you see, substitutions are possible. To be precise, the following substitutions and expansions are performed in the here-document data:
|
||||||
* [[syntax:pe | Parameter expansion]]
|
* [[syntax:pe | Parameter expansion]]
|
||||||
@ -151,31 +151,31 @@ As you see, substitutions are possible. To be precise, the following substitutio
|
|||||||
* [[syntax:expansion:arith | Arithmetic expansion]]
|
* [[syntax:expansion:arith | Arithmetic expansion]]
|
||||||
|
|
||||||
You can avoid that by quoting the tag:
|
You can avoid that by quoting the tag:
|
||||||
<code>
|
<code>
|
||||||
cat <<"EOF"
|
cat <<"EOF"
|
||||||
This won't be expanded: $PATH
|
This won't be expanded: $PATH
|
||||||
EOF
|
EOF
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Last but not least, if the redirection operator ''<nowiki><<</nowiki>'' 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.
|
Last but not least, if the redirection operator ''<nowiki><<</nowiki>'' 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.
|
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>
|
<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 =====
|
===== Here strings =====
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
<<< WORD
|
<<< WORD
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
The here-strings are a variation of the here-documents. The word ''WORD'' is taken for the input redirection:
|
The here-strings are a variation of the here-documents. The word ''WORD'' is taken for the input redirection:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
cat <<< "Hello world... $NAME is here..."
|
cat <<< "Hello world... $NAME is here..."
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Just beware to quote the ''WORD'' if it contains spaces. Otherwise the rest will be given as normal parameters.
|
Just beware to quote the ''WORD'' if it contains spaces. Otherwise the rest will be given as normal parameters.
|
||||||
|
|
||||||
@ -185,27 +185,27 @@ The here-string will append a newline (''\n'') to the data.
|
|||||||
|
|
||||||
More redirection operations can occur in a line of course. The order is **important**! They're evaluated from **left to right**.
|
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**:
|
If you want to redirect both, ''stderr'' and ''stdout'' to the same file (like ''/dev/null'', to hide it), this is **the wrong way**:
|
||||||
<code bash>
|
<code bash>
|
||||||
# { echo OUTPUT; echo ERRORS >&2; } is to simulate something that outputs to STDOUT and STDERR
|
# { echo OUTPUT; echo ERRORS >&2; } is to simulate something that outputs to STDOUT and STDERR
|
||||||
# you can test with it
|
# you can test with it
|
||||||
{ echo OUTPUT; echo ERRORS >&2; } 2>&1 1>/dev/null
|
{ echo OUTPUT; echo ERRORS >&2; } 2>&1 1>/dev/null
|
||||||
</code>
|
</code>
|
||||||
Why? Relatively easy:
|
Why? Relatively easy:
|
||||||
* initially, ''stdout'' points to your terminal (you read it)
|
* initially, ''stdout'' points to your terminal (you read it)
|
||||||
* same applies to ''stderr'', it's connected to your terminal
|
* 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...)
|
* ''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''
|
* ''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:
|
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:
|
||||||
<code bash>
|
<code bash>
|
||||||
{ echo OUTPUT; echo ERRORS >&2; } 1>/dev/null 2>&1
|
{ echo OUTPUT; echo ERRORS >&2; } 1>/dev/null 2>&1
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== Examples =====
|
===== Examples =====
|
||||||
|
|
||||||
How to make a program quiet (assuming all output goes to ''STDOUT'' and ''STDERR''?
|
How to make a program quiet (assuming all output goes to ''STDOUT'' and ''STDERR''?
|
||||||
<code>
|
<code>
|
||||||
command >/dev/null 2>&1
|
command >/dev/null 2>&1
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
===== See also =====
|
===== See also =====
|
||||||
* Internal: [[howto:redirection_tutorial | Illustrated Redirection Tutorial]]
|
* Internal: [[howto:redirection_tutorial | Illustrated Redirection Tutorial]]
|
||||||
|
@ -9,8 +9,8 @@
|
|||||||
|''?''|question mark|Status of the most recently executed foreground-pipeline (exit/return code)|
|
|''?''|question mark|Status of the most recently executed foreground-pipeline (exit/return code)|
|
||||||
|''-''|dash|Current option flags set by the shell itself, on invocation, or using the [[commands:builtin:set | set builtin command]]. It's just a set of characters, like ''himB'' for ''h'', ''i'', ''m'' and ''B''.|
|
|''-''|dash|Current option flags set by the shell itself, on invocation, or using the [[commands:builtin:set | set builtin command]]. It's just a set of characters, like ''himB'' for ''h'', ''i'', ''m'' and ''B''.|
|
||||||
|''$''|dollar-sign|The process ID (PID) of the shell. In an [[syntax:ccmd:grouping_subshell | explicit subshell]] it expands to the PID of the current "main shell", not the subshell. This is different from ''$BASHPID''!|
|
|''$''|dollar-sign|The process ID (PID) of the shell. In an [[syntax:ccmd:grouping_subshell | explicit subshell]] it expands to the PID of the current "main shell", not the subshell. This is different from ''$BASHPID''!|
|
||||||
|''!''|exclamation mark|The process ID (PID) of the most recently executed background pipeline (like started with ''command &'')|
|
|''!''|exclamation mark|The process ID (PID) of the most recently executed background pipeline (like started with ''command &'')|
|
||||||
|''0''|zero|The name of the shell or the shell script (filename). Set by the shell itself. \\ If Bash is started with a filename to execute (script), it's set to this filename. If started with the ''-c <CMDLINE>'' option (commandline given as argument), then ''$0'' will be the first argument after the given ''<CMDLINE>''. Otherwise, it is set to the string given on invocation for ''argv[0]''. \\ Unlike popular belief, ''$0'' is //not a positional parameter//.|
|
|''0''|zero|The name of the shell or the shell script (filename). Set by the shell itself. \\ If Bash is started with a filename to execute (script), it's set to this filename. If started with the ''-c <CMDLINE>'' option (commandline given as argument), then ''$0'' will be the first argument after the given ''<CMDLINE>''. Otherwise, it is set to the string given on invocation for ''argv[0]''. \\ Unlike popular belief, ''$0'' is //not a positional parameter//.|
|
||||||
|''_''|underscore|A kind of catch-all parameter. Directly after shell invocation, it's set to the filename used to invoke Bash, or the absolute or relative path to the script, just like ''$0'' would show it. Subsequently, expands to the last argument to the previous command. Placed into the environment when executing commands, and set to the full pathname of these commands. When checking mail, this parameter holds the name of the mail file currently being checked.|
|
|''_''|underscore|A kind of catch-all parameter. Directly after shell invocation, it's set to the filename used to invoke Bash, or the absolute or relative path to the script, just like ''$0'' would show it. Subsequently, expands to the last argument to the previous command. Placed into the environment when executing commands, and set to the full pathname of these commands. When checking mail, this parameter holds the name of the mail file currently being checked.|
|
||||||
|
|
||||||
===== Shell Variables =====
|
===== Shell Variables =====
|
||||||
@ -36,9 +36,9 @@ in the environment when Bash starts up, each shell option in the list will
|
|||||||
be enabled before reading any startup files.
|
be enabled before reading any startup files.
|
||||||
|
|
||||||
Example content:
|
Example content:
|
||||||
<code>
|
<code>
|
||||||
cmdhist:expand_aliases:extquote:force_fignore:hostcomplete:interactive_comments:progcomp:promptvars:sourcepath
|
cmdhist:expand_aliases:extquote:force_fignore:hostcomplete:interactive_comments:progcomp:promptvars:sourcepath
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
This variable is read-only.
|
This variable is read-only.
|
||||||
|
|
||||||
@ -155,7 +155,7 @@ is ''${BASH_SOURCE[$i]}''. Use ''LINENO'' to obtain the current line number.
|
|||||||
^Set by Bash: |yes ^Default: |n/a |
|
^Set by Bash: |yes ^Default: |n/a |
|
||||||
|
|
||||||
An array variable whose members are assigned by the ''=~'' binary operator to
|
An array variable whose members are assigned by the ''=~'' binary operator to
|
||||||
the ''<nowiki>[[</nowiki>'' conditional command.
|
the ''<nowiki>[[</nowiki>'' conditional command.
|
||||||
|
|
||||||
The element with index 0 is the portion of the string matching the entire regular expression. The element
|
The element with index 0 is the portion of the string matching the entire regular expression. The element
|
||||||
with index ''n'' is the portion of the string matching the nth parenthesized
|
with index ''n'' is the portion of the string matching the nth parenthesized
|
||||||
@ -392,9 +392,9 @@ Automatically set to a string that uniquely describes the type of machine on
|
|||||||
which Bash is executing.
|
which Bash is executing.
|
||||||
|
|
||||||
Example content:
|
Example content:
|
||||||
<code>
|
<code>
|
||||||
x86_64
|
x86_64
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== LINENO ====
|
==== LINENO ====
|
||||||
^Variable: |''LINENO'' ^Since: |unknown |
|
^Variable: |''LINENO'' ^Since: |unknown |
|
||||||
@ -419,9 +419,9 @@ Automatically set to a string that fully describes the system type on which
|
|||||||
Bash is executing, in the standard GNU "cpu-company-system" format.
|
Bash is executing, in the standard GNU "cpu-company-system" format.
|
||||||
|
|
||||||
Example content:
|
Example content:
|
||||||
<code>
|
<code>
|
||||||
x86_64-unknown-linux-gnu
|
x86_64-unknown-linux-gnu
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
==== MAPFILE ====
|
==== MAPFILE ====
|
||||||
^Variable: |''MAPFILE'' ^Since: |unknown |
|
^Variable: |''MAPFILE'' ^Since: |unknown |
|
||||||
@ -464,9 +464,9 @@ Automatically set to a string that describes the operating system on which
|
|||||||
Bash is executing.
|
Bash is executing.
|
||||||
|
|
||||||
Example content:
|
Example content:
|
||||||
<code>
|
<code>
|
||||||
linux-gnu
|
linux-gnu
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
|
|
||||||
==== PIPESTATUS ====
|
==== PIPESTATUS ====
|
||||||
@ -609,7 +609,7 @@ This is a colon-separated list of directories in which the shell looks for
|
|||||||
destination directories specified by the ''cd'' command.
|
destination directories specified by the ''cd'' command.
|
||||||
|
|
||||||
Example content:
|
Example content:
|
||||||
<code>.:~:/usr</code>
|
<code>.:~:/usr</code>
|
||||||
|
|
||||||
==== COLUMNS ====
|
==== COLUMNS ====
|
||||||
^Variable: |''COLUMNS'' ^Since: |unknown |
|
^Variable: |''COLUMNS'' ^Since: |unknown |
|
||||||
@ -661,7 +661,7 @@ completion. A filename whose suffix matches one of the entries in ''FIGNORE''
|
|||||||
is excluded from the list of matched filenames.
|
is excluded from the list of matched filenames.
|
||||||
|
|
||||||
Example content:
|
Example content:
|
||||||
<code>.o:~</code>
|
<code>.o:~</code>
|
||||||
|
|
||||||
==== FUNCNEST ====
|
==== FUNCNEST ====
|
||||||
^Variable: |''FUNCNEST'' ^Since: |4.2-alpha |
|
^Variable: |''FUNCNEST'' ^Since: |4.2-alpha |
|
||||||
@ -743,8 +743,8 @@ line and must match the complete line (no implicit '*' is appended).
|
|||||||
Each pattern is tested against the line after the checks specified by
|
Each pattern is tested against the line after the checks specified by
|
||||||
''HISTCONTROL'' are applied.
|
''HISTCONTROL'' are applied.
|
||||||
|
|
||||||
In addition to the normal shell pattern matching characters, "&" matches the
|
In addition to the normal shell pattern matching characters, "&" matches the
|
||||||
previous history line. "&" may be escaped using a backslash; the backslash is
|
previous history line. "&" may be escaped using a backslash; the backslash is
|
||||||
removed before attempting a match.
|
removed before attempting a match.
|
||||||
|
|
||||||
The second and subsequent lines of a multi-line compound command are not tested,
|
The second and subsequent lines of a multi-line compound command are not tested,
|
||||||
@ -805,7 +805,7 @@ When ''HOSTFILE'' is unset, the hostname list is cleared.
|
|||||||
==== IFS ====
|
==== IFS ====
|
||||||
^Variable: |''IFS'' ^Since: |unknown |
|
^Variable: |''IFS'' ^Since: |unknown |
|
||||||
^Type: |normal variable ^Read-only: |no |
|
^Type: |normal variable ^Read-only: |no |
|
||||||
^Set by Bash: |no ^Default: |''<space><tab><newline>''|
|
^Set by Bash: |no ^Default: |''<space><tab><newline>''|
|
||||||
|
|
||||||
The Internal Field Separator that is used for word splitting
|
The Internal Field Separator that is used for word splitting
|
||||||
after expansion and to split lines into words with the read
|
after expansion and to split lines into words with the read
|
||||||
@ -933,7 +933,7 @@ When used in the text of the message, ''$_'' expands to
|
|||||||
the name of the current mailfile.
|
the name of the current mailfile.
|
||||||
|
|
||||||
Example content:
|
Example content:
|
||||||
<code>/var/mail/bfox?"You have mail":~/shell-mail?"$_ has mail!"</code>
|
<code>/var/mail/bfox?"You have mail":~/shell-mail?"$_ has mail!"</code>
|
||||||
|
|
||||||
==== OPTERR ====
|
==== OPTERR ====
|
||||||
^Variable: |''OPTERR'' ^Since: |unknown |
|
^Variable: |''OPTERR'' ^Since: |unknown |
|
||||||
@ -1015,7 +1015,7 @@ The value of this parameter is expanded and used as the primary prompt string.
|
|||||||
==== PS2 ====
|
==== PS2 ====
|
||||||
^Variable: |''PS2'' ^Since: |unknown |
|
^Variable: |''PS2'' ^Since: |unknown |
|
||||||
^Type: |normal variable ^Read-only: |no |
|
^Type: |normal variable ^Read-only: |no |
|
||||||
^Set by Bash: |if unset ^Default: |"''> ''" |
|
^Set by Bash: |if unset ^Default: |"''> ''" |
|
||||||
|
|
||||||
The value of this parameter is expanded as with PS1 and used as
|
The value of this parameter is expanded as with PS1 and used as
|
||||||
the secondary prompt string.
|
the secondary prompt string.
|
||||||
@ -1077,7 +1077,7 @@ The optional modifiers (p and l) are:
|
|||||||
|''l'' |A longer format, including minutes, of the form MMmSS.FFs. The value of p determines whether or not the fraction is included. |
|
|''l'' |A longer format, including minutes, of the form MMmSS.FFs. The value of p determines whether or not the fraction is included. |
|
||||||
|
|
||||||
If this variable is not set, Bash acts as if it had the value
|
If this variable is not set, Bash acts as if it had the value
|
||||||
<code>$'\nreal\t%3lR\nuser\t%3lU\nsys%3lS'</code>
|
<code>$'\nreal\t%3lR\nuser\t%3lU\nsys%3lS'</code>
|
||||||
|
|
||||||
If the value is null, no timing information is displayed.
|
If the value is null, no timing information is displayed.
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
====== Words... ======
|
====== Words... ======
|
||||||
|
|
||||||
{{keywords>bash shell scripting token words split splitting recognition}}
|
{{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".
|
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".
|
||||||
|
|
||||||
@ -12,19 +12,19 @@ Bash scans the command line and splits it into words, usually to put the paramet
|
|||||||
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.
|
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:
|
You don't know what I'm talking about? I'm talking about this:
|
||||||
<code>
|
<code>
|
||||||
$ echo Hello little world
|
$ echo Hello little world
|
||||||
Hello little world
|
Hello little world
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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.
|
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.**
|
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:
|
Here's an example:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
$ echo Hello little world
|
$ echo Hello little world
|
||||||
Hello little world
|
Hello little world
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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''"!
|
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''"!
|
||||||
|
|
||||||
@ -33,28 +33,28 @@ Bash treats blanks as special characters, but there are two ways to tell Bash no
|
|||||||
|
|
||||||
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:
|
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:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
$ echo Hello\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ little \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ world
|
$ echo Hello\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ little \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ world
|
||||||
Hello little world
|
Hello little world
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
None of the escaped spaces will be used to perform word splitting. Thus, echo is called with one argument: "''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**.
|
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:
|
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:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
$ echo "Hello little world"
|
$ echo "Hello little world"
|
||||||
Hello little world
|
Hello little world
|
||||||
</code>
|
</code>
|
||||||
<code>
|
<code>
|
||||||
$ echo 'Hello little world'
|
$ echo 'Hello little world'
|
||||||
Hello little world
|
Hello little world
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
__What is it all about now?__ Well, for example imagine a program that expects a filename as an argument, like cat. Filenames can have spaces in them:
|
__What is it all about now?__ Well, for example imagine a program that expects a filename as an argument, like cat. Filenames can have spaces in them:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
$ ls -l
|
$ ls -l
|
||||||
total 4
|
total 4
|
||||||
-rw-r--r-- 1 bonsai bonsai 5 Apr 18 18:16 test file
|
-rw-r--r-- 1 bonsai bonsai 5 Apr 18 18:16 test file
|
||||||
@ -68,7 +68,7 @@ m00!
|
|||||||
|
|
||||||
$ cat "test file"
|
$ cat "test file"
|
||||||
m00!
|
m00!
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
@ -82,24 +82,24 @@ After the command line has been split into words, Bash will perform expansion, i
|
|||||||
|
|
||||||
Imagine you have a filename stored in a variable:
|
Imagine you have a filename stored in a variable:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
MYFILE="test file"
|
MYFILE="test file"
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
When this variable is used, its occurance will be replaced by its content.
|
When this variable is used, its occurance will be replaced by its content.
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
$ cat $MYFILE
|
$ cat $MYFILE
|
||||||
cat: test: No such file or directory
|
cat: test: No such file or directory
|
||||||
cat: file: No such file or directory
|
cat: file: No such file or directory
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
Though this is another step where spaces make things difficult, **quoting** is used to work around the difficulty. Quotes also affect word splitting:
|
Though this is another step where spaces make things difficult, **quoting** is used to work around the difficulty. Quotes also affect word splitting:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
$ cat "$MYFILE"
|
$ cat "$MYFILE"
|
||||||
m00!
|
m00!
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -111,13 +111,13 @@ m00!
|
|||||||
===== Example =====
|
===== Example =====
|
||||||
|
|
||||||
Let's follow an unquoted command through these steps, assuming that the variable is set:
|
Let's follow an unquoted command through these steps, assuming that the variable is set:
|
||||||
<code>
|
<code>
|
||||||
MYFILE="THE FILE.TXT"
|
MYFILE="THE FILE.TXT"
|
||||||
</code>
|
</code>
|
||||||
and the first review is:
|
and the first review is:
|
||||||
<code>
|
<code>
|
||||||
echo The file is named $MYFILE
|
echo The file is named $MYFILE
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
The parser will scan for blanks and mark the relevant words ("splitting the command line"):
|
The parser will scan for blanks and mark the relevant words ("splitting the command line"):
|
||||||
|
|
||||||
@ -132,9 +132,9 @@ A [[syntax:pe | parameter/variable expansion]] is part of that command line, Bas
|
|||||||
|''echo''|''The''|''file''|''is''|''named''|''THE''|''FILE.TXT''|
|
|''echo''|''The''|''file''|''is''|''named''|''THE''|''FILE.TXT''|
|
||||||
|
|
||||||
Now let's imagine we quoted ''$MYFILE'', the command line now looks like:
|
Now let's imagine we quoted ''$MYFILE'', the command line now looks like:
|
||||||
<code>
|
<code>
|
||||||
echo The file is named "$MYFILE"
|
echo The file is named "$MYFILE"
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
^Word splitting after substitution (quoted!):^^^^^^
|
^Word splitting after substitution (quoted!):^^^^^^
|
||||||
^Word 1^Word 2^Word 3^Word 4^Word 5^Word 6^
|
^Word 1^Word 2^Word 3^Word 4^Word 5^Word 6^
|
||||||
|
@ -7,4 +7,4 @@ Responsible for site content and design:
|
|||||||
''Jan Schampera\\ Erich-Schott-Str. 2\\ 95666 Mitterteich\\ Federal Republic of Germany''
|
''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>
|
<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>
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
**NOTE:** Due to neverending SPAM from various IPs (the usual pharmacy-links and whatnot nonsens) this page was made readonly.
|
**NOTE:** Due to neverending SPAM from various IPs (the usual pharmacy-links and whatnot nonsens) this page was made readonly.
|
||||||
|
|
||||||
Another way of submitting wishes is the following form:
|
Another way of submitting wishes is the following form:
|
||||||
{{contact>subj=Wiki: wishlist}}
|
{{contact>subj=Wiki: wishlist}}
|
||||||
|
|
||||||
|
|
||||||
===== Token Recognition and Shell Grammar =====
|
===== Token Recognition and Shell Grammar =====
|
||||||
@ -15,7 +15,7 @@ a more human-readable version of the following topics:
|
|||||||
|
|
||||||
===== General =====
|
===== General =====
|
||||||
A simplified version of `man bash' and interesting bash tricks.:-)
|
A simplified version of `man bash' and interesting bash tricks.:-)
|
||||||
> The whole site is meant to be like that: a simplified or better human-readable collection of manpage snipplets. Maybe a page with the main topics of bash manpage that link to the proper articles would be good (I just don't have enough articles heh) - a kind of summary page --- Jan
|
> The whole site is meant to be like that: a simplified or better human-readable collection of manpage snipplets. Maybe a page with the main topics of bash manpage that link to the proper articles would be good (I just don't have enough articles heh) - a kind of summary page --- Jan
|
||||||
|
|
||||||
|
|
||||||
===== Some ideas =====
|
===== Some ideas =====
|
||||||
@ -23,7 +23,7 @@ A simplified version of `man bash' and interesting bash tricks.:-)
|
|||||||
**hmm maybe some of this stuff could make it somewhere to the wiki...:)**
|
**hmm maybe some of this stuff could make it somewhere to the wiki...:)**
|
||||||
|
|
||||||
* an article about how to do ksh coprocess in bash (:V4: may be obsolete regarding [[bash4 | Bash 4 features]])
|
* an article about how to do ksh coprocess in bash (:V4: may be obsolete regarding [[bash4 | Bash 4 features]])
|
||||||
- single way -> process substitution
|
- single way -> process substitution
|
||||||
- 2 way name pipes
|
- 2 way name pipes
|
||||||
* let's write a interactive tutorial script that teaches readline/bash (like C-h t in emacs, or vimtutor or vilearn)
|
* let's write a interactive tutorial script that teaches readline/bash (like C-h t in emacs, or vimtutor or vilearn)
|
||||||
* programmable history: http://pastebin.bash-hackers.org/index.php?show=22
|
* programmable history: http://pastebin.bash-hackers.org/index.php?show=22
|
||||||
@ -35,7 +35,7 @@ A simplified version of `man bash' and interesting bash tricks.:-)
|
|||||||
From this solution on modifying the internal Field Separator (IFS) in bash I found here:
|
From this solution on modifying the internal Field Separator (IFS) in bash I found here:
|
||||||
* http://linux.derkeiler.com/Mailing-Lists/Debian/2006-08/msg00462.html
|
* http://linux.derkeiler.com/Mailing-Lists/Debian/2006-08/msg00462.html
|
||||||
|
|
||||||
> added to [[syntax:ccmd:classic_for|the for-loop-article]] as example, but also made some critical comments; thanks --- Jan
|
> added to [[syntax:ccmd:classic_for|the for-loop-article]] as example, but also made some critical comments; thanks --- Jan
|
||||||
|
|
||||||
==== Getopts tutorial ====
|
==== Getopts tutorial ====
|
||||||
* My school offers a UNIX programming course (C, shell, etc.). I use this site extensively to help my friends taking this course learn concepts such as I/O redirection etc. As it gets more advanced they need to do option parsing. Therefore I would like to see a "getopts illustrated" article or some such, showing the breakdown of how it parses parameters.
|
* My school offers a UNIX programming course (C, shell, etc.). I use this site extensively to help my friends taking this course learn concepts such as I/O redirection etc. As it gets more advanced they need to do option parsing. Therefore I would like to see a "getopts illustrated" article or some such, showing the breakdown of how it parses parameters.
|
||||||
|
Loading…
Reference in New Issue
Block a user