mirror of
https://github.com/flokoe/bash-hackers-wiki.git
synced 2024-11-01 06:53:05 +01:00
Add original and unmodified source files
This commit is contained in:
parent
4a5fd7b8bc
commit
bd317db54d
327
original_source/_media/howto/smallcaps.txt
Normal file
327
original_source/_media/howto/smallcaps.txt
Normal file
File diff suppressed because one or more lines are too long
327
original_source/alnum/start.txt
Normal file
327
original_source/alnum/start.txt
Normal file
File diff suppressed because one or more lines are too long
327
original_source/alpha/punct/start.txt
Normal file
327
original_source/alpha/punct/start.txt
Normal file
File diff suppressed because one or more lines are too long
327
original_source/alpha/start.txt
Normal file
327
original_source/alpha/start.txt
Normal file
File diff suppressed because one or more lines are too long
196
original_source/bash4.txt
Normal file
196
original_source/bash4.txt
Normal file
@ -0,0 +1,196 @@
|
||||
====== Bash 4 - a rough overview ======
|
||||
|
||||
:V4:
|
||||
|
||||
<note>Attention: Since Bash 4 has been around for quite some time now (4.3 will come soon), I consider it to be "standard". This page is not maintained anymore and is left here to keep your links working. See the [[scripting:bashchanges]] page for new stuff introduced.</note>
|
||||
|
||||
Besides many bugfixes since Bash 3.2, Bash 4 will bring some interesting new
|
||||
features for shell users and scripters. See also [[scripting:bashchanges]] for a small general overview with more details.
|
||||
|
||||
Not all of the changes and news are included here, just the biggest or
|
||||
most interesting ones. The changes to completion, and the readline component
|
||||
are not covered. **Though, if you're familiar with these parts of Bash (and Bash 4),
|
||||
feel free to write a chapter here.**
|
||||
|
||||
The complete list of fixes and changes is in the CHANGES or NEWS file of your
|
||||
Bash 4 distribution.
|
||||
|
||||
<WRAP center round download 60%>
|
||||
The current available **stable** version is 4.4.18 release (February 03, 2018):
|
||||
* ftp://ftp.cwru.edu/pub/bash/bash-4.4.18.tar.gz
|
||||
* ftp://ftp.gnu.org/pub/gnu/bash/bash-4.4.18.tar.gz
|
||||
</WRAP>
|
||||
|
||||
|
||||
|
||||
===== New or changed commands and keywords =====
|
||||
|
||||
==== The new "coproc" keyword ====
|
||||
|
||||
Bash 4 introduces the concepts of coprocesses, a well known feature of other
|
||||
shells. The basic concept is simple: It will start any command in the background
|
||||
and set up an array that is populated with accessible files that represent the
|
||||
filedescriptors of the started process.
|
||||
|
||||
In other words: It lets you start a process in background and communicate with
|
||||
its input and output data streams.
|
||||
|
||||
See [[syntax:keywords:coproc | The coproc keyword]]
|
||||
|
||||
==== The new "mapfile" builtin ====
|
||||
|
||||
The ''mapfile'' builtin is able to map the lines of a file directly into an
|
||||
array. This avoids having to fill an array yourself using a loop. It enables you to define
|
||||
the range of lines to read, and optionally call a callback, for example to
|
||||
display a progress bar.
|
||||
|
||||
See: [[commands:builtin:mapfile]]
|
||||
|
||||
==== Changes to the "case" keyword ====
|
||||
|
||||
The ''case'' construct understands two new action list terminators:
|
||||
|
||||
The '';&'' terminator causes execution to continue with the next action list
|
||||
(rather than terminate the ''case'' construct).
|
||||
|
||||
The '';;&'' terminator causes the ''case'' construct to test the next given
|
||||
pattern instead of terminating the whole execution.
|
||||
|
||||
See [[syntax:ccmd:case]]
|
||||
|
||||
==== Changes to the "declare" builtin ====
|
||||
|
||||
The ''-p'' option now prints all attributes and values of declared variables (or
|
||||
functions, when used with ''-f''). The output is fully re-usable as input.
|
||||
|
||||
The new option ''-l'' declares a variable in a way that the content is
|
||||
converted to lowercase on assignment. For uppercase, the same applies to
|
||||
''-u''. The option ''-c'' causes the content to be capitalized before
|
||||
assignment.
|
||||
|
||||
''declare -A'' declares associative arrays (see below).
|
||||
|
||||
==== Changes to the "read" builtin ====
|
||||
|
||||
The ''read'' builtin command has some interesting new features.
|
||||
|
||||
The ''-t'' option to specify a timeout value has been slightly tuned. It now
|
||||
accepts fractional values and the special value 0 (zero). When ''-t 0'' is
|
||||
specified, ''read'' immediately returns with an exit status indicating if
|
||||
there's data waiting or not. However, when a timeout is given, and the ''read''
|
||||
builtin times out, any partial data recieved up to the timeout is stored in the
|
||||
given variable, rather than lost. When a timeout is hit, ''read'' exits with a
|
||||
code greater than 128.
|
||||
|
||||
A new option, ''-i'', was introduced to be able to preload the input buffer with
|
||||
some text (when Readline is used, with ''-e''). The user is able to change the
|
||||
text, or press return to accept it.
|
||||
|
||||
See [[commands:builtin:read]]
|
||||
|
||||
==== Changes to the "help" builtin ====
|
||||
|
||||
The builtin itself didn't change much, but the data displayed is more structured
|
||||
now. The help texts are in a better format, much easier to read.
|
||||
|
||||
There are two new options: ''-d'' displays the summary of a help text, ''-m''
|
||||
displays a manpage-like format.
|
||||
|
||||
==== Changes to the "ulimit" builtin ====
|
||||
|
||||
Besides the use of the 512 bytes blocksize everywhere in POSIX mode, ''ulimit''
|
||||
supports two new limits: ''-b'' for max socket buffer size and ''-T'' for max
|
||||
number of threads.
|
||||
|
||||
===== Expansions =====
|
||||
|
||||
==== Brace Expansion ====
|
||||
|
||||
The brace expansion was tuned to provide expansion results with leading zeros
|
||||
when requesting a row of numbers.
|
||||
|
||||
See [[syntax:expansion:brace]]
|
||||
|
||||
==== Parameter Expansion ===
|
||||
|
||||
Methods to modify the case on expansion time have been added.
|
||||
|
||||
On expansion time you can modify the syntax by adding operators to the parameter
|
||||
name.
|
||||
|
||||
See [[syntax:pe#case_modification | Case modification on parameter expansion]]
|
||||
|
||||
==== Substring expansion ====
|
||||
When using substring expansion on the positional parameters, a starting
|
||||
index of 0 now causes $0 to be prepended to the list (if the positional parameters are used). Before, this expansion
|
||||
started with $1:
|
||||
<code>
|
||||
# this should display $0 on Bash v4, $1 on Bash v3
|
||||
echo ${@:0:1}
|
||||
</code>
|
||||
|
||||
==== Globbing ====
|
||||
|
||||
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
|
||||
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
|
||||
names during globbing.
|
||||
|
||||
See [[syntax:expansion:globs]]
|
||||
|
||||
===== Associative Arrays =====
|
||||
|
||||
Besides the classic method of integer indexed arrays, Bash 4 supports associative arrays.
|
||||
|
||||
An associative array is an array indexed by an arbitrary string, something like
|
||||
<code>
|
||||
declare -A ASSOC
|
||||
|
||||
ASSOC[First]="first element"
|
||||
ASSOC[Hello]="second element"
|
||||
ASSOC[Peter Pan]="A weird guy"
|
||||
</code>
|
||||
|
||||
See [[syntax:arrays]]
|
||||
|
||||
===== Redirection =====
|
||||
|
||||
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>''
|
||||
notation.
|
||||
|
||||
The parser now understands ''|&'' as a synonym for ''2>&1 |'', which redirects
|
||||
the standard error for a command through a pipe.
|
||||
|
||||
See [[syntax:redirection]]
|
||||
|
||||
===== Interesting new shell variables =====
|
||||
^Variable ^Description ^
|
||||
|[[syntax:shellvars#BASHPID|BASHPID]] | contains the PID of the current shell (this is different than what ''$$'' does!) |
|
||||
|[[syntax:shellvars#PROMPT_DIRTRIM|PROMPT_DIRTRIM]] | specifies the max. level of unshortened pathname elements in the prompt |
|
||||
|[[syntax:shellvars#FUNCNEST|FUNCNEST]] | control the maximum number of shell function recursions |
|
||||
|
||||
See [[syntax:shellvars]]
|
||||
|
||||
===== Interesting new Shell Options =====
|
||||
|
||||
The mentioned shell options are **off by default** unless otherwise mentioned.
|
||||
|
||||
^Option ^Description ^
|
||||
|''[[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#dirspell | dirspell]]'' | enables spelling corrections on directory names during globbing |
|
||||
|''[[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 |
|
||||
|
||||
|
||||
See [[internals:shell_options]]
|
||||
|
||||
===== Misc =====
|
||||
|
||||
* If a command is not found, the shell attempts to execute a shell function named ''command_not_found_handle'', supplying the command words as the function arguments. This can be used to display userfriendly messages or perform different command searches
|
||||
* The behaviour of the ''set -e'' (''errexit'') mode was changed, it now acts more intuitive (and is better documented in the manpage).
|
||||
* The output target for the ''xtrace'' (''set -x''/''set +x'') feature is configurable **since Bash 4.1** (previously, it was fixed to ''stderr''): a variable named [[syntax:shellvars#BASH_XTRACEFD|BASH_XTRACEFD]] can be set to the filedescriptor that should get the output
|
||||
* Bash 4.1 is able to log the history to syslog (only to be enabled at compile time in ''config-top.h'')
|
327
original_source/commands/builtin/alias.txt
Normal file
327
original_source/commands/builtin/alias.txt
Normal file
File diff suppressed because one or more lines are too long
58
original_source/commands/builtin/caller.txt
Normal file
58
original_source/commands/builtin/caller.txt
Normal file
@ -0,0 +1,58 @@
|
||||
====== The caller builtin command ======
|
||||
|
||||
===== Synopsis =====
|
||||
<code>caller [FRAMENUMBER]</code>
|
||||
|
||||
===== Description =====
|
||||
The ''caller'' builtin command is used to print execution frames of subroutine calls. Without giving a framenumber, the topmost execution frame information is printed ("who called me") wile linenumber and filename.
|
||||
|
||||
When an execution frame number is given (0 - topmost), the linenumber, the subroutine (function) and the filename is printed. When an invalid execution frame number is given, it exists ''FALSE''. This way it can be used in a loop (see the examples section below).
|
||||
|
||||
===== Examples =====
|
||||
|
||||
==== Simple stack trace ====
|
||||
|
||||
The code below defines a function ''die'' that is used to exit the program. It prints a list of execution frames, starting with the topmost frame (0). The topmost frame is the "caller of the die function", in this case function "f1".
|
||||
|
||||
This way, you can print a "stack trace" for debugging or logging purposes.
|
||||
|
||||
The code is made very simple, just to show the basic purposes.
|
||||
|
||||
<code bash>
|
||||
#!/bin/bash
|
||||
|
||||
die() {
|
||||
local frame=0
|
||||
while caller $frame; do
|
||||
((++frame));
|
||||
done
|
||||
echo "$*"
|
||||
exit 1
|
||||
}
|
||||
|
||||
f1() { die "*** an error occured ***"; }
|
||||
f2() { f1; }
|
||||
f3() { f2; }
|
||||
|
||||
f3
|
||||
</code>
|
||||
|
||||
**Output**
|
||||
<code>
|
||||
12 f1 ./callertest.sh
|
||||
13 f2 ./callertest.sh
|
||||
14 f3 ./callertest.sh
|
||||
16 main ./callertest.sh
|
||||
*** an error occured ***
|
||||
</code>
|
||||
|
||||
===== 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>
|
||||
* 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.
|
||||
|
||||
===== Portability considerations =====
|
||||
* ''caller'' is not specified by POSIX(R)
|
||||
* the ''caller'' builtin command appeared in Bash version 3.0
|
||||
|
||||
===== See also =====
|
46
original_source/commands/builtin/cd.txt
Normal file
46
original_source/commands/builtin/cd.txt
Normal file
@ -0,0 +1,46 @@
|
||||
====== The cd builtin command ======
|
||||
|
||||
===== Synopsis =====
|
||||
<code>cd [-L|-P] [DIRECTORY]</code>
|
||||
<code>cd -</code>
|
||||
|
||||
===== Description =====
|
||||
The ''cd'' builtin command is used to change the current working directory
|
||||
* to the given directory (''cd DIRECTORY'')
|
||||
* to the previous working directory (''cd -'') as saved in the [[syntax:shellvars#OLDPWD|OLDPWD]] shell variable
|
||||
* to the user's home directory as specified in the [[syntax:shellvars#HOME|HOME]] environment variable (when used without a ''DIRECTORY'' argument)
|
||||
|
||||
The ''cd'' builtin command searches the directories listed in [[syntax:shellvars#CDPATH|CDPATH]] for a matching directory.
|
||||
|
||||
The default behaviour is to follow symbolic links unless the ''-P'' option is given or the shell is configured to do so (see the ''-P'' option of [[commands:builtin:set|the set builtin command]]).
|
||||
|
||||
==== Options ====
|
||||
|
||||
^Option^Description^
|
||||
|''-L'' |Follow symbolic links (default) |
|
||||
|''-P'' |Do not follow symbolic links |
|
||||
|''-@'' |Browse a file's extended attributed, if supported |
|
||||
|
||||
==== Exit status ====
|
||||
|
||||
* true if the directory was changed successfully
|
||||
* false if a change to the home directory was requested, but [[syntax:shellvars#HOME|HOME]] is unset
|
||||
* false if anything else goes wrong
|
||||
|
||||
===== Examples =====
|
||||
|
||||
==== Change the working directory to the user's home directory ====
|
||||
|
||||
<code>cd</code>
|
||||
|
||||
==== Change the working directory to the previous directory ====
|
||||
|
||||
<code>cd -</code>
|
||||
|
||||
===== Portability considerations =====
|
||||
|
||||
===== See also =====
|
||||
* variable [[syntax:shellvars#CDPATH|CDPATH]]
|
||||
* variable [[syntax:shellvars#HOME|HOME]]
|
||||
* variable [[syntax:shellvars#OLDPWD|OLDPWD]]
|
||||
* the ''-P'' option of [[commands:builtin:set|the set builtin command]]
|
327
original_source/commands/builtin/command.txt
Normal file
327
original_source/commands/builtin/command.txt
Normal file
File diff suppressed because one or more lines are too long
327
original_source/commands/builtin/continuebreak.txt
Normal file
327
original_source/commands/builtin/continuebreak.txt
Normal file
File diff suppressed because one or more lines are too long
137
original_source/commands/builtin/declare.txt
Normal file
137
original_source/commands/builtin/declare.txt
Normal file
@ -0,0 +1,137 @@
|
||||
====== The declare builtin command ======
|
||||
|
||||
===== Synopsis =====
|
||||
<code>
|
||||
declare [-aAfFgilnrtux] [-p] [NAME[=VALUE] ...]
|
||||
|
||||
# obsolete typeset synonym
|
||||
typeset [-aAfFgilnrtux] [-p] [NAME[=VALUE] ...]
|
||||
</code>
|
||||
|
||||
===== Description =====
|
||||
|
||||
''declare'' is used to display or set variables along with variable attributes. When used to display variables/functions and their value, the output is re-usable as input for the shell.
|
||||
|
||||
If no ''NAME'' is given, it displays the values of all variables or functions when restricted by the ''-f'' option.
|
||||
|
||||
If ''NAME'' is followed by ''=VALUE'', ''declare'' also sets the value for a variable.
|
||||
|
||||
When used in a function, ''declare'' makes ''NAMEs'' local variables, unless used with the ''-g'' option.
|
||||
|
||||
Don't use it's synonym ''typeset'' when coding for Bash, since it's tagged as obsolete.
|
||||
|
||||
==== Options ====
|
||||
|
||||
Below, ''[-+]X'' indicates an attribute, use ''-X'' to set the attribute, ''+X'' to remove it.
|
||||
|
||||
^Option ^Description ^
|
||||
|''[-+]a'' |make NAMEs indexed arrays (removing with ''+a'' is valid syntax, but leads to an error message) |
|
||||
|''[-+]A'' |make NAMEs associative arrays |
|
||||
|''[-+]c'' | **Undocumented** convert NAMEs to "capcase" on assignment (makes the first letter upper-case and the rest lower). Requires Bash built with ''-DCASEMOD_CAPCASE'' |
|
||||
|''-f'' |restrict action or display to function names and definitions (removing with ''+f'' is valid syntax, but leads to an error message) |
|
||||
|''-F'' |restrict display to function names only (plus line number and source file when debugging) |
|
||||
|''-g'' |create global variables when used in a shell function; otherwise ignored (by default, ''declare'' declares local scope variables when used in shell functions) |
|
||||
|''[-+]i'' |make NAMEs have the "integer" attribute |
|
||||
|''[-+]l'' |convert NAMEs to lower case on assignment (makes sure the variable contains only lower case letters) |
|
||||
|''[-+]n'' |make NAME a reference to the variable named by its value. Introduced in Bash 4.3-alpha.\\ '' ${!NAME}'' reveals the reference variable name, VALUE.\\ Use ''unset -n NAME'' to unset the variable. (''unset -v NAME'' unsets the VALUE variable.)\\ Use ''%%[[%% -R NAME %%]]%%'' to test if NAME has been set to a VALUE, another variable's name.|
|
||||
|''-p'' |display the attributes and value of each NAME |
|
||||
|''[-+]r'' |make NAMEs readonly (removing with ''+r'' is valid syntax, but not possible) |
|
||||
|''[-+]t'' |make NAMEs have the "trace" attribute (effective only for functions) |
|
||||
|''[-+]u'' |convert NAMEs to upper case on assignment (makes sure the variable contains only upper case letters) |
|
||||
|''[-+]x'' |make NAMEs exported |
|
||||
|
||||
==== Return status ====
|
||||
|
||||
^Status ^Reason ^
|
||||
|0 |no error |
|
||||
|!= 0 |invalid option |
|
||||
|!= 0 |invalid variable name given |
|
||||
|!= 0 |attempt to **define** a function using ''-f'' |
|
||||
|!= 0 |assignment to a readonly variable |
|
||||
|!= 0 |removing the readonly-attribute from a readonly variable |
|
||||
|!= 0 |assignment to an array variable without the compound assignment syntax (''array=(...)'') |
|
||||
|!= 0 |attempt to use ''+a'' to "destroy" an array |
|
||||
|!= 0 |attemt to display a non-existent function with ''-f'' |
|
||||
|
||||
===== Notes =====
|
||||
|
||||
Unix shells offer very few datatypes. Bash and some other shells extend this by allowing "attributes" to be set on variable names. The only attributes specified by POSIX are ''export'' and ''readonly'', which are set by their own dedicated builtins. Datatypes in bash have a few other interesting capabilities such as the ability to modify data on assignment.
|
||||
|
||||
===== Examples =====
|
||||
|
||||
==== Display defined functions ====
|
||||
''declare -f'' can be used to display all defined functions...
|
||||
<code>
|
||||
$ declare -f
|
||||
foo ()
|
||||
{
|
||||
echo "FOO is BAR"
|
||||
}
|
||||
world ()
|
||||
{
|
||||
echo "Hello World!"
|
||||
}
|
||||
</code>
|
||||
...or just a specific defined function.
|
||||
<code>
|
||||
$ declare -f foo
|
||||
foo ()
|
||||
{
|
||||
echo "FOO is BAR"
|
||||
}
|
||||
</code>
|
||||
|
||||
==== Nameref ====
|
||||
|
||||
Bash 4.3 adds a new way to indirectly reference variables. ''typeset -n'' or ''declare -n'' can be used to make a variable indirectly refer to another. In Bash, the lvalue of the assignment given to ''typeset -n'' or ''declare -n'' will refer to the variable whose name is expanded on the RHS.
|
||||
|
||||
''typeset -n'' is used in the example below. See notes below.
|
||||
|
||||
<code>
|
||||
# Sum a set of arrays and assign the result indirectly, also printing each intermediary result (without portability workarounds)
|
||||
# sum name arrname [ arrname ... ]
|
||||
function sum {
|
||||
typeset -n _result=$1 _arr
|
||||
typeset IFS=+
|
||||
_result=0
|
||||
for _arr in "${@:2}"; do # Demonstrate the special property of "for" on a nameref.
|
||||
(( _result += ${_arr[*]} ))
|
||||
printf '%s = %d\n' "${!_result}" "$_result" # Demonstrate the special property of ${!ref} on a nameref.
|
||||
done
|
||||
}
|
||||
|
||||
a=(1 2 3) b=(6 5 4) c=(2 4 6)
|
||||
sum total a b c
|
||||
printf 'Final value of "total" is: %d\n' "$total"
|
||||
</code>
|
||||
<div hide>
|
||||
function sum {
|
||||
typeset -n _result=$1
|
||||
shift
|
||||
typeset IFS=+ _arrx
|
||||
_result=0
|
||||
for _arrx in "$@"; do # Demonstrate the special property of "for" on a nameref.
|
||||
typeset -n _arr=$_arrx
|
||||
(( _result += ${_arr[*]} ))
|
||||
printf '%s = %d\n' "${!_result}" "$_result" # Demonstrate the special property of ${!ref} on a nameref.
|
||||
done
|
||||
}
|
||||
|
||||
a=(1 2 3); b=(6 5 4); c=(2 4 6)
|
||||
sum total a b c
|
||||
printf 'Final value of "total" is: %d\n' "$total"
|
||||
</div>
|
||||
|
||||
''typeset -n'' is currently implemented in ksh93, mksh, and Bash 4.3. Bash and mksh's implementations are quite similar, but much different from ksh93's. See [[#portability_considerations | Portability considerations]] for details. ksh93 namerefs are much more powerful than Bash's.
|
||||
|
||||
===== Portability considerations =====
|
||||
|
||||
* ''declare'' is not specified by POSIX(r)
|
||||
* ''declare'' is unique to Bash and totally non-portable with the possible exception of Zsh in Bash compatibility mode. Bash marks the synonym ''typeset'' as obsolete, which in Bash behaves identically to ''declare''. All other Korn-like shells use ''typeset'', so it probably isn't going away any time soon. Unfortunately, being a non-standard builtin, ''typeset'' differs significantly between shells. ksh93 also considers ''typeset'' a special builtin, while Bash does not - even in POSIX mode. If you use ''typeset'', you should attempt to only use it in portable ways.
|
||||
* **todo** nameref portability...
|
||||
|
||||
===== See also =====
|
||||
* [[syntax:arrays]]
|
||||
* [[commands:builtin:readonly]]
|
||||
* [[commands:builtin:unset]]
|
||||
* [[http://austingroupbugs.net/view.php?id=351 | declaration commands]] will change the behavior of certain builtins such as ''export'' in the next version of POSIX.
|
48
original_source/commands/builtin/echo.txt
Normal file
48
original_source/commands/builtin/echo.txt
Normal file
@ -0,0 +1,48 @@
|
||||
====== The echo builtin command ======
|
||||
|
||||
===== Synopsis =====
|
||||
<code>
|
||||
echo [-neE] [arg ...]
|
||||
</code>
|
||||
|
||||
===== 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.
|
||||
|
||||
==== Options ====
|
||||
^Option ^Description ^
|
||||
| ''-n'' | The trailing newline is suppressed. |
|
||||
| ''-e'' | Interpretation of the following backslash-escaped characters (below) is enabled. |
|
||||
| ''-E'' | Disables the interpretation of these escape characters, even on systems where they are interpreted by default. |
|
||||
|
||||
==== Escape sequences ====
|
||||
^Escape ^Description ^
|
||||
| ''\a'' | alert (bell) |
|
||||
| ''\b'' | backspace |
|
||||
| ''\c'' | suppress further output |
|
||||
| ''\e'' | |
|
||||
| ''\E'' | an escape character |
|
||||
| ''\f'' | form feed |
|
||||
| ''\n'' | new line |
|
||||
| ''\r'' | carriage return |
|
||||
| ''\t'' | horizontal tab |
|
||||
| ''\v'' | vertical tab |
|
||||
| ''\\'' | backslash |
|
||||
| ''\0nnn'' | the eight-bit character whose value is the octal value nnn (zero to three octal digits) |
|
||||
| ''\xHH'' | the eight-bit character whose value is the hexadecimal value HH (one or two hex digits) |
|
||||
| ''\uHHHH'' | the Unicode (ISO/IEC 10646) character whose value is the hexadecimal value HHHH (one to four hex digits) |
|
||||
| ''\UHHHHHHHH'' | the Unicode (ISO/IEC 10646) character whose value is the hexadecimal value HHHHHHHH (one to eight hex digits) |
|
||||
|
||||
===== Examples =====
|
||||
|
||||
|
||||
===== Portability considerations =====
|
||||
* ''echo'' is a portability train wreck. No major shell follows POSIX completely, and any shell that attempts to do so should be considered horribly broken. [[http://pubs.opengroup.org/onlinepubs/9699919799/utilities/echo.html#tag_20_37|SUSv4]] specifies that ''echo'' **shall not** include any options. Further, it specifies that the behavior of ''-n'' as a first argument shall be determined by the implementation, unless XSI is followed, in which case ''-n'' is always treated as a string, and backslash escapes are interpreted by default. ''dash'' has the misfeature of following this and interpreting escapes by default, but includes a ''-n'' feature for suppressing newlines nevertheless.\\ \\ In practice, if you're able to assume a korn-like shell including bash, mksh, or zsh, ''echo'' when used in simple cases is generally reliable. For example, in the very common situation in which echo is supplied with a single argument and whose output is to have a newline appended, using ''echo'' is considered common practice.
|
||||
|
||||
* **Never use options to ''echo''! //Ever//!** Any time you feel tempted to use ''echo -e'', ''-n'', or any other special feature of echo, **use [[commands:builtin:printf|printf]] instead!** If portability is a requirement, you should consider using ''printf'' //exclusively// and just ignore that ''echo'' even exists. If you must use ''echo -e'' and refuse to use ''printf'', it is usually acceptable to use ''echo $'...' ''if targeting only shells that support this special quoting style.
|
||||
|
||||
* ''ksh93'' has a ''print'' command, which if coding specifically for ''ksh93'' should be preferred over ''echo''. [[commands:builtin:printf|printf]] still includes most of the functionality of both, and should usually be the most preferred option.
|
||||
|
||||
===== See also =====
|
||||
* [[commands:builtin:printf]]
|
||||
* http://cfajohnson.com/shell/cus-faq.html#Q0b
|
||||
* http://www.in-ulm.de/~mascheck/various/echo+printf/
|
113
original_source/commands/builtin/eval.txt
Normal file
113
original_source/commands/builtin/eval.txt
Normal file
@ -0,0 +1,113 @@
|
||||
====== The eval builtin command ======
|
||||
===== Synopsis =====
|
||||
<code>eval: eval [arg ...]</code>
|
||||
|
||||
===== Description =====
|
||||
|
||||
''eval'' takes its arguments, concatenates them separated by spaces, and executes the resulting string as Bash code in the current execution environment. ''eval'' in Bash works in essentially the same way as most other languages that have an ''eval'' function. Perhaps the easiest way to think about ''eval'' is that it works in the same way as running ''bash -c "bash code..." ''from a script, except in the case of ''eval'', the given code is executed in the current shell environment rather than a child process.
|
||||
|
||||
===== Examples =====
|
||||
In this example, the literal text within the [[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>
|
||||
#!/usr/bin/env bash
|
||||
{ myCode=$(</dev/stdin); } <<\EOF
|
||||
... arbitrary bash code here ...
|
||||
EOF
|
||||
|
||||
eval "$myCode"
|
||||
</code>
|
||||
|
||||
==== Expansion side-effects ====
|
||||
Frequently, ''eval'' is used to cause side-effects by performing a pass of expansion on the code before executing the resulting string. This allows for things that otherwise wouldn't be possible with ordinary Bash syntax. This also, of course, makes ''eval'' the most powerful command in all of shell programming (and in most other languages for that matter).
|
||||
|
||||
This code defines a set of identical functions using the supplied names. ''eval'' is the only way to achieve this effect.
|
||||
|
||||
<code>
|
||||
main() {
|
||||
local fun='() { echo "$FUNCNAME"; }' x
|
||||
|
||||
for x in {f..n}; do
|
||||
eval "${x}${fun}"
|
||||
done
|
||||
|
||||
"$@"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
</code>
|
||||
|
||||
==== 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.
|
||||
|
||||
<code>
|
||||
while (( ++n <= 5 )) || ! evalBall="eval $evalBall"; do
|
||||
printf -v evalBall 'eval %q' "printf $n;${evalBall-printf '0\n'}"
|
||||
done
|
||||
$evalBall
|
||||
</code>
|
||||
|
||||
The above example is mostly fun and games but illustrates the ''printf %q'' property.
|
||||
|
||||
==== Higher-order functions ====
|
||||
Since all current POSIX-compatible shells lack support for [[http://en.wikipedia.org/wiki/First-class_function | first-class functions]], it can be tempting and sometimes useful to simulate some of their effect using ''eval'' to evaluate a string containing code.
|
||||
|
||||
This example shows [[http://en.wikipedia.org/wiki/Partial_application | partial application]] using ''eval''.
|
||||
|
||||
<code>
|
||||
function partial {
|
||||
eval shift 2 \; function "$1" \{ "$2" "$(printf '%q ' "${@:3}")" '"$@"; }'
|
||||
}
|
||||
|
||||
function repeat {
|
||||
[[ $1 == +([0-9]) ]] || return
|
||||
typeset n
|
||||
while ((n++ < $1)); do
|
||||
"${@:2}"
|
||||
done
|
||||
}
|
||||
|
||||
partial print3 repeat 3 printf '%s ' # Create a new function named print3
|
||||
print3 hi # Print "hi" 3 times
|
||||
echo
|
||||
</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.
|
||||
|
||||
===== 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.
|
||||
|
||||
* ''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>
|
||||
$ ( eval a=( a b\\ c d ); printf '<%s> ' "${a[@]}"; echo ) # Only works in Bash.
|
||||
<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.
|
||||
-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.
|
||||
<a> <b c> <d>
|
||||
</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.
|
||||
|
||||
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>
|
||||
$ ( set -x; touch 'x+=(\[[123]\]=*)' 'x+=([3]=yo)'; eval x+=(*); echo "${x[@]}" )
|
||||
+ touch 'x+=(\[[123]\]=*)' 'x+=([3]=yo)'
|
||||
+ eval 'x+=(\[[123]\]=*)' 'x+=([3]=yo)'
|
||||
++ x+=(\[[123]\]=*)
|
||||
++ x+=([3]=yo)
|
||||
+ echo '[[123]]=*' yo
|
||||
[[123]]=* yo
|
||||
</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]].
|
||||
|
||||
===== See also =====
|
||||
* [[http://mywiki.wooledge.org/BashFAQ/048|BashFAQ 48 - eval and security issues]] -- **IMPORTANT**
|
||||
* [[http://fvue.nl/wiki/Bash:_Why_use_eval_with_variable_expansion%3F|Another eval article]]
|
||||
* [[http://mywiki.wooledge.org/BashFAQ/006#Assigning_indirect.2BAC8-reference_variables|Indirection via eval]]
|
||||
* [[http://fvue.nl/wiki/Bash:_Passing_variables_by_reference|More indirection via eval]]
|
||||
* [[https://github.com/vaeth/push|Martin Väth's "push"]] -- ''printf %q'' work-alike for POSIX.
|
||||
* [[http://www.chiark.greenend.org.uk/~sgtatham/aliases.html|The "magic alias" hack]]
|
72
original_source/commands/builtin/exec.txt
Normal file
72
original_source/commands/builtin/exec.txt
Normal file
@ -0,0 +1,72 @@
|
||||
====== The exec builtin command ======
|
||||
|
||||
===== Synopsis =====
|
||||
<code>exec [-a NAME] [-cl] [COMMAND] [ARG...] [REDIRECTION...]</code>
|
||||
|
||||
===== Description =====
|
||||
The ''exec'' builtin command is used to
|
||||
* **replace** the shell with a given program (executing it, **not as new process**)
|
||||
* set redirections for the program to execute or for the current shell
|
||||
|
||||
If only redirections are given, the redirections affect the current shell without executing any program.
|
||||
|
||||
==== Options ====
|
||||
|
||||
^Option^Description^
|
||||
|''-a NAME'' |Passes ''NAME'' as zeroth argument for the program to be executed |
|
||||
|''-c'' |Execute the program with an empty (cleared) environment |
|
||||
|''-l'' |Prepends a dash (''-'') to the zeroth argument of the program to be executed, similar to what the ''login'' program does |
|
||||
|
||||
==== Exit status ====
|
||||
|
||||
* on redirection errors it returns 1, otherwise 0
|
||||
* on exec failures:
|
||||
* a non-interactive shell terminates; if the [[internals:shell_options#execfail | shell option execfail]] is set ''exec'' returns failure
|
||||
* in an interactive shell, ''exec'' returns failure
|
||||
|
||||
===== Examples =====
|
||||
|
||||
==== Wrapper around a program ====
|
||||
<code bash>
|
||||
myprog=/bin/ls
|
||||
echo "This is the wrapper script, it will exec $myprog"
|
||||
|
||||
# do some vodoo here, probably change the arguments etc.
|
||||
# well, stuff a wrapper is there for
|
||||
|
||||
exec "$myprog" "$@"
|
||||
</code>
|
||||
|
||||
==== Open a file as input for the script ====
|
||||
<code bash>
|
||||
# open it
|
||||
exec 3< input.txt
|
||||
|
||||
# for example: read one line from the file(-descriptor)
|
||||
read -u 3 LINE
|
||||
# or
|
||||
read LINE <&3
|
||||
|
||||
# finally, close it
|
||||
exec 3<&-
|
||||
</code>
|
||||
|
||||
==== 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:
|
||||
<code bash>
|
||||
exec >/var/adm/my.log 2>&1
|
||||
|
||||
# script continues here...
|
||||
</code>
|
||||
|
||||
===== Portability considerations =====
|
||||
|
||||
*POSIX(r) specifies error code ranges:
|
||||
* if ''exec'' can't find the program to execute, the error code shall be 126
|
||||
* on a redirection error, the error code shall be between 1 and 125
|
||||
* the ''-a NAME'' option appeared in Bash 4.2-alpha
|
||||
* POSIX(r) does **not** specify any options for ''exec'' (like ''-c'', ''-l'', ''-a NAME'').
|
||||
|
||||
===== See also =====
|
||||
* [[syntax:redirection]]
|
39
original_source/commands/builtin/exit.txt
Normal file
39
original_source/commands/builtin/exit.txt
Normal file
@ -0,0 +1,39 @@
|
||||
====== The exit builtin command ======
|
||||
|
||||
===== Synopsis =====
|
||||
<code>exit [N]</code>
|
||||
|
||||
===== Description =====
|
||||
The ''exit'' command terminates the current shell (or script).
|
||||
|
||||
If ''N'' is given, the return code to the parent process is set to ''N''. If not, the returned status the the status of the most recently executed command (i.e. ''$?'').
|
||||
|
||||
A [[commands:builtin:trap|trap]] on ''EXIT'' is executed before the shell exits, except the executed ''exit'' command is part of an already running trap.
|
||||
|
||||
==== Options ====
|
||||
There are no options.
|
||||
|
||||
|
||||
==== Exit status ====
|
||||
|
||||
Naturally, you can't ask for the exit status from within the shell that executed the ''exit'' command, because the shell exits.
|
||||
|
||||
^Status ^Reason ^
|
||||
|255 |invalid (e.g. non-numeric) argument - this staus is returned to the parent |
|
||||
|
||||
|
||||
===== Examples =====
|
||||
|
||||
==== Exit the shell and explicitely set its exit status ====
|
||||
<code>
|
||||
exit 3
|
||||
</code>
|
||||
|
||||
===== Portability considerations =====
|
||||
* if ''N'' is specified, but its value is not between 0 and 255 inclusively, the exit status is undefined.
|
||||
|
||||
|
||||
===== See also =====
|
||||
* [[commands:builtin:trap|The trap builtin command]]
|
||||
* [[dict:terms:exit_status|The exit status]]
|
||||
|
47
original_source/commands/builtin/export.txt
Normal file
47
original_source/commands/builtin/export.txt
Normal file
@ -0,0 +1,47 @@
|
||||
====== The export builtin command ======
|
||||
|
||||
===== Synopsis =====
|
||||
|
||||
<code>
|
||||
export [-fn] [NAME[=VALUE] ...]
|
||||
export -p
|
||||
</code>
|
||||
|
||||
===== Description =====
|
||||
|
||||
The ''export'' builtin command is used to mark variables or functions referenced by ''NAME'' for automatic export to the environment. If ''NAME'' is a shell variable, a value ''VALUE'' can be assigned before exporting it.
|
||||
|
||||
==== Options ====
|
||||
|
||||
^Option ^Description ^
|
||||
|''-f'' |refer to shell functions |
|
||||
|''-n'' |remove the export property from any referenced ''NAME'' |
|
||||
|''-p'' |print all exported variables, with ''-f'', print all exported functions - all in a format re-usable as input |
|
||||
|
||||
An argument of ''%%--%%'' disables further option processing.
|
||||
==== Return status ====
|
||||
|
||||
^Status ^Reason ^
|
||||
|0 |no error |
|
||||
|!=0 |invalid option |
|
||||
|!=0 |a given ''NAME'' is invalid |
|
||||
|
||||
===== Examples =====
|
||||
|
||||
Set the display to use when launching a GUI application (useful during SSH sessions):
|
||||
<code>
|
||||
export DISPLAY=":0"
|
||||
</code>
|
||||
|
||||
Set your default text editor (e.g. SublimeText):
|
||||
<code>
|
||||
export EDITOR=subl
|
||||
</code>
|
||||
===== Portability considerations =====
|
||||
|
||||
* in POSIX(r), only the ''-p'' option is specified
|
||||
* in POSIX(r), only variables (with value assignment) are to be exported, not shell functions
|
||||
|
||||
===== See also =====
|
||||
|
||||
* [[commands:builtin:declare]]
|
327
original_source/commands/builtin/false.txt
Normal file
327
original_source/commands/builtin/false.txt
Normal file
File diff suppressed because one or more lines are too long
327
original_source/commands/builtin/fc.txt
Normal file
327
original_source/commands/builtin/fc.txt
Normal file
File diff suppressed because one or more lines are too long
327
original_source/commands/builtin/getopts.txt
Normal file
327
original_source/commands/builtin/getopts.txt
Normal file
File diff suppressed because one or more lines are too long
327
original_source/commands/builtin/hash.txt
Normal file
327
original_source/commands/builtin/hash.txt
Normal file
File diff suppressed because one or more lines are too long
65
original_source/commands/builtin/kill.txt
Normal file
65
original_source/commands/builtin/kill.txt
Normal file
@ -0,0 +1,65 @@
|
||||
====== The kill builtin command ======
|
||||
|
||||
===== Synopsis =====
|
||||
<code>kill [-s SIGNAL | -n SIGNALNUMBER | -SIGNAL] PID|JOB</code>
|
||||
<code>kill -l|-L [SIGNAL...]</code>
|
||||
|
||||
===== Description =====
|
||||
The ''kill'' command is used to send signals to processes specified by their ''PID'' or their ''JOB''-specification.
|
||||
|
||||
The signal(s) to be specified can have the following formats:
|
||||
* Numerical: The signal is specified using its constant numeric value. Be aware that not all systems have identical numbers for the signals.
|
||||
* Symbolic (long): The signal is specified using the same name that is used for the constant/macro in the C API (''SIG<name>'')
|
||||
* Symbolic (short): The signal is specified using the name from the C API without the ''SIG''-prefix (''<name>'')
|
||||
|
||||
Without any specified signal, the command sends the ''SIGTERM''-signal.
|
||||
|
||||
The ''kill'' command is a Bash builtin command instead of relying on the external ''kill'' command of the operating system to
|
||||
* be able to use shell job specifications instead of Unix process IDs
|
||||
* be able to send signals ("kill something") also, when your process limit is reached
|
||||
|
||||
==== Options ====
|
||||
|
||||
^Option ^Description ^
|
||||
|''-s SIGNAL'' |specifies the signal to send |
|
||||
|''-n SIGNALNUMBER'' |specifies the signal to send |
|
||||
|''-SIGNAL'' |specifies the signal to send |
|
||||
|''-l [SIGNAL...]'' |Lists supported/known signal numbers and their symbolic name. If ''SIGNAL'' is given, only list this signal, translated (if a number is given the symbolic name is printed, and vice versa) |
|
||||
|''-L [SIGNAL...]'' |Same as ''-l [SIGNAL]'' (compatiblity option) |
|
||||
|
||||
==== Return status ====
|
||||
|
||||
^Status ^Reason ^
|
||||
|0 |no error/success |
|
||||
|!=0 |invalid option |
|
||||
|!=0 |invalid signal specification |
|
||||
|!=0 |error returned by the system function (e.g. insufficient permissions to send to a specific process) |
|
||||
|
||||
===== Examples =====
|
||||
|
||||
==== List supported signals ====
|
||||
|
||||
<code>
|
||||
kill -l
|
||||
</code>
|
||||
|
||||
==== Send KILL to a process ID ====
|
||||
|
||||
<code>
|
||||
kill -9 12345
|
||||
</code>
|
||||
|
||||
<code>
|
||||
kill -KILL 12345
|
||||
</code>
|
||||
|
||||
<code>
|
||||
kill -SIGKILL 12345
|
||||
</code>
|
||||
|
||||
===== Portability considerations =====
|
||||
|
||||
* POSIX(R) and ISO C only standardize symbolic signal names (no numbers) and a default action
|
||||
|
||||
===== See also =====
|
||||
|
66
original_source/commands/builtin/let.txt
Normal file
66
original_source/commands/builtin/let.txt
Normal file
@ -0,0 +1,66 @@
|
||||
====== The let builtin command ======
|
||||
|
||||
===== Synopsis =====
|
||||
|
||||
<code>
|
||||
let arg [arg ...]
|
||||
</code>
|
||||
|
||||
===== 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.
|
||||
* 0 (TRUE) when ''arg'' evaluated to not 0 (arithmetic "true")
|
||||
* 1 (FALSE) when ''arg'' evaluated to 0 (arithmetic "false")
|
||||
For this return code mapping, please see [[syntax:arith_expr#arithmetic_expressions_and_return_codes | this section]]. They work in the same way as ''<nowiki>((</nowiki>''.
|
||||
|
||||
===== 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''**.
|
||||
|
||||
<code>
|
||||
$ let 'b = a' "(a += 3) + $((a = 1)), b++"
|
||||
$ echo "$a - $b - $?"
|
||||
4 - 2 - 0
|
||||
</code>
|
||||
|
||||
Is equivalent to the [[syntax:ccmd:arithmetic_eval | arithmetic evaluation compound command]]:
|
||||
|
||||
<code>
|
||||
$ (( b = a, (a += 3) + $((a = 1)), b++ ))
|
||||
$ echo "$a - $b - $?"
|
||||
4 - 2 - 0
|
||||
</code>
|
||||
|
||||
<WRAP info>
|
||||
Remember that inside arithmetic evaluation contexts, all other expansions are processed as usual (from left-to-right), and the resulting text is evaluated as an arithmetic expression. Arithmetic already has a way to control precedence using parentheses, so it's very rare to need to nest arithmetic expansions within one another. It's used above only to illustrate how this precedence works.
|
||||
</WRAP>
|
||||
|
||||
Unlike ''<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>
|
||||
~ $ ( y=1+1 let x=y; declare -p x y )
|
||||
declare -- x="2"
|
||||
bash: declare: y: not found
|
||||
|
||||
~ $ ( y=1+1 let x=y++; declare -p x y )
|
||||
declare -- x="2"
|
||||
declare -- y="3"
|
||||
</code>
|
||||
|
||||
This can be useful in certain situations where a temporary variable is needed.
|
||||
|
||||
===== Portability considerations =====
|
||||
|
||||
* the ''let'' command is not specified by POSIX(r). The portable alternative is: <code>[ "$(( <EXPRESSION> ))" -ne 0 ]</code>. To make portable scripts simpler and cleaner, ''let'' can be defined as: <code>
|
||||
# POSIX
|
||||
let() {
|
||||
IFS=, command eval test '$(($*))' -ne 0
|
||||
}
|
||||
</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.
|
||||
* [[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.
|
||||
|
||||
===== See also =====
|
||||
|
||||
* Internal: [[syntax:expansion:arith | arithmetic expansion]]
|
||||
* Internal: [[syntax:arith_expr | arithmetic expressions]]
|
||||
* Internal: [[syntax:ccmd:arithmetic_eval | arithmetic evaluation compound command]]
|
25
original_source/commands/builtin/local.txt
Normal file
25
original_source/commands/builtin/local.txt
Normal file
@ -0,0 +1,25 @@
|
||||
====== The local builtin command ======
|
||||
|
||||
===== Synopsis =====
|
||||
<code>
|
||||
local [option] name[=value] ...
|
||||
</code>
|
||||
|
||||
===== Description =====
|
||||
|
||||
''local'' is identical to [[commands:builtin:declare|declare]] in every way, and takes all the same options, with 3 exceptions:
|
||||
* Usage outside of a function is an error. Both ''declare'' and ''local'' within a function have the same effect on variable scope, including the -g option.
|
||||
* ''local'' with no options prints variable names and values in the same format as ''declare'' with no options, except the variables are filtered to print only locals that were set in the same scope from which ''local'' was called. Variables in parent scopes are not printed.
|
||||
* If name is ‘-’, the set of shell options is made local to the function in which local is invoked: shell options changed using the set builtin inside the function are restored to their original values when the function returns. The restore is effected as if a series of set commands were executed to restore the values that were in place before the function.
|
||||
|
||||
===== Portability considerations =====
|
||||
|
||||
* ''local'' is not specified by POSIX. Most bourne-like shells don't have a builtin called ''local'', but some such as ''dash'' and the busybox shell do.
|
||||
|
||||
* The behavior of function scope is not defined by POSIX, however local variables are implemented widely by bourne-like shells, and behavior differs substantially. Even the''dash'' shell has local variables.
|
||||
|
||||
* In ksh93, using POSIX-style function definitions, ''typeset'' doesn't set ''local'' variables, but rather acts upon variables of the next-outermost scope (e.g. setting attributes). Using ''typeset'' within functions defined using ksh ''function name {'' syntax, variables follow roughly [[http://community.schemewiki.org/?lexical-scope|lexical-scoping]], except that functions themselves don't have scope, just like Bash. This means that even functions defined within a "function's scope" don't have access to non-local variables except through ''namerefs''.
|
||||
|
||||
===== See also =====
|
||||
|
||||
* http://wiki.bash-hackers.org/scripting/basics#variable_scope
|
141
original_source/commands/builtin/mapfile.txt
Normal file
141
original_source/commands/builtin/mapfile.txt
Normal file
@ -0,0 +1,141 @@
|
||||
====== The mapfile builtin command ======
|
||||
|
||||
===== Synopsis =====
|
||||
<code>
|
||||
mapfile [-n COUNT] [-O ORIGIN] [-s COUNT] [-t] [-u FD] [-C CALLBACK] [-c QUANTUM] [ARRAY]
|
||||
</code>
|
||||
|
||||
<code>
|
||||
readarray [-n COUNT] [-O ORIGIN] [-s COUNT] [-t] [-u FD] [-C CALLBACK] [-c QUANTUM] [ARRAY]
|
||||
</code>
|
||||
|
||||
===== Description =====
|
||||
|
||||
This builtin is also accessible using the command name ''readarray''.
|
||||
|
||||
''mapfile'' is one of the two builtin commands primarily intended for handling standard input (the other being ''read''). ''mapfile'' reads lines of standard input and assigns each to the elements of an indexed array. If no array name is given, the default array name is ''MAPFILE''. The target array must be a "normal" integer indexed array.
|
||||
|
||||
''mapfile'' returns success (0) unless an invalid option is given or the given array ''ARRAY'' is set readonly.
|
||||
|
||||
^Option ^Description ^
|
||||
|''-c QUANTUM'' |Specifies the number of lines that have to be read between every call to the callback specified with ''-C''. The default QUANTUM is 5000|
|
||||
|''-C CALLBACK'' |Specifies a callback. The string ''CALLBACK'' can be any shell code, the index of the array that will be assigned, and the line is appended at evaluation time. |
|
||||
|''-n COUNT'' |Reads at most ''COUNT'' lines, then terminates. If ''COUNT'' is 0, then all lines are read (default). |
|
||||
|''-O ORIGIN'' |Starts populating the given array ''ARRAY'' at the index ''ORIGIN'' rather than clearing it and starting at index 0. |
|
||||
|''-s COUNT'' |Discards the first ''COUNT'' lines read. |
|
||||
|''-t'' |Remove any trailing newline from a line read, before it is assigned to an array element. |
|
||||
|''-u FD'' |Read from filedescriptor ''FD'' rather than standard input. |
|
||||
|
||||
While ''mapfile'' isn't a common or portable shell feature, it's functionality will be familiar to many programmers. Almost all programming languages (aside from shells) with support for compound datatypes like arrays, and which handle open file objects in the traditional way, have some analogous shortcut for easily reading all lines of some input as a standard feature. In Bash, ''mapfile'' in itself can't do anything that couldn't already be done using read and a loop, and if portability is even a slight concern, should never be used. However, it does //significantly// outperform a read loop, and can make for shorter and cleaner code - especially convenient for interactive use.
|
||||
|
||||
===== Examples =====
|
||||
|
||||
Here's a real-world example of interactive use borrowed from Gentoo workflow. Xorg updates require rebuilding drivers, and the Gentoo-suggested command is less than ideal, so let's Bashify it. The first command produces a list of packages, one per line. We can read those into the array named "args" using ''mapfile'', stripping trailing newlines with the '-t' option. The resulting array is then expanded into the arguments of the "emerge" command - an interface to Gentoo's package manager. This type of usage can make for a safe and effective replacement for xargs(1) in certain situations. Unlike xargs, all arguments are guaranteed to be passed to a single invocation of the command with no wordsplitting, pathname expansion, or other monkey business.
|
||||
|
||||
<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
|
||||
|
||||
==== The callback ===
|
||||
|
||||
This is one of the more unusual features of a Bash builtin. As far as I'm able to tell, the exact behavior is as follows: If defined, as each line is read, the code contained within the string argument to the -C flag is evaluated and executed //before// the assignment of each array element. There are no restrictions to this string, which can be any arbitrary code, however, two additional "words" are automatically appended to the end before evaluation: the index, and corresponding line of data to be assigned to the next array element. Since all this happens before assignment, the callback feature cannot be used to modify the element to be assigned, though it can read and modify any array elements already assigned.
|
||||
|
||||
A very simple example might be to use it as a kind of progress bar. This will print a dot for each line read. Note the escaped comment to hide the appended words from printf.
|
||||
|
||||
<code>$ printf '%s\n' {1..5} | mapfile -c 1 -C 'printf . \#' )
|
||||
.....</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".
|
||||
<code>
|
||||
$ foo() { echo "|$1|"; }; mapfile -n 11 -c 2 -C 'foo' <file
|
||||
|2|
|
||||
|4|
|
||||
etc..
|
||||
</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:
|
||||
|
||||
<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}
|
||||
prefix input1
|
||||
prefix input3
|
||||
prefix input5
|
||||
prefix input7
|
||||
prefix input9
|
||||
prefix input2
|
||||
prefix input4
|
||||
prefix input6
|
||||
prefix input8
|
||||
prefix input10
|
||||
</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.
|
||||
|
||||
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[@]}"; }
|
||||
prefix input1
|
||||
prefix input3
|
||||
prefix input5
|
||||
prefix input7
|
||||
prefix input9
|
||||
|
||||
prefix input2
|
||||
prefix input4
|
||||
prefix input6
|
||||
prefix input8
|
||||
prefix input10
|
||||
</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.
|
||||
|
||||
<code>
|
||||
#!/usr/bin/env bash
|
||||
|
||||
showRecord() {
|
||||
printf 'key[%d] = %d, %d\n' "$1" "${vals[@]:keys[$1]*2:2}"
|
||||
}
|
||||
|
||||
parseRecords() {
|
||||
trap 'unset -f _f' RETURN
|
||||
_f() {
|
||||
local x
|
||||
IFS=: read -r _ x
|
||||
((keys[x]=n++))
|
||||
}
|
||||
local n
|
||||
|
||||
_f
|
||||
mapfile -tc2 -C _f "$1"
|
||||
eval "$1"'=("${'"$1"'[@]##*:}")' # Return the array with some modification
|
||||
}
|
||||
|
||||
main() {
|
||||
local -a keys vals
|
||||
parseRecords vals
|
||||
showRecord "$1"
|
||||
}
|
||||
|
||||
main "$1" <<-"EOF"
|
||||
fabric.domain:123
|
||||
routex:1
|
||||
routey:2
|
||||
fabric.domain:321
|
||||
routex:6
|
||||
routey:4
|
||||
EOF
|
||||
</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.
|
||||
===== Bugs =====
|
||||
|
||||
* Early implementations were buggy. For example, ''mapfile'' filling the readline history buffer with calls to the ''CALLBACK''. This was fixed in 4.1 beta.
|
||||
* ''mapfile -n'' reads an extra line beyond the last line assigned to the array, through Bash. [[ftp://ftp.gnu.org/gnu/bash/bash-4.2-patches/bash42-035 | Fixed in 4.2.35]].
|
||||
* ''mapfile'' callbacks could cause a crash if the variable being assigned is manipulated in certain ways. [[https://lists.gnu.org/archive/html/bug-bash/2013-01/msg00039.html]]. Fixed in 4.3.
|
||||
===== To Do =====
|
||||
* Create an implementation as a shell function that's portable between Ksh, Zsh, and Bash (and possibly other bourne-like shells with array support).
|
||||
|
||||
===== See also =====
|
||||
* [[syntax:arrays]]
|
||||
* [[commands:builtin:read]] - If you don't know about this yet, why are you reading this page?
|
||||
* [[http://mywiki.wooledge.org/BashFAQ/001]] - It's FAQ 1 for a reason.
|
358
original_source/commands/builtin/printf.txt
Normal file
358
original_source/commands/builtin/printf.txt
Normal file
@ -0,0 +1,358 @@
|
||||
====== The printf command ======
|
||||
|
||||
<div center round todo box 70%>
|
||||
FIXME Stranger, this is a very big topic that needs experience - please fill in missing information, extend the descriptions, and correct the details if you can!
|
||||
</div>
|
||||
<div center round tip 70%>
|
||||
__**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]].
|
||||
</div>
|
||||
|
||||
Unlike other documentations, I don't want to redirect you to the manual page for the ''printf()'' C function family. However, if you're more experienced, that should be the most detailed description for the format strings and modifiers.
|
||||
|
||||
Due to conflicting historical implementations of the ''echo'' command, POSIX(r) recommends that ''printf'' is preferred over ''[[commands:builtin:echo|echo]]''.
|
||||
|
||||
===== General =====
|
||||
|
||||
The ''printf'' command provides a method to print preformatted text similar to the ''printf()'' system interface (C function). It's meant as successor for ''echo'' and has far more features and possibilities.
|
||||
|
||||
Beside other reasons, POSIX(r) has a very good argument to recommend it: Both historical main flavours of the ''echo'' command are mutual exclusive, they collide. A "new" command had to be invented to solve the issue.
|
||||
|
||||
===== Syntax =====
|
||||
|
||||
<code>
|
||||
printf <FORMAT> <ARGUMENTS...>
|
||||
</code>
|
||||
|
||||
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:
|
||||
<code>
|
||||
printf "Surname: %s\nName: %s\n" "$SURNAME" "$FIRSTNAME"
|
||||
</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!).
|
||||
|
||||
==== Options ====
|
||||
|
||||
|''-v VAR''|If given, the output is assigned to the variable ''VAR'' instead of printed to ''stdout'' (comparable to ''sprintf()'' in some way)|
|
||||
|
||||
The ''-v'' Option can't assign directly to array indexes in Bash versions older than Bash 4.1.
|
||||
|
||||
<note warning>
|
||||
In versions newer than 4.1, one must be careful when performing expansions into the first non-option argument of printf as this opens up the possibility of an easy code injection vulnerability.
|
||||
<code>
|
||||
$ var='-vx[$(echo hi >&2)]'; printf "$var" hi; declare -p x
|
||||
hi
|
||||
declare -a x='([0]="hi")'
|
||||
</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.
|
||||
</note>
|
||||
|
||||
==== Arguments ====
|
||||
|
||||
Of course in shell-meaning the arguments are just strings, however, the common C-notations plus some additions for number-constants are recognized to give a number-argument to ''printf'':
|
||||
|
||||
^Number-Format^Description^
|
||||
|''N''|A normal decimal number|
|
||||
|''0N''|An octal number|
|
||||
|''0xN''|A hexadecimal number|
|
||||
|''0XN''|A hexadecimal number|
|
||||
|''"X''|(a literal double-quote infront of a character): interpreted as number (underlying codeset) **don't forget escaping**|
|
||||
|''<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).
|
||||
|
||||
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>
|
||||
__**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>
|
||||
printf '%d\n' 08
|
||||
</code>
|
||||
</note>
|
||||
|
||||
==== Format strings ====
|
||||
|
||||
The format string interpretion is derived from the C ''printf()'' function family. Only format specifiers that end in one of the letters ''diouxXfeEgGaAcs'' are recognized.
|
||||
|
||||
To print a literal ''%'' (percent-sign), use ''<nowiki>%%</nowiki>'' in the format string.
|
||||
|
||||
__**Again:**__ Every format specifier expects an associated argument provided!
|
||||
|
||||
These specifiers have different names, depending who you ask. But they all mean the same: A placeholder for data with a specified format:
|
||||
* format placeholder
|
||||
* conversion specification
|
||||
* formatting token
|
||||
* ...
|
||||
|
||||
^Format^Description^
|
||||
|''%b''|Print the associated argument while interpreting backslash escapes in there|
|
||||
|''%q''|Print the associated argument **shell-quoted**, reusable as input|
|
||||
|''%d''|Print the associated argument as **signed decimal** number|
|
||||
|''%i''|Same as ''%d''|
|
||||
|''%o''|Print the associated argument as **unsigned octal** number|
|
||||
|''%u''|Print the associated argument as **unsigned decimal** number|
|
||||
|''%x''|Print the associated argument as **unsigned hexadecimal** number with lower-case hex-digits (a-f)|
|
||||
|''%X''|Same as ''%x'', but with upper-case hex-digits (A-F)|
|
||||
|''%f''|Interpret and print the associated argument as **floating point** number|
|
||||
|''%e''|Interpret the associated argument as **double**, and print it in ''<N>±e<N>'' format|
|
||||
|''%E''|Same as ''%e'', but with an upper-case ''E'' in the printed format|
|
||||
|''%g''|Interprets the associated argument as **double**, but prints it like ''%f'' or ''%e''|
|
||||
|''%G''|Same as ''%g'', but print it like ''%E''|
|
||||
|''%c''|Interprets the associated argument as **char**: only the first character of a given argument is printed|
|
||||
|''%s''|Interprets the associated argument literally as string|
|
||||
|''%n''|Assigns the number of characters printed so far to the variable named in the corresponding argument. Can't specify an array index. If the given name is already an array, the value is assigned to the zeroth element.|
|
||||
|''%a''|Interprets the associated argument as **double**, and prints it in the form of a C99 [[http://www.exploringbinary.com/hexadecimal-floating-point-constants/ | hexadecimal floating-point literal]].|
|
||||
|''%A''|Same as ''%a'', but print it like ''%E''|
|
||||
|''%(FORMAT)T''|output the date-time string resulting from using ''FORMAT'' as a format string for ''strftime(3)''. The associated argument is the number of seconds since Epoch, or ''-1'' (current time) or ''-2'' (shell startup time). If no corresponding argument is supplies, the current time is used as default|
|
||||
|''%%''|No conversion is done. Produces a ''%'' (percent sign)|
|
||||
|
||||
Some of the mentioned format specifiers can modify their behaviour by getting a format modifier:
|
||||
==== Modifiers ====
|
||||
|
||||
To be more flexible in the output of numbers and strings, the ''printf'' command allows format modifiers. These are specified **between** the introductory ''%'' and the character that specifies the format:
|
||||
<code>printf "%50s\n" "This field is 50 characters wide..."</code>
|
||||
|
||||
=== Field and printing modifiers ===
|
||||
|
||||
^Field output format^^
|
||||
|''<N>''|**Any number**: Specifies a **minimum field width**, if the text to print is shorter, it's padded with spaces, if the text is longer, the field is expanded|
|
||||
|''.''|**The dot**: Together with a field width, the field is **not** expanded when the text is longer, the text is truncated instead. "''%.s''" is an undocumented equivalent for "''%.0s''", which will force a field width of zero, effectively hiding the field from output|
|
||||
|''*''|**The asterisk**: the width is given as argument before the string or number. Usage (the "''*''" corresponds to the "''20''"): ''printf "%*s\n" 20 "test string"''|
|
||||
|''#''|"Alternative format" for numbers: see table below|
|
||||
|''-''|**Left-bound** text printing in the field (standard is **right-bound**)|
|
||||
|''0''|Pads numbers with zeros, not spaces|
|
||||
|''<space>''|Pad a positive number with a space, where a minus (''-'') is for negative numbers|
|
||||
|''+''|Prints all numbers **signed** (''+'' for positive, ''-'' for negative)|
|
||||
|''<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 ''#'':**__
|
||||
|
||||
^Alternative Format^^
|
||||
|''%#o''|The octal number is printed with a leading zero, unless it's zero itself|
|
||||
|''%#x'', ''%#X''|The hex number is printed with a leading "''0x''"/"''0X''", unless it's zero|
|
||||
|''%#g'', ''%#G''|The float number is printed with **trailing zeros** until the number of digits for the current precision is reached (usually trailing zeros are not printed)|
|
||||
|all number formats except ''%d'', ''%o'', ''%x'', ''%X''|Always print a decimal point in the output, even if no digits follow it|
|
||||
|
||||
=== Precision ===
|
||||
|
||||
The precision for a floating- or double-number can be specified by using ''.<DIGITS>'', where ''<DIGITS>'' is the number of digits for precision. If ''<DIGITS>'' is an asterisk (''*''), the precision is read from the argument that precedes the number to print, like (prints 4,3000000000):
|
||||
<code>printf "%.*f\n" 10 4,3</code>
|
||||
The format ''.*N'' to specify the N'th argument for precision does not work in Bash.
|
||||
|
||||
For strings, the precision specifies the maximum number of characters to print (i.e., the maximum field width). For integers, it specifies the number of digits to print (zero-padding!).
|
||||
|
||||
==== Escape codes ====
|
||||
|
||||
These are interpreted if used anywhere in the format string, or in an argument corresponding to a ''%b'' format.
|
||||
|
||||
^Code^Description^
|
||||
|''\\''|Prints the character ''\'' (backslash)|
|
||||
|''\a''|Prints the alert character (ASCII code 7 decimal)|
|
||||
|''\b''|Prints a backspace|
|
||||
|''\f''|Prints a form-feed|
|
||||
|''\n''|Prints a newline|
|
||||
|''\r''|Prints a carriage-return|
|
||||
|''\t''|Prints a horizontal tabulator|
|
||||
|''\v''|Prints a vertical tabulator|
|
||||
|''\"''|Prints a ''<nowiki>'</nowiki>''|
|
||||
|''\?''|Prints a ''?''|
|
||||
|''\<NNN>''|Interprets ''<NNN>'' as **octal** number and prints the corresponding character from the character set|
|
||||
|''\0<NNN>''|same as ''\<NNN>''|
|
||||
|''\x<NNN>''|Interprets ''<NNN>'' as **hexadecimal** number and prints the corresponding character from the character set (**3 digits**)|
|
||||
|''\u<NNNN>''|same as ''\x<NNN>'', but **4 digits**|
|
||||
|''\U<NNNNNNNN>''|same as ''\x<NNN>'', but **8 digits**|
|
||||
|
||||
The following additional escape and extra rules apply only to arguments associated with a ''%b'' format:
|
||||
|
||||
|''\c''|Terminate output similarly to the ''\c'' escape used by ''echo -e''. printf produces no additional output after coming across a ''\c'' escape in a ''%b'' argument.|
|
||||
|
||||
* Backslashes in the escapes: ''\<nowiki>'</nowiki>'', ''\"'', and ''\?'' are not removed.
|
||||
* Octal escapes beginning with ''\0'' may contain up to four digits. (POSIX specifies up to three).
|
||||
|
||||
These are also respects in which ''%b'' differs from the escapes used by [[syntax/quoting#ansi_c_like_strings | $'...']] style quoting.
|
||||
|
||||
===== Examples =====
|
||||
|
||||
==== Snipplets ====
|
||||
|
||||
* print the decimal representation of a hexadecimal number (preserve the sign)
|
||||
* ''printf "%d\n" 0x41''
|
||||
* ''printf "%d\n" -0x41''
|
||||
* ''printf "%+d\n" 0x41''
|
||||
* print the octal representation of a decimal number
|
||||
* ''printf "%o\n" 65''
|
||||
* ''printf "%05o\n" 65'' (5 characters width, padded with zeros)
|
||||
* this prints a 0, since no argument is specified
|
||||
* ''printf "%d\n"''
|
||||
* print the code number of the character ''A''
|
||||
* ''printf "%d\n" \'A''
|
||||
* ''printf "%d\n" "'A"''
|
||||
* Generate a greeting banner and assign it to the variable ''GREETER''
|
||||
* ''printf -v GREETER "Hello %s" "$LOGNAME"''
|
||||
* Print a text at the end of the line, using ''tput'' to get the current line width
|
||||
* ''printf "%*s\n" $(tput cols) "Hello world!"''
|
||||
|
||||
==== Small code table ====
|
||||
|
||||
This small loop prints all numbers from 0 to 127 in
|
||||
* decimal
|
||||
* octal
|
||||
* hex
|
||||
<code>
|
||||
for ((x=0; x <= 127; x++)); do
|
||||
printf '%3d | %04o | 0x%02x\n' "$x" "$x" "$x"
|
||||
done
|
||||
</code>
|
||||
|
||||
==== 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, ...):
|
||||
|
||||
<code>
|
||||
the_mac="0:13:ce:7:7a:ad"
|
||||
|
||||
# lowercase hex digits
|
||||
the_mac="$(printf "%02x:%02x:%02x:%02x:%02x:%02x" 0x${the_mac//:/ 0x})"
|
||||
|
||||
# or the uppercase-digits variant
|
||||
the_mac="$(printf "%02X:%02X:%02X:%02X:%02X:%02X" 0x${the_mac//:/ 0x})"
|
||||
</code>
|
||||
|
||||
==== Replacement echo ====
|
||||
|
||||
This code was found in Solaris manpage for echo(1).
|
||||
|
||||
Solaris version of ''/usr/bin/echo'' is equivalent to:
|
||||
|
||||
<code>
|
||||
printf "%b\n" "$*"
|
||||
</code>
|
||||
|
||||
Solaris ''/usr/ucb/echo'' is equivalent to:
|
||||
|
||||
<code>
|
||||
if [ "X$1" = "X-n" ]
|
||||
then
|
||||
shift
|
||||
printf "%s" "$*"
|
||||
else
|
||||
printf "%s\n" "$*"
|
||||
fi
|
||||
</code>
|
||||
|
||||
==== prargs Implementation ====
|
||||
|
||||
Working off the replacement echo, here is a terse implementation of prargs:
|
||||
|
||||
<code>
|
||||
printf '"%b"\n' "$0" "$@" | nl -v0 -s": "
|
||||
</code>
|
||||
|
||||
==== repeating a character (for example to print a line) ====
|
||||
|
||||
A small trick: Combining printf and parameter expansion to draw a line
|
||||
|
||||
<code>
|
||||
length=40
|
||||
printf -v line '%*s' "$length"
|
||||
echo ${line// /-}
|
||||
</code>
|
||||
or:
|
||||
<code>
|
||||
length=40
|
||||
eval printf -v line '%.0s-' {1..$length}
|
||||
</code>
|
||||
|
||||
==== Replacement for some calls to date(1) ====
|
||||
|
||||
The ''%(...)T'' format string is a direct interface to ''strftime(3)''.
|
||||
|
||||
<code>
|
||||
$ printf 'This is week %(%U/%Y)T.\n' -1
|
||||
This is week 52/2010.
|
||||
</code>
|
||||
|
||||
Please read the manpage of ''strftime(3)'' to get more information about the supported formats.
|
||||
|
||||
===== differences from awk printf =====
|
||||
|
||||
Awk also derives its //printf()// function from C, and therefore has similar format specifiers. However, in all versions of awk the space character is used as a string concatenation operator, so it cannot be used as an argument separator. **Arguments to awk printf must be separated by commas.** Some versions of awk do not require printf arguments to be surrounded by parentheses, but you should use them anyway to provide portability.
|
||||
|
||||
In the following example, the two strings are concatenated by the intervening space so that no argument remains to fill the format.
|
||||
|
||||
<code>
|
||||
|
||||
$ echo "Foo" | awk '{ printf "%s\n" $1 }'
|
||||
awk: (FILENAME=- FNR=1) fatal: not enough arguments to satisfy format string
|
||||
`%s
|
||||
Foo'
|
||||
^ ran out for this one
|
||||
|
||||
</code>
|
||||
|
||||
Simply replacing the space with a comma and adding parentheses yields correct awk syntax.
|
||||
|
||||
<code>
|
||||
$ echo "Foo" | awk '{ printf( "%s\n", $1 ) }'
|
||||
Foo
|
||||
</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.
|
||||
|
||||
<code>
|
||||
echo "Foo" | awk '{ system( "printf \"%s\\n \" \"" $1 "\"" ) }'
|
||||
Foo
|
||||
</code>
|
||||
|
||||
===== Differences from C, and portability considerations =====
|
||||
|
||||
* The a, A, e, E, f, F, g, and G conversions are supported by Bash, but not required by POSIX.
|
||||
|
||||
* There is no wide-character support (wprintf). For instance, if you use ''%c'', you're actually asking for the first byte of the argument. Likewise, the maximum field width modifier (dot) in combination with ''%s'' goes by bytes, not characters. This limits some of printf's functionality to working with ascii only. ksh93's ''printf'' supports the ''L'' modifier with ''%s'' and ''%c'' (but so far not ''%S'' or ''%C'') in order to treat precision as character width, not byte count. zsh appears to adjust itself dynamically based upon ''LANG'' and ''LC_CTYPE''. If ''LC_CTYPE=C'', zsh will throw "character not in range" errors, and otherwise supports wide characters automatically if a variable-width encoding is set for the current locale.
|
||||
|
||||
* Bash recognizes and skips over any characters present in the length modifiers specified by POSIX during format string parsing.
|
||||
<code c|builtins/printf.def>
|
||||
#define LENMODS "hjlLtz"
|
||||
...
|
||||
/* skip possible format modifiers */
|
||||
modstart = fmt;
|
||||
while (*fmt && strchr (LENMODS, *fmt))
|
||||
fmt++;
|
||||
</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.
|
||||
|
||||
* 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>
|
||||
# Illustrates Bash-like behavior. Redefining printf is usually unnecessary / not recommended.
|
||||
function printf {
|
||||
case $1 in
|
||||
-v)
|
||||
shift
|
||||
nameref x=$1
|
||||
shift
|
||||
x=$(command printf "$@")
|
||||
;;
|
||||
*)
|
||||
command printf "$@"
|
||||
esac
|
||||
}
|
||||
builtin cut
|
||||
print $$
|
||||
printf -v 'foo[2]' '%d\n' "$(cut -d ' ' -f 1 /proc/self/stat)"
|
||||
typeset -p foo
|
||||
# 22461
|
||||
# typeset -a foo=([2]=22461)
|
||||
</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.
|
||||
|
||||
* Assigning to variables: The ''printf -v'' way is slightly different to the way using command-substitution. [[syntax:expansion:cmdsubst | Command substitution]] removes trailing newlines before substituting the text, ''printf -v'' preserves all output.
|
||||
|
||||
===== See also =====
|
||||
|
||||
* SUS: [[http://pubs.opengroup.org/onlinepubs/9699919799/utilities/printf.html | printf utility]] and [[http://pubs.opengroup.org/onlinepubs/9699919799/functions/printf.html | printf() function]]
|
||||
* [[snipplets:print_horizontal_line|Code snip: Print a horizontal line]] uses some ''printf'' examples
|
||||
* [[BashFAQ>018 | Greg's BashFAQ 18: How can I use numbers with leading zeros in a loop, e.g., 01, 02?]]
|
242
original_source/commands/builtin/read.txt
Normal file
242
original_source/commands/builtin/read.txt
Normal file
@ -0,0 +1,242 @@
|
||||
====== The read builtin command ======
|
||||
//read something about read here!//
|
||||
|
||||
===== Synopsis =====
|
||||
<code>
|
||||
read [-ers] [-u <FD>] [-t <TIMEOUT>] [-p <PROMPT>] [-a <ARRAY>] [-n <NCHARS>] [-N <NCHARS>] [-d <DELIM>] [-i <TEXT>] [<NAME...>]
|
||||
</code>
|
||||
|
||||
===== Description =====
|
||||
The ''read'' builtin reads **one line** of data (text, user input, ...) from standard input or a supplied filedescriptor number into one or more variables named by ''<NAME...>''.
|
||||
|
||||
Since Bash 4.3-alpha, ''read'' skips any ''NUL'' (ASCII code 0) characters in input.
|
||||
|
||||
If ''<NAME...>'' is given, the line is word-split using [[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%>
|
||||
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>
|
||||
while read -r; do
|
||||
printf '"%s"\n' "$REPLY"
|
||||
done <<<" a line with prefix and postfix space "
|
||||
</code>
|
||||
</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.
|
||||
|
||||
|
||||
|
||||
==== Options ====
|
||||
|
||||
^Option^Description^
|
||||
|''-a <ARRAY>''|read the data word-wise into the specified array ''<ARRAY>'' instead of normal variables|
|
||||
|''-d <DELIM>''|recognize ''<DELIM>'' as data-end, rather than ''<newline>''|
|
||||
|''-e''|on interactive shells: use Bash's readline interface to read the data. Since version 5.1-alpha, this can also be used on specified file descriptors using ''-u''|
|
||||
|''-i <STRING>''|preloads the input buffer with text from ''<STRING>'', only works when Readline (''-e'') is used|
|
||||
|''-n <NCHARS>''|reads ''<NCHARS>'' characters of input, then quits|
|
||||
|''-N <NCHARS>''|reads ''<NCHARS>'' characters of input, //ignoring any delimiter//, then quits|
|
||||
|''-p <PROMPT>''|the prompt string ''<PROMPT>'' is output (without a trailing automatic newline) before the read is performed|
|
||||
|''-r''|raw input - **disables** interpretion of **backslash escapes** and **line-continuation** in the read data|
|
||||
|''-s''|secure input - don't echo input if on a terminal (passwords!)|
|
||||
|''-t <TIMEOUT>''|wait for data ''<TIMEOUT>'' seconds, then quit (exit code 1). Fractional seconds ("5.33") are allowed since Bash 4. A value of 0 immediately returns and indicates if data is waiting in the exit code. Timeout is indicated by an exit code greater than 128. If timeout arrives before data is read completely (before end-of-line), the partial data is saved.|
|
||||
|''-u <FD>''|use the filedescriptor number ''<FD>'' rather than ''stdin'' (0)|
|
||||
|
||||
When both, ''-a <ARRAY>'' and a variable name ''<NAME>'' is given, then the array is set, but not the variable.
|
||||
|
||||
Of course it's valid to set individual array elements without using ''-a'':
|
||||
<code>
|
||||
read MYARRAY[5]
|
||||
</code>
|
||||
|
||||
<WRAP center round important 90%>
|
||||
|
||||
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
|
||||
<code>
|
||||
read x[1]
|
||||
</code>
|
||||
then pathname expansion will expand to the filename ''x1'' and break your processing!
|
||||
|
||||
Even worse, if ''nullglob'' is set, your array/index will disappear.
|
||||
|
||||
To avoid this, either **disable pathname expansion** or **quote** the array name and index:
|
||||
<code>
|
||||
read 'x[1]'
|
||||
</code>
|
||||
|
||||
</WRAP>
|
||||
|
||||
==== Return status ====
|
||||
|
||||
^Status ^Reason ^
|
||||
|0 |no error |
|
||||
|0 |error when assigning to a read-only variable ((fixed in 4.2-rc1)) |
|
||||
|2 |invalid option |
|
||||
|>128 |timeout (see ''-t'') |
|
||||
|!=0 |invalid filedescriptor supplied to ''-u'' |
|
||||
|!=0 |end-of-file reached |
|
||||
|
||||
==== read without -r ====
|
||||
|
||||
Essentially all you need to know about ''-r'' is to **ALWAYS** use it. The exact behavior you get without ''-r'' is completely useless even for weird purposes. It basically allows the escaping of input which matches something in IFS, and also escapes line continuations. It's explained pretty well in the [[http://pubs.opengroup.org/onlinepubs/9699919799/utilities/read.html#tag_20_109 | POSIX read]] spec.
|
||||
|
||||
<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:49:00 ormaaj so that's what read without -r does?
|
||||
2012-05-23 13:49:16 geirha no, -r doesn't remove the backslashes
|
||||
2012-05-23 13:49:34 ormaaj I thought read <<<'str' was equivalent to read -r <<<$'str'
|
||||
2012-05-23 13:49:38 geirha # read x y <<< 'foo\ bar baz'; echo "<$x><$y>"
|
||||
2012-05-23 13:49:40 shbot geirha: <foo bar><baz>
|
||||
2012-05-23 13:50:32 geirha no, read without -r is mostly pointless. Damn bourne
|
||||
2012-05-23 13:51:08 ormaaj So it's mostly (entirely) used to escape spaces
|
||||
2012-05-23 13:51:24 ormaaj and insert newlines
|
||||
2012-05-23 13:51:47 geirha ormaaj: you mostly get the same effect as using \ at the prompt
|
||||
2012-05-23 13:52:04 geirha echo \" outputs a " , read x <<< '\"' reads a "
|
||||
2012-05-23 13:52:32 ormaaj oh weird
|
||||
2012-05-23 13:52:46 * ormaaj struggles to think of a point to that...
|
||||
2012-05-23 13:53:01 geirha ormaaj: ask Bourne :P
|
||||
2012-05-23 13:53:20 geirha (not Jason)
|
||||
2012-05-23 13:53:56 ormaaj hm thanks anyway :)
|
||||
</code>
|
||||
|
||||
===== Examples =====
|
||||
==== Rudimentary cat replacement ====
|
||||
|
||||
A rudimentary replacement for the ''cat'' command: read lines of input from a file and print them on the terminal.
|
||||
<code>
|
||||
opossum() {
|
||||
while read -r; do
|
||||
printf "%s\n" "$REPLY"
|
||||
done <"$1"
|
||||
}
|
||||
</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'').
|
||||
|
||||
|
||||
|
||||
==== Press any key... ====
|
||||
|
||||
Remember the MSDOS ''pause'' command? Here's something similar:
|
||||
<code>
|
||||
pause() {
|
||||
local dummy
|
||||
read -s -r -p "Press any key to continue..." -n 1 dummy
|
||||
}
|
||||
</code>
|
||||
|
||||
Notes:
|
||||
* ''-s'' to suppress terminal echo (printing)
|
||||
* ''-r'' to not interpret special characters (like waiting for a second character if somebody presses the backslash)
|
||||
|
||||
==== Reading Columns ====
|
||||
|
||||
=== Simple Split ===
|
||||
|
||||
Read can be used to split a string:
|
||||
<code>
|
||||
var="one two three"
|
||||
read -r col1 col2 col3 <<< "$var"
|
||||
printf "col1: %s col2: %s col3 %s\n" "$col1" "$col2" "$col3"
|
||||
</code>
|
||||
|
||||
Take care that you cannot use a pipe:
|
||||
<code>
|
||||
echo "$var" | read col1 col2 col3 # does not work!
|
||||
printf "col1: %s col2: %s col3 %s\n" "$col1" "$col2" "$col3"
|
||||
</code>
|
||||
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]]).
|
||||
|
||||
If the variable has more fields than there are variables, the last variable get the remaining of the line:
|
||||
<code>
|
||||
read col1 col2 col3 <<< "one two three four"
|
||||
printf "%s\n" "$col3" #prints three four
|
||||
</code>
|
||||
|
||||
|
||||
=== 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]],
|
||||
the Internal Field Separator.
|
||||
<code>
|
||||
IFS=":" read -r col1 col2 <<< "hello:world"
|
||||
printf "col1: %s col2: %s\n" "$col1" "$col2"
|
||||
</code>
|
||||
Here we use the ''var=value command'' syntax to set the environment of ''read'' temporarily. We could have set ''IFS'' normally,
|
||||
but then we would have to take care to save its value and restore it afterward (''OLD=$IFS IFS=":"; read ....;IFS=$OLD'').
|
||||
|
||||
The default ''IFS'' is special in that 2 fields can be separated by one or more space or tab. When you set ''IFS'' to something besides whitespace (space or tab), the
|
||||
fields are separated by **exactly** one character:
|
||||
<code>
|
||||
IFS=":" read -r col1 col2 col3 <<< "hello::world"
|
||||
printf "col1: %s col2: %s col3 %s\n" "$col1" "$col2" "$col3"
|
||||
</code>
|
||||
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:
|
||||
<code>
|
||||
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"
|
||||
</code>
|
||||
|
||||
|
||||
==== Are you sure? ====
|
||||
<code>
|
||||
asksure() {
|
||||
echo -n "Are you sure (Y/N)? "
|
||||
while read -r -n 1 -s answer; do
|
||||
if [[ $answer = [YyNn] ]]; then
|
||||
[[ $answer = [Yy] ]] && retval=0
|
||||
[[ $answer = [Nn] ]] && retval=1
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
echo # just a final linefeed, optics...
|
||||
|
||||
return $retval
|
||||
}
|
||||
|
||||
### using it
|
||||
if asksure; then
|
||||
echo "Okay, performing rm -rf / then, master...."
|
||||
else
|
||||
echo "Pfff..."
|
||||
fi
|
||||
</code>
|
||||
|
||||
==== Ask for a path with a default value ====
|
||||
|
||||
__**Note:**__ The ''-i'' option was introduced with Bash 4
|
||||
|
||||
<code>
|
||||
read -e -p "Enter the path to the file: " -i "/usr/local/etc/" FILEPATH
|
||||
</code>
|
||||
|
||||
The user will be prompted, he can just accept the default, or edit it.
|
||||
|
||||
==== Multichar-IFS: Parsing a simple date/time string ====
|
||||
|
||||
Here, ''IFS'' contains both, a colon and a space. The fields of the date/time string are recognized correctly.
|
||||
|
||||
<code>
|
||||
datetime="2008:07:04 00:34:45"
|
||||
IFS=": " read -r year month day hour minute second <<< "$datetime"
|
||||
</code>
|
||||
|
||||
===== Portability considerations =====
|
||||
* POSIX(r) only specified the ''-r'' option (raw read); ''-r'' is not only POSIX, you can find it in earlier Bourne source code
|
||||
* POSIX(r) doesn't support arrays
|
||||
* ''REPLY'' is not POSIX(r), you need to set ''IFS'' to the empty string to get the whole line for shells that don't know ''REPLY''. <code>
|
||||
while IFS= read -r line; do
|
||||
...
|
||||
done < text.txt
|
||||
</code>
|
||||
|
||||
|
||||
|
||||
|
||||
===== See also =====
|
||||
* Internal: [[commands:builtin:printf | The printf builtin command]]
|
39
original_source/commands/builtin/readonly.txt
Normal file
39
original_source/commands/builtin/readonly.txt
Normal file
@ -0,0 +1,39 @@
|
||||
====== The readonly builtin command ======
|
||||
|
||||
===== Synopsis =====
|
||||
|
||||
<code>
|
||||
readonly [-p] [-a] [-A] [-f] [NAME[=VALUE] ...]
|
||||
</code>
|
||||
|
||||
===== Description =====
|
||||
|
||||
The ''readonly'' builtin command is used to mark variables or functions as read-only, which means unchangeable. This implies that it can't be unset anymore. A ''readonly'' variable may not be redefined in child scopes. A readonly global may not be redefined as a function local variable. Simple command environment assignments may not reference readonly variables.
|
||||
|
||||
==== Options ====
|
||||
|
||||
^Option ^Description ^
|
||||
|''-a'' |refer to normal arrays |
|
||||
|''-A'' |refer to associative arrays |
|
||||
|''-f'' |refer to functions |
|
||||
|''-p'' |print all read-only variables or functions, ''-a'', ''-A'' and ''-f'' can be used to filter. The output is reusable as input |
|
||||
|
||||
An argument of ''%%--%%'' disables further option processing.
|
||||
==== Return status ====
|
||||
|
||||
^Status ^Reason ^
|
||||
|0 |no error |
|
||||
|!=0 |invalid option |
|
||||
|!=0 |invalid combination of options |
|
||||
|!=0 |a given ''NAME'' is invalid |
|
||||
|
||||
===== Examples =====
|
||||
|
||||
===== Portability considerations =====
|
||||
|
||||
* in POSIX(r), only the ''-p'' option is specified
|
||||
|
||||
===== See also =====
|
||||
|
||||
* [[commands:builtin:declare]]
|
||||
* [[commands:builtin:unset]]
|
30
original_source/commands/builtin/return.txt
Normal file
30
original_source/commands/builtin/return.txt
Normal file
@ -0,0 +1,30 @@
|
||||
====== The return builtin command ======
|
||||
|
||||
===== Synopsis =====
|
||||
<code>return [N]</code>
|
||||
|
||||
===== Description =====
|
||||
The ''return'' command returns from a shell function.
|
||||
|
||||
If ''N'' is given, the return code to the caller is set to ''N''. If not, the returned status the the status of the most recently executed command (i.e. ''$?'').
|
||||
|
||||
==== Options ====
|
||||
There are no options.
|
||||
|
||||
|
||||
==== Exit status ====
|
||||
|
||||
If everything is okay, the ''return'' command doesn't come back. If it comes back, there was a problem in doing the return.
|
||||
|
||||
^Status ^Reason ^
|
||||
|1 |''return'' was called while not being in a shell function or sourced file |
|
||||
|
||||
|
||||
===== Examples =====
|
||||
|
||||
|
||||
===== Portability considerations =====
|
||||
|
||||
===== See also =====
|
||||
* [[commands:builtin:exit|The exit builtin command]]
|
||||
* [[dict:terms:exit_status|The exit status]]
|
327
original_source/commands/builtin/select.txt
Normal file
327
original_source/commands/builtin/select.txt
Normal file
File diff suppressed because one or more lines are too long
74
original_source/commands/builtin/set.txt
Normal file
74
original_source/commands/builtin/set.txt
Normal file
@ -0,0 +1,74 @@
|
||||
====== The set builtin command ======
|
||||
|
||||
FIXME incomplete - text, examples, maybe extended description
|
||||
|
||||
===== Synopsis =====
|
||||
|
||||
<code>
|
||||
set [--abefhkmnptuvxBCHP] <-o OPTIONNAME> [-][--] <POSPARAMS>
|
||||
</code>
|
||||
|
||||
===== Description =====
|
||||
|
||||
''set'' is primarily made to
|
||||
* set the positional parameters (see [[scripting:posparams | handling positional parameters]]) to ''<POSPARAMS>''
|
||||
* set shell attributes with short options (see below)
|
||||
* set shell attributes with long option names (see below)
|
||||
|
||||
Without any options, ''set'' displays all shell- and environment-variables (only is POSIX-mode) in a re-usable format ''NAME=VALUE''.
|
||||
|
||||
==== Attributes ====
|
||||
|
||||
All attributes below can be switched on using ''-X'' and switched off using ''+X''. This is done because of the historical meaning of the ''-'' to set flags (true for most commands on UNIX(r)).
|
||||
|
||||
^Flag^Optionname^Description^
|
||||
|''-a''|''allexport''|Automatically mark new and altered variables to be exported to subsequent environments.|
|
||||
|''-b''|''notify''|Don't wait for the next prompt to print when showing the reports for a terminated background job (only with job control)|
|
||||
|''-e''|''errexit''|When set, the shell exits when a simple command in a command list exits non-zero (''FALSE''). This is not done in situations, where the exit code is already checked (''if'', ''while'', ''until'', ''<nowiki>||</nowiki>'', ''<nowiki>&&</nowiki>'')|
|
||||
|''-f''|''noglob''|Disable [[syntax:expansion:globs | pathname expansion]] (globbing)|
|
||||
|''-h''|''hashall''|Remembers the location of commands when they're called (hashing). Enabled by default.|
|
||||
|''-k''|''keyword''|Allows to place environment-assignments everywhere in the commandline, not only infront of the called command.|
|
||||
|''-m''|''monitor''|**Monitor mode**. With job control, a short descriptive line is printed when a backgroud job ends. Default is "on" for interactive shells (with job control).|
|
||||
|''-n''|''noexec''|Read and parse but **do not execute commands** - useful for checking scripts for syntax errors. Ignored by interactive shells.|
|
||||
|''-o''| |Set/unset attributes with long option names, e.g. ''set -o noglob''. The long option names are in the second column of this table. If no option name is given, all options are printed with their current status.|
|
||||
|''-p''|''privileged''|Turn on privileged mode.|
|
||||
|''-t''|''onecmd''|Exit after reading and executing **one** command.|
|
||||
|''-u''|''nounset''|Treat unset variables as an error when performing parameter expansion. Non-interactive shells exit on this error.|
|
||||
|''-v''|''verbose''|Print shell input lines as they are read - useful for debugging.|
|
||||
|''-x''|''xtrace''|Print commands just before execution - with all expansions and substitutions done, and words marked - useful for debugging.|
|
||||
|''-B''|''braceexpand''|The shell performs [[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]]|
|
||||
|''-E''|''errtrace''|''ERR''-traps are inherited by by shell functions, command substitutions, and commands executed in a subshell environment.|
|
||||
|''-H''|''histexpand''|Enable ''!''-style history expansion. Defaults to ''on'' for interactive shells.|
|
||||
|''-P''|''physical''|Don't follow symlinks when changing directories - use the physical filesystem structure.|
|
||||
|''-T''|''functrace''|''DEBUG''- and ''RETURN''-traps are inherited by subsequent environments, like ''-E'' for ''ERR'' trap.|
|
||||
|''-''| |"End of options" - all following arguments are assigned to the positional parameters, even when they begin with a dash. ''-x'' and ''-v'' options are turned off. Positional parameters are unchanged (unlike using ''<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.|
|
||||
^^Long options usable with ''-o'' without a short equivalent ^^
|
||||
| |''emacs''|Use an emacs-style command line editing interface. This is enabled by default when the shell is interactive, unless the shell is started with ''--noediting'' option.|
|
||||
| |''history''|If set, command historization is done (enabled by default on interactive shells)|
|
||||
| |''ignoreeof''|The effect is as if the shell command ''IGNOREEOF=10'' had been executed. See [[syntax:shellvars | shell variables]].|
|
||||
| |''nolog''|**(currently ignored)**|
|
||||
| |''pipefail''|If set, the exit code from a pipeline is different from the normal ("last command in pipeline") behaviour: ''TRUE'' when no command failed, ''FALSE'' when something failed (code of the rightmost command that failed)|
|
||||
| |''posix''|When set, Bash runs in POSIX mode.|
|
||||
| |''vi''|Enables a ''vi''-style command line editing interface.|
|
||||
|
||||
===== Examples =====
|
||||
|
||||
Tag a part of a shell script to output debugging information (''-x''):
|
||||
<code bash>
|
||||
#!/bin/bash
|
||||
...
|
||||
set -x # on
|
||||
...
|
||||
set +x # off
|
||||
...
|
||||
</code>
|
||||
|
||||
===== Portability considerations =====
|
||||
|
||||
''set'' and its basic behaviour and options are specified by POSIX(r). However, options that influence Bash-specific things are not portable, naturally.
|
||||
|
||||
===== See also =====
|
||||
|
||||
* Internal: [[commands:builtin:shopt | The shopt builtin command]]
|
65
original_source/commands/builtin/shift.txt
Normal file
65
original_source/commands/builtin/shift.txt
Normal file
@ -0,0 +1,65 @@
|
||||
====== The shift builtin command ======
|
||||
|
||||
===== Synopsis =====
|
||||
<code>shift [n]</code>
|
||||
|
||||
===== Description =====
|
||||
|
||||
The ''shift'' builtin command is used to "shift" the positional parameters by the given number ''n'' or by 1, if no number is given.
|
||||
|
||||
This means, the number and the position of the positional parameters are changed.
|
||||
The very first positional parameter is discarded, the second becomes the first one, etc.
|
||||
|
||||
Imagine the following set of positional parameters (''$1'' to ''$4''):
|
||||
|
||||
^1 |This |
|
||||
^2 |is |
|
||||
^3 |a |
|
||||
^4 |test |
|
||||
|
||||
When you use ''shift 1'', they will be changed to:
|
||||
|
||||
^1 |is |
|
||||
^2 |a |
|
||||
^3 |test |
|
||||
|
||||
The [[syntax:shellvars#special_parameters|special parameter]] ''$#'' will reflect the final number of positional parameters.
|
||||
|
||||
If the number given is 0, no changes are made to the positional parameters.
|
||||
|
||||
|
||||
==== Options ====
|
||||
|
||||
There are no options.
|
||||
|
||||
==== Return status ====
|
||||
|
||||
^Status ^Reason ^
|
||||
|0 |no error |
|
||||
|1 |non-numeric argument |
|
||||
|1 |given number (or the default 1) is bigger than the number of actually present positional parameters |
|
||||
|1 |given number is negative |
|
||||
|
||||
|
||||
===== Examples =====
|
||||
|
||||
===== Portability considerations =====
|
||||
|
||||
* The ''shift'' builtin command is specified by POSIX(r).
|
||||
* Many shells will throw a fatal error when attempting to ''shift'' more than the number of positional parameters. **POSIX does not require that behavior**. Bash (even in POSIX mode) and Zsh return 1 when there are no args, and no error output is produced unless the [[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: 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'
|
||||
no args
|
||||
</code> While, POSIX requires this behavior, it isn't very obvious and some shells don't do it correctly. To work around this, you can use something like:
|
||||
|
||||
<code> $ mksh -c 'f() { if ! ${1+false} && shift; then echo "$1"; else echo "no args"; fi; }; f'
|
||||
no args
|
||||
</code>
|
||||
<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'
|
||||
(no output)
|
||||
</code> The above mksh workaround will work in this case too.
|
||||
|
||||
===== See also =====
|
||||
|
46
original_source/commands/builtin/shopt.txt
Normal file
46
original_source/commands/builtin/shopt.txt
Normal file
@ -0,0 +1,46 @@
|
||||
====== The shopt builtin command ======
|
||||
|
||||
The ''shopt'' builtin manages [[internals:shell_options | shell options]], a set of boolean (''on''/''off'') configuration variables that control the behaviour of the shell.
|
||||
|
||||
===== Synopsis =====
|
||||
|
||||
<code>
|
||||
shopt [-pqsu] [-o] <OPTNAME...>
|
||||
</code>
|
||||
|
||||
===== Description =====
|
||||
|
||||
Note: Some of these options and other shell options can also be set with [[commands:builtin:set | the set builtin]].
|
||||
|
||||
==== Options ====
|
||||
|
||||
^Option^Description^
|
||||
|''-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**.|
|
||||
|''-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|
|
||||
|''-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.
|
||||
|
||||
==== Exit code ====
|
||||
|
||||
When listing options, the exit code is ''TRUE'' (0), if all options are enabled, ''FALSE'' otherwise.
|
||||
|
||||
When setting/unsetting an option, the exit code is ''TRUE'' unless the named option doesn't exitst.
|
||||
|
||||
===== Examples ======
|
||||
|
||||
Enable the ''nullglob'' option:
|
||||
<code>
|
||||
shopt -s nullglob
|
||||
</code>
|
||||
|
||||
===== Portability considerations ======
|
||||
|
||||
The ''shopt'' command is not portable accross different shells.
|
||||
|
||||
===== See also ======
|
||||
|
||||
* Internal: [[commands:builtin:set | The set builtin command]]
|
||||
* Internal: [[internals:shell_options | List of shell options]]
|
327
original_source/commands/builtin/source.txt
Normal file
327
original_source/commands/builtin/source.txt
Normal file
File diff suppressed because one or more lines are too long
1
original_source/commands/builtin/start.txt
Normal file
1
original_source/commands/builtin/start.txt
Normal file
@ -0,0 +1 @@
|
||||
====== Builtin Commands ======
|
327
original_source/commands/builtin/times.txt
Normal file
327
original_source/commands/builtin/times.txt
Normal file
File diff suppressed because one or more lines are too long
62
original_source/commands/builtin/trap.txt
Normal file
62
original_source/commands/builtin/trap.txt
Normal file
@ -0,0 +1,62 @@
|
||||
====== The trap builtin command ======
|
||||
|
||||
===== Synopsis =====
|
||||
<code>trap [-lp] [[ARGUMENT] SIGNAL]</code>
|
||||
|
||||
===== Description =====
|
||||
The ''trap'' command is used to "trap" signals and other events. In this context, "trapping" means to install handler code.
|
||||
|
||||
The shell code ''ARGUMENT'' is to be read and executed whenever the shell receives a signal or another event ''SIGNAL''. The given ''SIGNAL'' specification can be
|
||||
* the name of a signal with the SIG prefix, e.g. ''SIGTERM''
|
||||
* the name of a signal without the SIG prefix, e.g. ''TERM''
|
||||
* the number of a signal (see ''trap -l''), e.g. ''15''
|
||||
* the name or number of a special event (see table below), e.g. ''EXIT''
|
||||
|
||||
Without any options or operands, ''trap'' prints a list of installed traps in a reusable format (equivalent to the ''-p'' option).
|
||||
|
||||
Special ''ARGUMENT''s
|
||||
* if ''ARGUMENT'' is absent or ''-'' (dash), the signal/event handler is reset to its original value
|
||||
* if ''ARGUMENT'' is the null string, the signal/event is ignored
|
||||
|
||||
Special events
|
||||
^Name ^Code ^Description ^
|
||||
|''EXIT'' |0 |executed on shell exit |
|
||||
|''DEBUG'' | |executed before every simple command |
|
||||
|''RETURN'' | |executed when a shell function or a sourced code finishes executing |
|
||||
|''ERR'' | |executed each time a command's failure would cause the shell to exit when the [[commands:builtin:set|''-e'' option (''errexit'')]] is enabled |
|
||||
|
||||
==== Options ====
|
||||
|
||||
^Option ^Description ^
|
||||
|''-l'' |print a list of signal names and their corresponding numbers |
|
||||
|''-p'' |display the trap commands associated with each signal specification in a reusable format |
|
||||
|
||||
==== Return status ====
|
||||
|
||||
^Status ^Reason ^
|
||||
|0 |no error/success |
|
||||
|!=0 |invalid option |
|
||||
|!=0 |invalid signal specification |
|
||||
|
||||
===== Examples =====
|
||||
|
||||
==== List installed traps ====
|
||||
|
||||
<code>
|
||||
trap
|
||||
</code>
|
||||
|
||||
==== Ignore terminal interrupt (Ctrl-C, SIGINT) ====
|
||||
|
||||
<code>
|
||||
trap '' INT
|
||||
</code>
|
||||
|
||||
===== Portability considerations =====
|
||||
|
||||
* ''trap'' is specified by POSIX(R) without the ''-l'' and ''-p'' options
|
||||
* in POSIX(R), beside signals, only ''EXIT'' (0) is valid as an event
|
||||
|
||||
===== See also =====
|
||||
|
||||
* [[commands:builtin:set|the set command]] for the ''-e'' (''errexit'') option
|
327
original_source/commands/builtin/true.txt
Normal file
327
original_source/commands/builtin/true.txt
Normal file
File diff suppressed because one or more lines are too long
327
original_source/commands/builtin/typeset.txt
Normal file
327
original_source/commands/builtin/typeset.txt
Normal file
File diff suppressed because one or more lines are too long
150
original_source/commands/builtin/unset.txt
Normal file
150
original_source/commands/builtin/unset.txt
Normal file
@ -0,0 +1,150 @@
|
||||
====== The unset builtin command ======
|
||||
|
||||
===== Synopsis =====
|
||||
|
||||
<code>
|
||||
unset [-f|v] [-n] [NAME ...]
|
||||
</code>
|
||||
|
||||
===== Description =====
|
||||
|
||||
The ''unset'' builtin command is used to unset values and attributes of shell variables and functions.
|
||||
Without any option, ''unset'' tries to unset a variable first, then a function.
|
||||
|
||||
==== Options ====
|
||||
|
||||
^ Option ^ Description ^
|
||||
| ''-f'' | treats each ''NAME'' as a function name |
|
||||
| ''-v'' | treats each ''NAME'' as a variable name |
|
||||
| ''-n'' | treats each ''NAME'' as a name reference and unsets the variable itself rather than the variable it references |
|
||||
|
||||
==== Exit status ====
|
||||
|
||||
^Status ^Reason ^
|
||||
|0 |no error |
|
||||
|!=0 |invalid option |
|
||||
|!=0 |invalid combination of options (''-v'' **and** ''-f'') |
|
||||
|!=0 |a given ''NAME'' is read-only |
|
||||
|
||||
===== Examples =====
|
||||
|
||||
<code>
|
||||
unset -v EDITOR
|
||||
</code>
|
||||
|
||||
<code>
|
||||
unset -f myfunc1 myfunc2
|
||||
</code>
|
||||
|
||||
==== Scope ====
|
||||
|
||||
In bash, unset has some interesting properties due to its unique dynamic scope. If a local variable is both declared and unset (by calling unset on the local) from within the same function scope, then the variable appears unset to that scope and all child scopes until either returning from the function, or another local variable of the same name is declared underneath where the original variable was unset. In other words, the variable looks unset to everything until returning from the function in which the variable was set (and unset), at which point variables of the same name from higher scopes are uncovered and accessible once again.
|
||||
|
||||
If however unset is called from a child scope relative to where a local variable has been set, then the variable of the same name in the next-outermost scope becomes visible to its scope and all children - as if the variable that was unset was never set to begin with. This property allows looking upwards through the stack as variable names are unset, so long as unset and the local it unsets aren't together in the same scope level.
|
||||
|
||||
Here's a demonstration of this behavior.
|
||||
<code>
|
||||
#!/usr/bin/env bash
|
||||
|
||||
FUNCNEST=10
|
||||
|
||||
# Direct recursion depth.
|
||||
# Search up the stack for the first non-FUNCNAME[1] and count how deep we are.
|
||||
callDepth() {
|
||||
# Strip "main" off the end of FUNCNAME[@] if current function is named "main" and
|
||||
# Bash added an extra "main" for non-interactive scripts.
|
||||
if [[ main == !(!("${FUNCNAME[1]}")|!("${FUNCNAME[-1]}")) && $- != *i* ]]; then
|
||||
local -a 'fnames=("${FUNCNAME[@]:1:${#FUNCNAME[@]}-2}")'
|
||||
else
|
||||
local -a 'fnames=("${FUNCNAME[@]:1}")'
|
||||
fi
|
||||
|
||||
if (( ! ${#fnames[@]} )); then
|
||||
printf 0
|
||||
return
|
||||
fi
|
||||
|
||||
local n
|
||||
while [[ $fnames == ${fnames[++n]} ]]; do
|
||||
:
|
||||
done
|
||||
|
||||
printf -- $n
|
||||
}
|
||||
|
||||
# This function is the magic stack walker.
|
||||
unset2() {
|
||||
unset -v -- "$@"
|
||||
}
|
||||
|
||||
f() {
|
||||
local a
|
||||
if (( (a=$(callDepth)) <= 4 )); then
|
||||
(( a == 1 )) && unset a
|
||||
(( a == 2 )) && declare -g a='global scope yo'
|
||||
f
|
||||
else
|
||||
trap 'declare -p a' DEBUG
|
||||
unset2 a # declare -- a="5"
|
||||
unset a a # declare -- a="4"
|
||||
unset a # declare -- a="2"
|
||||
unset a # ./unset-tests: line 44: declare: a: not found
|
||||
: # declare -- a="global scope yo"
|
||||
fi
|
||||
}
|
||||
|
||||
a='global scope'
|
||||
f
|
||||
|
||||
# vim: set fenc=utf-8 ff=unix ts=4 sts=4 sw=4 ft=sh nowrap et:
|
||||
</code>
|
||||
output:
|
||||
<code>
|
||||
declare -- a="5"
|
||||
declare -- a="4"
|
||||
declare -- a="2"
|
||||
./unset-tests: line 44: declare: a: not found
|
||||
declare -- a="global scope yo"
|
||||
</code>
|
||||
|
||||
Some things to observe:
|
||||
* ''unset2'' is only really needed once. We remain 5 levels deep in ''f'''s for the remaining ''unset'' calls, which peel away the outer layers of ''a'''s.
|
||||
* Notice that the "a" is unset using an ordinary unset command at recursion depth 1, and subsequently calling unset reveals a again in the global scope, which has since been modified in a lower scope using declare -g.
|
||||
* Declaring a global with declare -g bypasses all locals and sets or modifies the variable of the global scope (outside of all functions). It has no affect on the visibility of the global.
|
||||
* This doesn't apply to individual array elements. If two local arrays of the same name appear in different scopes, the entire array of the inner scope needs to be unset before any elements of the outer array become visible. This makes "unset" and "unset2" identical for individual array elements, and for arrays as a whole, unset and unset2 behave as they do for scalar variables.
|
||||
|
||||
==== Args ====
|
||||
Like several other Bash builtins that take parameter names, unset expands its arguments.
|
||||
<code>
|
||||
~ $ ( a=({a..d}); unset 'a[2]'; declare -p a )
|
||||
declare -a a='([0]="a" [1]="b" [3]="d")'
|
||||
</code>
|
||||
|
||||
As usual in such cases, it's important to quote the args to avoid accidental results such as globbing.
|
||||
<code>
|
||||
~ $ ( 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]'
|
||||
+ declare -p a
|
||||
declare -a a='([0]="a" [3]="d")'
|
||||
</code>
|
||||
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.
|
||||
|
||||
<code>
|
||||
~ $ ( unset -v 'a[$(echo a was set >&2)0]' )
|
||||
~ $ ( a=(); unset -v 'a[$(echo a was set >&2)0]' )
|
||||
a was set
|
||||
</code>
|
||||
|
||||
===== Portability considerations =====
|
||||
|
||||
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>
|
||||
Therefore, it is recommended to explicitly specify ''-f'' or ''-v'' when using ''unset''. Also, I prefer it as a matter of style.
|
||||
|
||||
===== See also =====
|
||||
|
||||
* [[commands:builtin:declare]]
|
||||
* [[commands:builtin:unset]]
|
||||
* [[http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_29 | POSIX ''unset'' utility]]
|
39
original_source/commands/builtin/wait.txt
Normal file
39
original_source/commands/builtin/wait.txt
Normal file
@ -0,0 +1,39 @@
|
||||
====== The wait builtin command ======
|
||||
|
||||
===== Synopsis =====
|
||||
<code>wait [-f] [-n] [-p VARNAME] [ID...]</code>
|
||||
|
||||
===== Description =====
|
||||
The ''wait'' builtin command is used to wait for job completion and return exit status.
|
||||
|
||||
* if ''ID'' is a job specification, it waits for all processes in the pipeline of this job
|
||||
* waits for a specific job (asynchronous command) and report its exit status if one or more ''ID'' is given
|
||||
* waits for all running jobs (asynchronous commands)
|
||||
* waits for "the next" job (''-n'' option)
|
||||
* waits for termination instead of status change (''-f'' option)
|
||||
|
||||
''ID'' may be an operating system process identifier or a shell job specification.
|
||||
|
||||
==== Options ====
|
||||
|
||||
^ Option ^ Description ^
|
||||
| ''-n'' | Waits for "the next" child to exit (as opposed to "all children" without this option). Accepts a list of IDs (jobs) |
|
||||
| ''-f'' | Waits for the termination of the given ''ID'' (instead of waiting for a status change only) |
|
||||
| ''-p VARNAME'' | When waiting for a list (-n) or all jobs, writes the job ID to the job that was actually terminated into the variable ''VARNAME'' |
|
||||
|
||||
==== Return status ====
|
||||
|
||||
The return status is the return status of the job waited for, or
|
||||
|
||||
^Status ^Reason ^
|
||||
|0 |waited for all jobs in shell's job list |
|
||||
|1 |the given ''ID'' is not a valid job or process ID |
|
||||
|
||||
===== Examples =====
|
||||
|
||||
|
||||
===== Portability considerations =====
|
||||
|
||||
|
||||
===== See also =====
|
||||
|
488
original_source/commands/classictest.txt
Normal file
488
original_source/commands/classictest.txt
Normal file
@ -0,0 +1,488 @@
|
||||
====== The classic test command ======
|
||||
|
||||
''test <EXPRESSION>''
|
||||
|
||||
''[ <EXPRESSION> ]''
|
||||
|
||||
===== General syntax =====
|
||||
|
||||
This command allows you to do various tests and sets its exit code to 0 (//TRUE//) or 1 (//FALSE//) whenever such a test succeeds or not. Using this exit code, it's possible to let Bash react on the result of such a test, here by using the command in an if-statement:
|
||||
<code>
|
||||
#!/bin/bash
|
||||
# test if /etc/passwd exists
|
||||
|
||||
if test -e /etc/passwd; then
|
||||
echo "Alright man..." >&2
|
||||
else
|
||||
echo "Yuck! Where is it??" >&2
|
||||
exit 1
|
||||
fi
|
||||
</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''").
|
||||
|
||||
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:
|
||||
<code>
|
||||
#!/bin/bash
|
||||
# test if /etc/passwd exists
|
||||
|
||||
if [ -e /etc/passwd ]; then
|
||||
echo "Alright man..." >&2
|
||||
else
|
||||
echo "Yuck! Where is it??" >&2
|
||||
exit 1
|
||||
fi
|
||||
</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!**
|
||||
|
||||
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>
|
||||
#!/bin/bash
|
||||
|
||||
mymusic="/data/music/Van Halen/Van Halen - Right Now.mp3"
|
||||
|
||||
if [ -e "$mymusic" ]; then
|
||||
echo "Let's rock" >&2
|
||||
else
|
||||
echo "No music today, sorry..." >&2
|
||||
exit 1
|
||||
fi
|
||||
</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...]]
|
||||
|
||||
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:
|
||||
<code>
|
||||
[ "$mystring"!="test" ]
|
||||
</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''!
|
||||
|
||||
Well, I addressed several basic rules, now let's see what the test-command can do for you. The Bash test-types can be split into several sections: **file tests**, **string tests**, **arithmetic tests**, **misc tests**.
|
||||
Below, the tests marked with :!: are non-standard tests (i.e. not in SUS/POSIX/etc..).
|
||||
|
||||
===== File tests =====
|
||||
This section probably holds the most tests, I'll list them in some logical order. Since Bash 4.1, all tests related to permissions respect ACLs, if the underlying filesystem/OS supports them.
|
||||
|
||||
^ Operator syntax ^ Description | |
|
||||
| **-a** <FILE> | True if <FILE> exists. :!: (not recommended, may collide with ''-a'' for ''AND'', see below) | |
|
||||
| **-e** <FILE> | True if <FILE> exists. | |
|
||||
| **-f** <FILE> | True, if <FILE> exists and is a **regular** file. | |
|
||||
| **-d** <FILE> | True, if <FILE> exists and is a **directory**. ||
|
||||
| **-c** <FILE> | True, if <FILE> exists and is a **character special** file. | |
|
||||
| **-b** <FILE> | True, if <FILE> exists and is a **block special** file. | |
|
||||
| **-p** <FILE> | True, if <FILE> exists and is a **named pipe** (FIFO). | |
|
||||
| **-S** <FILE> | True, if <FILE> exists and is a **socket** file. | |
|
||||
| **-L** <FILE> | True, if <FILE> exists and is a **symbolic link**. | |
|
||||
| **-h** <FILE> | True, if <FILE> exists and is a **symbolic link**. | |
|
||||
| **-g** <FILE> | True, if <FILE> exists and has **sgid bit** set. | |
|
||||
| **-u** <FILE> | True, if <FILE> exists and has **suid bit** set. | |
|
||||
| **-r** <FILE> | True, if <FILE> exists and is **readable**. | |
|
||||
| **-w** <FILE> | True, if <FILE> exists and is **writable**. | |
|
||||
| **-x** <FILE> | True, if <FILE> exists and is **executable**. | |
|
||||
| **-s** <FILE> | True, if <FILE> exists and has size bigger than 0 (**not empty**). | |
|
||||
| **-t** <fd> | True, if file descriptor <fd> is open and refers to a terminal. | |
|
||||
| <FILE1> **-nt** <FILE2> | True, if <FILE1> is **newer than** <FILE2> (mtime). :!: | |
|
||||
| <FILE1> **-ot** <FILE2> | True, if <FILE1> is **older than** <FILE2> (mtime). :!: | |
|
||||
| <FILE1> **-ef** <FILE2> | True, if <FILE1> and <FILE2> refer to the **same device and inode numbers**. :!: | |
|
||||
|
||||
|
||||
|
||||
===== String tests =====
|
||||
^Operator syntax^Description^
|
||||
|**-z** <STRING>|True, if <STRING> is **empty**.|
|
||||
|**-n** <STRING>|True, if <STRING> is **not empty** (this is the default operation).|
|
||||
|<STRING1> **=** <STRING2>|True, if the strings are **equal**.|
|
||||
|<STRING1> **!=** <STRING2>|True, if the strings are **not equal**.|
|
||||
|<STRING1> **<** <STRING2>|True if <STRING1> sorts **before** <STRING2> lexicographically (pure ASCII, not current locale!). Remember to escape! Use ''\<''|
|
||||
|<STRING1> **>** <STRING2>|True if <STRING1> sorts **after** <STRING2> lexicographically (pure ASCII, not current locale!). Remember to escape! Use ''\>''|
|
||||
|
||||
===== Arithmetic tests =====
|
||||
^Operator syntax^Description^
|
||||
|<INTEGER1> **-eq** <INTEGER2>|True, if the integers are **equal**.|
|
||||
|<INTEGER1> **-ne** <INTEGER2>|True, if the integers are **NOT equal**.|
|
||||
|<INTEGER1> **-le** <INTEGER2>|True, if the first integer is **less than or equal** second one.|
|
||||
|<INTEGER1> **-ge** <INTEGER2>|True, if the first integer is **greater than or equal** second one.|
|
||||
|<INTEGER1> **-lt** <INTEGER2>|True, if the first integer is **less than** second one.|
|
||||
|<INTEGER1> **-gt** <INTEGER2>|True, if the first integer is **greater than** second one.|
|
||||
|
||||
===== Misc syntax =====
|
||||
^ Operator syntax ^ Description ^
|
||||
| <TEST1> **-a** <TEST2> | True, if <TEST1> **and** <TEST2> are true (AND). Note that ''-a'' also may be used as a file test (see above) |
|
||||
| <TEST1> **-o** <TEST2> | True, if either <TEST1> **or** <TEST2> is true (OR). |
|
||||
| **!** <TEST> | True, if <TEST> is **false** (NOT). |
|
||||
| **(** <TEST> **)** | Group a test (for precedence). **Attention:** In normal shell-usage, the "(" and ")" must be escaped; use "\(" and "\)"! |
|
||||
| **-o** <OPTION_NAME> | True, if the [[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. |
|
||||
| **-R** <VARIABLENAME> | True if the variable <VARIABLENAME> has been set and is a nameref variable (since 4.3-alpha) |
|
||||
|
||||
|
||||
|
||||
===== Number of Arguments Rules =====
|
||||
|
||||
The ''test'' builtin, especially hidden under its ''['' name, may seem simple but is in fact **causing a lot of trouble sometimes**.
|
||||
One of the difficulty is that the behaviour of ''test'' not only depends on its arguments but also on the **number of its arguments**.
|
||||
|
||||
Here are the rules taken from the manual (__**Note:**__ This is for the command ''test'', for ''['' the number of arguments is calculated without the final '']'', for example ''[ ]'' follows the "zero arguments" rule):
|
||||
|
||||
* **0 arguments**
|
||||
* The expression is false.
|
||||
* **1 argument**
|
||||
* The expression is true if, and only if, the argument is not null
|
||||
* **2 arguments**
|
||||
* If the first argument is ''!'' (exclamation mark), the expression is true if, and only if, the second argument is null
|
||||
* If the first argument is one of the unary conditional operators listed above under the syntax rules, the expression is true if the unary test is true
|
||||
* If the first argument is not a valid unary conditional operator, the expression is false
|
||||
* **3 arguments**
|
||||
* If the second argument is one of the binary conditional operators listed above under the syntax rules, the result of the expression is the result of the binary test using the first and third arguments as operands
|
||||
* If the first argument is ''!'', the value is the negation of the two-argument test using the second and third arguments
|
||||
* If the first argument is exactly ''('' and the third argument is exactly '')'', the result is the one-argument test of the second argument. Otherwise, the expression is false. The ''-a'' and ''-o'' operators are considered binary operators in this case (**Attention:** This means the operator ''-a'' is not a file operator in this case!)
|
||||
* **4 arguments**
|
||||
* If the first argument is ''!'', the result is the negation of the three-argument expression composed of the remaining arguments. Otherwise, the expression is parsed and evaluated according to precedence using the rules listed above
|
||||
* **5 or more arguments**
|
||||
* The expression is parsed and evaluated according to precedence using the rules listed above
|
||||
|
||||
|
||||
These rules may seem complex, but it's not so bad in practice. Knowing them might help you to explain some of the "unexplicable" behaviours you might encounter:
|
||||
<code>
|
||||
var=""
|
||||
if [ -n $var ]; then echo "var is not empty"; fi
|
||||
</code>
|
||||
|
||||
This code prints "var is not empty", even though ''-n something'' is supposed to be true if ''$var'' is not empty - **why?**
|
||||
|
||||
Here, as ''$var'' is **not quoted**, word splitting occurs and ''$var'' results in actually nothing (Bash removes it from the command's argument list!). So the test is in fact ''[ -n ]'' **and falls into the "one argument" rule**, the only argument is "-n" which is not null and so the test returns true. The solution, as usual, is to **quote the parameter expansion**: ''[ -n "$var" ]'' so that the test has always 2 arguments, even if the second one is the null string.
|
||||
|
||||
These rules also explain why, for instance, -a and -o can have several meanings.
|
||||
|
||||
===== AND and OR =====
|
||||
|
||||
|
||||
|
||||
|
||||
==== The Prefered Way ====
|
||||
|
||||
The way often recommended to logically connect several tests with AND and OR is to use **several single test commands** and to **combine** them with the shell ''&&'' and ''||'' **list control operators**.
|
||||
|
||||
See this:
|
||||
<code>
|
||||
if [ -n "$var"] && [ -e "$var"]; then
|
||||
echo "\$var is not null and a file named $var exists!"
|
||||
fi
|
||||
</code>
|
||||
|
||||
The return status of AND and OR lists is the exit status of the last command executed in the list
|
||||
* With ''command1 && command2'', ''command2'' is executed if, and only if, ''command1'' returns an exit status of zero (true)
|
||||
* With ''command1 ││ command2'', ''command2'' is executed if, and only if, ''command1'' returns a non-zero exit status (false)
|
||||
|
||||
|
||||
|
||||
==== The other way: -a and -o ====
|
||||
|
||||
The logical operators AND and OR for the test-command itself are ''-a'' and ''-o'', thus:
|
||||
<code>
|
||||
if [ -n "$var" -a -e "$var" ] ; then
|
||||
echo "\$var is not null and a file named $var exists"
|
||||
fi
|
||||
</code>
|
||||
|
||||
They are **not** ''&&'' or ''||'':
|
||||
<code>
|
||||
$ if [ -n "/tmp" && -d "/tmp"]; then echo true; fi # DOES NOT WORK
|
||||
bash: [: missing `]'
|
||||
</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**:
|
||||
* ''[ -n "/tmp"''
|
||||
* ''-d "/tmp" ]''
|
||||
...which **must** fail.
|
||||
|
||||
|
||||
|
||||
|
||||
==== Why you should avoid using -a and -o ====
|
||||
|
||||
=== If portability is a concern ===
|
||||
|
||||
POSIX(r)/SUSv3 does **not** specify the behaviour of ''test'' in cases where there are more than 4 arguments. If you write a script that might not be executed by Bash, the behaviour might be different! ((<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 ===
|
||||
|
||||
Let's say, we want to check the following two things (AND):
|
||||
- if a string is null (empty)
|
||||
- if a command produced an output
|
||||
Let's see:
|
||||
<code>
|
||||
if [ -z "false" -a -z "$(echo I am executed >&2)" ] ; then ...
|
||||
</code>
|
||||
=> The arguments are all expanded **before** ''test'' runs, thus the echo-command **is executed**.
|
||||
|
||||
<code>
|
||||
if [ -z "false" ] && [ -z "$(echo I am not executed >&2)" ]; then...
|
||||
</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**.
|
||||
|
||||
__**Note:**__ In my opinion, ''-a'' and ''-o'' are also less readable ''[pgas]''
|
||||
|
||||
==== Precedence and Parenthesis ====
|
||||
|
||||
Take care if you convert your scripts from using ''-a'' and ''-o'' to use the list way (''&&'' and ''||''):
|
||||
* in the test-command rules, ''-a'' has **precedence over** ''-o''
|
||||
* in the shell grammar rules, ''&&'' and ''||'' have **equal precedence**
|
||||
That means, **you can get different results**, depending on the manner of use:
|
||||
<code>
|
||||
$ if [ "true" ] || [ -e /does/not/exist ] && [ -e /does/not/exist ]; then echo true; else echo false; fi
|
||||
false
|
||||
|
||||
$ if [ "true" -o -e /does/not/exist -a -e /does/not/exist ]; then echo true; else echo false;fi
|
||||
true
|
||||
</code>
|
||||
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:
|
||||
<code>
|
||||
$ if [ "true" ] || { [ -e /does/not/exist ] && [ -e /does/not/exist ] ;} ; then echo true; else echo false; fi
|
||||
true
|
||||
</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:
|
||||
<code>
|
||||
$ if [ \( "true" -o -e /does/not/exist \) -a -e /does/not/exist ]; then echo true; else echo false; fi
|
||||
false
|
||||
|
||||
# equivalent, but less readable IMHO:
|
||||
$ if [ '(' "true" -o -e /does/not/exist ')' -a -e /does/not/exist ]; then echo true; else echo false; fi
|
||||
false
|
||||
</code>
|
||||
|
||||
|
||||
|
||||
===== NOT =====
|
||||
|
||||
As for AND and OR, there are 2 ways to negate a test with the shell keyword ''!'' or passing ''!'' as an argument to ''test''.
|
||||
|
||||
|
||||
Here ''!'' negates the exit status of the command ''test'' which is 0 (true), and the else part is executed:
|
||||
<code>
|
||||
if ! [ -d '/tmp' ]; then echo "/tmp doesn't exists"; else echo "/tmp exists"; fi
|
||||
</code>
|
||||
|
||||
Here the ''test'' command itself exits with status 1 (false) and the else is also executed:
|
||||
<code>
|
||||
if [ ! -d '/tmp' ]; then echo "/tmp doesn't exists"; else echo "/tmp exists"; fi
|
||||
</code>
|
||||
|
||||
Unlike for AND and OR, both methods for NOT have an identical behaviour, at least for doing one single test.
|
||||
|
||||
===== Pitfalls summarized =====
|
||||
|
||||
In this section you will get all the mentioned (and maybe more) possible pitfalls and problems in a summary.
|
||||
|
||||
==== General ====
|
||||
|
||||
Here's the copy of a mail on bug-bash list. A user asking a question about using the test command in Bash, **he's talking about a problem, which you may have already had yourself**:
|
||||
<code>
|
||||
From: (PROTECTED)
|
||||
Subject: -d option not working. . .?
|
||||
Date: Tue, 11 Sep 2007 21:51:59 -0400
|
||||
To: bug-bash@gnu.org
|
||||
|
||||
Hi All,
|
||||
|
||||
I've got a script that I'm trying to set up, but it keeps telling me
|
||||
that "[-d command not found". Can someone please explain what is
|
||||
wrong with this?:
|
||||
|
||||
|
||||
|
||||
|
||||
#!/bin/sh
|
||||
|
||||
for i in $*
|
||||
do
|
||||
{
|
||||
if [-d $i]
|
||||
then
|
||||
echo "$i is a directory! Yay!"
|
||||
else
|
||||
echo "$i is not a directory!"
|
||||
fi
|
||||
}
|
||||
done
|
||||
|
||||
|
||||
|
||||
Regards
|
||||
</code>
|
||||
|
||||
See the problem regarding the used test-command (the other potential problems are not of interest here)?
|
||||
<code>
|
||||
[-d $i]
|
||||
</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:
|
||||
|
||||
<code>
|
||||
From: Bob Proulx (EMAIL PROTECTED)
|
||||
Subject: Re: -d option not working. . .?
|
||||
Date: Wed, 12 Sep 2007 10:32:35 -0600
|
||||
To: bug-bash@gnu.org
|
||||
|
||||
> (QUOTED TEXT WAS REMOVED)
|
||||
|
||||
The shell is first and foremost a way to launch other commands. The
|
||||
syntax is simply "if" followed by a command-list, (e.g. if /some/foo;
|
||||
or even if cmd1; cmd2; cmd3; then). Plus the '( ... )' syntax is
|
||||
already taken by the use of starting a subshell.
|
||||
|
||||
As I recall in the original shell language the file test operator was
|
||||
not built-in. It was provided by the standalone '/bin/test' command.
|
||||
The result was effectively this:
|
||||
|
||||
if /bin/test -d somedir
|
||||
|
||||
Although the full path /bin/test was never used. I showed it that way
|
||||
here for emphasis that following the 'if' statement is a command list.
|
||||
Normally it would simply have been:
|
||||
|
||||
if test -d somedir
|
||||
|
||||
Of course that is fine and for the best portability that style is
|
||||
still the recommended way today to use the test command. But many
|
||||
people find that it looks different from other programming languages.
|
||||
To make the test operator (note I mention the test operator and not
|
||||
the shell language, this is a localized change not affecting the
|
||||
language as a whole) look more like other programming languages the
|
||||
'test' program was coded to ignore the last argument if it was a ']'.
|
||||
Then a copy of the test program could be used as the '[' program.
|
||||
|
||||
...modify /bin/test to ignore ']' as last argument...
|
||||
cp /bin/test /bin/[
|
||||
|
||||
This allows:
|
||||
|
||||
if [ -d somedir ]
|
||||
|
||||
Doesn't that look more normal? People liked it and it caught on. It
|
||||
was so popular that both 'test' and '[' are now shell built-ins. They
|
||||
don't launch an external '/bin/test' program anymore. But they *used*
|
||||
to launch external programs. Therefore argument parsing is the same
|
||||
as if they still did launch an external program. This affects
|
||||
argument parsing.
|
||||
|
||||
it test -f *.txt
|
||||
test: too many arguments
|
||||
|
||||
Oops. I have twenty .txt files and so test got one -f followed by the
|
||||
first file followed by the remaining files. (e.g. test -f 1.txt 2.txt
|
||||
3.txt 4.txt)
|
||||
|
||||
if test -d $file
|
||||
test: argument expected
|
||||
|
||||
Oops. I meant to set file.
|
||||
|
||||
file=/path/some/file
|
||||
if test -d $file
|
||||
|
||||
If variables such as that are not set then they wlll be expanded by
|
||||
the shell before passing them to the (possibly external) command and
|
||||
disappear entirely. This is why test arguments should always be quoted.
|
||||
|
||||
if test -d "$file"
|
||||
if [ -d "$file" ]
|
||||
|
||||
Actually today test is defined that if only one argument is given as
|
||||
in this case "test FOO" then then test returns true if the argument is
|
||||
non-zero in text length. Because "-d" is non-zero length "test -d" is
|
||||
true. The number of arguments affects how test parses the args. This
|
||||
avoids a case where depending upon the data may look like a test
|
||||
operator.
|
||||
|
||||
DATA="something"
|
||||
if test "$DATA" # true, $DATA is non-zero length
|
||||
|
||||
DATA=""
|
||||
if test "$DATA" # false, $DATA is zero length
|
||||
|
||||
But the problem case is how should test handle an argument that looks
|
||||
like an operator? This used to generate errors but now because it is
|
||||
only one argument is defined to be the same as test -n $DATA.
|
||||
|
||||
DATA="-d"
|
||||
if test "$DATA" # true, $DATA is non-zero length
|
||||
if test -d # true, same as previous case.
|
||||
|
||||
Because test and [ are possibly external commands all of the parts of
|
||||
them are chosen to avoid shell metacharacters. The Fortran operator
|
||||
naming was well known at the time (e.g. .gt., .eq., etc.) and was
|
||||
pressed into service for the shell test operator too. Comming from
|
||||
Fortran using -gt, -eq, etc. looked very normal.
|
||||
|
||||
Incorrect use generating unlikely to be intended results:
|
||||
|
||||
if test 5 > 2 # true, "5" is non-zero length, creates file named "2"
|
||||
|
||||
Intended use:
|
||||
|
||||
if test 5 -gt 2 # true (and no shell meta characters needing quoting)
|
||||
|
||||
Then much later, sometime in the mid 1980's, the Korn sh decided to
|
||||
improve upon this situation. A new test operator was introduced.
|
||||
This one was always a shell built-in and therefore could act upon the
|
||||
shell arguments directly. This is '[[' which is a shell keyword.
|
||||
(Keyword, metacharacters, builtins, all are different.) Because the
|
||||
shell processes [[ internally all arguments are known and do not need
|
||||
to be quoted.
|
||||
|
||||
if [[ -d $file ]] # okay
|
||||
if [[ 5 > 2 ]] # okay
|
||||
|
||||
I am sure that I am remembering a detail wrong but hopefully this is
|
||||
useful as a gentle introduction and interesting anyway.
|
||||
|
||||
Bob
|
||||
</code>
|
||||
|
||||
I hope this text protects you a bit from stepping from one pitfall into the next.
|
||||
|
||||
I find it very interesting and informative, that's why I quoted it here. Many thanks, Bob, also for the permission to copy the text here!
|
||||
|
||||
===== Code examples =====
|
||||
|
||||
==== Snipplets ====
|
||||
|
||||
Some code snipplets follow, different ways of shell reaction is used.
|
||||
|
||||
* **check if a variable is defined/non-NULL**
|
||||
* ''test "$MYVAR"''
|
||||
* ''[ "$MYVAR" ]''
|
||||
* **Note:** There are possibilities to make a difference if a variable is //undefined// or //NULL// - see [[syntax:pe#use_an_alternate_value|Parameter Expansion - Using an alternate value]]
|
||||
* **check if a directory exists, if not, create it**
|
||||
* ''test ! -d /home/user/foo && mkdir /home/user/foo''
|
||||
* ''[ ! -d /home/user/foo ] && mkdir /home/user/foo''
|
||||
* ''if [ ! -d /home/user/foo ]; then mkdir /home/user/foo; fi''
|
||||
* **check if minimum one parameter was given, and that one is "Hello"**
|
||||
* ''test $# -ge 1 -a "$1" = "Hello" || exit 1''
|
||||
* ''[ $# -ge 1 ] && [ "$1" = "Hello" ] || exit 1'' (see [[syntax:basicgrammar#lists | lists description]])
|
||||
|
||||
|
||||
|
||||
==== Listing directories ====
|
||||
|
||||
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>
|
||||
for fn in *; do
|
||||
[ -d "$fn" ] && echo "$fn"
|
||||
done
|
||||
</code>
|
||||
|
||||
|
||||
===== See also =====
|
||||
|
||||
* Internal: [[syntax:ccmd:conditional_expression | conditional expression]] (aka "the new test command")
|
||||
* Internal: [[syntax:ccmd:if_clause | the if-clause]]
|
327
original_source/commands/d_.foldername.txt
Normal file
327
original_source/commands/d_.foldername.txt
Normal file
File diff suppressed because one or more lines are too long
2
original_source/commands/start.txt
Normal file
2
original_source/commands/start.txt
Normal file
@ -0,0 +1,2 @@
|
||||
====== Commands ======
|
||||
|
11
original_source/dict/terms/directory.txt
Normal file
11
original_source/dict/terms/directory.txt
Normal file
@ -0,0 +1,11 @@
|
||||
====== Directory ======
|
||||
|
||||
In terms of UNIX(r), a directory is a special file which contains a list of [[dict:terms:hardlink | hardlinks]] to other files. These other files also can be directories of course, so it's possible to create a "hierarchy of directories" - the UNIX(r)-typical filesystem structure.
|
||||
|
||||
The structure begins at the special directory ''/'' (root directory) and all other directory entries are **subdirectories** of it.
|
||||
|
||||
|
||||
===== See also =====
|
||||
* [[dict:terms:hardlink | hardlink]]
|
||||
* [[dict:terms:file | file]]
|
||||
* [[dict:terms:special_file | special file]]
|
20
original_source/dict/terms/end_of_options.txt
Normal file
20
original_source/dict/terms/end_of_options.txt
Normal file
@ -0,0 +1,20 @@
|
||||
====== End of Options ======
|
||||
|
||||
The options of UNIX(r) utilities usually are introduced with a dash (''-'') character.
|
||||
|
||||
This is problematic when a non-option argument has to be specified that begins with a dash. A common example for this are filenames.
|
||||
|
||||
Many utilities use the convention to specify two consecutive dashes (''<nowiki>--</nowiki>'') to signal "end of options at this point". Beyond this tag, no options are processed anymore, even if an argument begins with a dash.
|
||||
|
||||
Example: You want to list (''ls'') the file with the name ''-hello''. With common option processing, this could end up in the ls-options ''-h'', ''-e'', ''-l'' and ''-o'' and probably in an error message about invalid options. You use this to avoid the wrong option processing:
|
||||
<code>
|
||||
ls -- -hello
|
||||
</code>
|
||||
|
||||
POSIX(r) specifies that every utility should follow this rule (see ch. [[https://pubs.opengroup.org/onlinepubs/9699919799.2013edition/basedefs/V1_chap12.html|12.2 Utility Syntax Guidelines]]), except
|
||||
* ''echo'' (historical reasons)
|
||||
* ''test'' (obvious parsing reasons)
|
||||
|
||||
===== See also =====
|
||||
|
||||
* Scripting article, internal: [[howto:getopts_tutorial]]
|
78
original_source/dict/terms/exit_status.txt
Normal file
78
original_source/dict/terms/exit_status.txt
Normal file
@ -0,0 +1,78 @@
|
||||
====== Exit Status ======
|
||||
|
||||
* exit code
|
||||
* return status
|
||||
|
||||
===== Purpose =====
|
||||
|
||||
The exit status is a numeric value that is returned by a program to the calling program or shell. In C programs, this is represented by the return value of the ''main()'' function or the value you give to ''exit(3)''. The only part of the number that matters are the least significant 8 bits, which means there are only values from 0 to 255.
|
||||
|
||||
In the shell, every operation generates an exit status (return status), even if no program is called. An example for such an operation is a redirection.
|
||||
|
||||
The parameter to the
|
||||
* ''exit'' (exit the shell/script)
|
||||
* ''return'' (return from a function)
|
||||
builtin commands serve the purpose of giving the exit status to the calling component.
|
||||
|
||||
This - and only this - makes it possible to determinate the success or failure of an operation. For scripting, always set exit codes.
|
||||
|
||||
===== Values =====
|
||||
|
||||
The code is a number between 0 and 255, where the part from 126 to 255 is reserved to be used by the Bash shell directly or for special purposes, like reporting a termination by a signal:
|
||||
|
||||
^ Code ^ Description ^
|
||||
| 0 | success |
|
||||
| 1-255 | failure (in general) |
|
||||
| 126 | the requested command (file) can't be executed (but was found) |
|
||||
| 127 | command (file) not found |
|
||||
| 128 | according to ABS it's used to report an invalid argument to the exit builtin, but I wasn't able to verify that in the source code of Bash (see code 255) |
|
||||
| 128 + N | the shell was terminated by the signal N (also used like this by various other programs) |
|
||||
| 255 | wrong argument to the exit builtin (see code 128) |
|
||||
|
||||
The lower codes 0 to 125 are not reserved and may be used for whatever the program likes to report. A value of **0 means successful** termination, a value **not 0 means unsuccessful** termination. This behavior (== 0, != 0) is also what Bash reacts on in some code flow control statements like ''if'' or ''while''.
|
||||
|
||||
===== Portability =====
|
||||
|
||||
Tables of shell behavior involving non-portable side-effects or common bugs with exit statuses. Note heirloom doesn't support pipeline negation (''! pipeline'').
|
||||
|
||||
==== Misc ====
|
||||
|
||||
^ test ^ bash\\ 4.2.45 ^ bash\\ (POSIX) ^ zsh 5.0.2\\ (emulate ksh) ^ ksh93\\ 93v- 2013-03-18 ^ mksh\\ R44 2013/02/24 ^ posh\\ 0.11 ^ dash\\ 0.5.7.3 ^ busybox\\ 1.2.1 ^ heirloom\\ 050706 ^
|
||||
| <code>:; : `false` `echo $? >&2`</code> | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 1 |
|
||||
| <code>false; eval; echo $?</code> | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 |
|
||||
| <code>x=`false` eval echo \$?</code> | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 1 |
|
||||
| <code>eval echo \$? <&0`false`</code> | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 1 |
|
||||
| <code>while :; do ! break; done; echo $?</code> | 1 | 1 | 1 | 1 | 0 | 0 | 1 | 1 | - |
|
||||
| [[https://lists.gnu.org/archive/html/bug-bash/2010-09/msg00009.html | discussion]]<code>false; : | echo $?</code>| 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 |
|
||||
| <code>(exit 2); for x in "`exit 3`"; do echo $?; done</code> | 3 | 3 | 3 | 3 | 2 | 2 | 0 | 0 | 3 |
|
||||
|
||||
==== functions ====
|
||||
Measuring side-effects during the function call, during return, and transparency of the return builtin.
|
||||
|
||||
^ test ^ bash ^ bash\\ (POSIX) ^ zsh\\ (emulate ksh) ^ ksh93 ^ mksh ^ posh ^ dash ^ busybox ^ heirloom ^
|
||||
| <code>f() { echo $?; }; :; f `false`</code> | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 1 |
|
||||
| <code>f() { return; }; false; f; echo $?</code> | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 |
|
||||
| <code>f() { return $?; }; false; f; echo $?</code> | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
|
||||
| <code>f() { ! return; }; f; echo $?</code> | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 1 | - |
|
||||
| <code>f() { ! return; }; false; f; echo $?</code> | 1 | 1 | 0 | 0 | 1 | 1 | 0 | 0 | - |
|
||||
| <code>f() { return; }; x=`false` f; echo $?</code> | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 |
|
||||
| <code>f() { return; }; f <&0`false`; echo $?</code> | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 1 |
|
||||
| <code>f() { x=`false` return; }; f; echo $?</code> | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 1 |
|
||||
| <code>f() { return <&0`false`; }; f; echo $?</code> | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 1 |
|
||||
| <code>f() { x=`false` return <&0`false`; }; f; echo $?</code> | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 1 |
|
||||
==== case..esac ====
|
||||
Statuses measured within the command and after, with matching and non-matching patterns.
|
||||
|
||||
^ test ^ bash ^ bash\\ (POSIX) ^ zsh\\ (emulate ksh) ^ ksh93 ^ mksh ^ posh ^ dash ^ busybox ^ heirloom ^
|
||||
| <code>(exit 2); case x in x) echo $?;; esac</code> | 2 | 2 | 0 | 2 | 2 | 2 | 0 | 0 | 2 |
|
||||
| <code>(exit 2); case `exit 3`x in x) echo $?;; esac</code> | 3 | 3 | 0 | 3 | 2 | 2 | 0 | 0 | 3 |
|
||||
| <code>(exit 2); case x in `exit 4`x) echo $?;; esac</code> | 4 | 4 | 4 | 4 | 2 | 2 | 0 | 0 | 4 |
|
||||
| <code>(exit 2); case `exit 3`x in `exit 4`x) echo $?;; esac</code> | 4 | 4 | 4 | 4 | 2 | 2 | 0 | 0 | 4 |
|
||||
| <code>(exit 2); case x in x);; esac; echo $?</code> | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 2 |
|
||||
| <code>(exit 2); case x in "");; esac; echo $?</code> | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 2 |
|
||||
| <code>(exit 2); case `exit 3`x in x);; esac; echo $?</code> | 0 | 0 | 0 | 3 | 0 | 0 | 0 | 0 | 3 |
|
||||
| <code>(exit 2); case `exit 3`x in "");; esac; echo $?</code> | 0 | 0 | 0 | 3 | 0 | 0 | 0 | 0 | 3 |
|
||||
| <code>(exit 2); case x in `exit 4`x);; esac; echo $?</code> | 0 | 0 | 0 | 4 | 0 | 0 | 0 | 0 | 4 |
|
||||
| <code>(exit 2); case x in `exit 4`);; esac; echo $?</code> | 0 | 0 | 4 | 4 | 0 | 0 | 0 | 0 | 4 |
|
||||
| <code>(exit 2); case `exit 3`x in `exit 4`);; esac; echo $?</code> | 0 | 0 | 4 | 4 | 0 | 0 | 0 | 0 | 4 |
|
||||
| <code>(exit 2); case `exit 3`x in `exit 4`x);; esac; echo $?</code> | 0 | 0 | 0 | 4 | 0 | 0 | 0 | 0 | 4 |
|
16
original_source/dict/terms/file.txt
Normal file
16
original_source/dict/terms/file.txt
Normal file
@ -0,0 +1,16 @@
|
||||
====== File ======
|
||||
|
||||
A file is a pool of data in the [[dict:terms:filesystem | filesystem]]. On userlevel, it's referenced using a name, a [[dict:terms:hardlink | hardlink]] to the file.
|
||||
|
||||
If a file is not referenced anymore (number of hardlinks to it drops to 0) then the space allocated for that file is re-used, unless it's still used by some process.
|
||||
|
||||
The file-data splits into actual payload (file contents) and some metadata like filesize, filemode or timestamps. The metadata is stored in the [[dict:terms:inode | inode]].
|
||||
|
||||
Strictly spoken, a [[dict:terms:hardlink | hardlink]] (also called "filename") points to the [[dict:terms:inode | inode]] which organizes a file, not to the file itself.
|
||||
|
||||
|
||||
===== See also =====
|
||||
* [[dict:terms:filesystem | filesystem]]
|
||||
* [[dict:terms:filetimes | filetimes]]
|
||||
* [[dict:terms:hardlink | hardlink]]
|
||||
* [[dict:terms:inode | inode]]
|
327
original_source/dict/terms/filesystem.txt
Normal file
327
original_source/dict/terms/filesystem.txt
Normal file
File diff suppressed because one or more lines are too long
17
original_source/dict/terms/filetimes.txt
Normal file
17
original_source/dict/terms/filetimes.txt
Normal file
@ -0,0 +1,17 @@
|
||||
====== File timestamp ======
|
||||
|
||||
===== atime =====
|
||||
This timestamp indicates when a file was last accessed (read). ''cat''ing a file or executing a shellscript will set it, for example.
|
||||
|
||||
|
||||
|
||||
===== ctime =====
|
||||
This timestamp is set, whenever the metadata of a file (stored in the responsible inode) is set. The metadata includes for example:
|
||||
* file name
|
||||
* fize size
|
||||
* file mode (permissions)
|
||||
and some other things. ''ctime'' will also be updated when a file is written to (when ''mtime'' is updated.
|
||||
|
||||
===== mtime =====
|
||||
The mtime is set, whenever a file's contents are changed, for example by editing a file.
|
||||
|
23
original_source/dict/terms/globbing.txt
Normal file
23
original_source/dict/terms/globbing.txt
Normal file
@ -0,0 +1,23 @@
|
||||
====== Globbing ======
|
||||
|
||||
Globbing is the procedure of
|
||||
* matching all filenames against a given pattern
|
||||
* expanding to all matching filenames
|
||||
|
||||
Unlike MSDOS, where the called program had to interpret the patterns, the globbing on UNIX(r) is done by the shell, the matched filenames are given as parameters to a called command:
|
||||
<code>
|
||||
$ cat *.txt
|
||||
</code>
|
||||
really executes
|
||||
<code>
|
||||
$ cat 1.txt 3.txt foobar.txt XXX.txt
|
||||
</code>
|
||||
|
||||
The term "glob" originates back in the UNIX(r) days where an executable ''glob'' (from "global") existed which was used to expand pattern-matching characters. Later, this functionality was built into the shell. There's still a library function called ''glob()'' (POSIX(r)), which serves the same purpose.
|
||||
|
||||
===== See also =====
|
||||
* [[dict:terms:shell | shell]]
|
||||
* [[dict:terms:hardlink | hardlink]]
|
||||
|
||||
===== See also (article) =====
|
||||
* [[ syntax:expansion:globs | pathname expansion]]
|
40
original_source/dict/terms/hardlink.txt
Normal file
40
original_source/dict/terms/hardlink.txt
Normal file
@ -0,0 +1,40 @@
|
||||
====== Hardlink ======
|
||||
|
||||
Also the article for:
|
||||
* filename
|
||||
|
||||
A hardlink associates a //filename// with a [[dict:terms:file | file]]. That name is an entry in a directory listing. Of course a file can have more hardlinks to it (usually the number of hardlinks to a file is limited), but all hardlinks to a file must reside on the same [[dict:terms:filesystem | filesystem]] as the file itself!
|
||||
|
||||
What you usually call a file is just a name for that file, and thus, a hardlink.
|
||||
|
||||
The difference between a [[dict:terms:symlink | symbolic link]] and a hard link is that there is no easy way to differentiate between a 'real' file and a hard link, let's take a look at the example:
|
||||
|
||||
* create an empty file
|
||||
|
||||
<code>$ touch a</code>
|
||||
|
||||
* create a hard link 'b' and sym link 'c' to empty file
|
||||
|
||||
<code>$ ln a b
|
||||
$ ln -s a c</code>
|
||||
|
||||
as you can see file(1) can't differentiate between a real file 'a' and a hard link 'b', but it can tell 'c' is a sym link
|
||||
|
||||
<code>$ file *
|
||||
a: empty
|
||||
b: empty
|
||||
c: symbolic link to `a'</code>
|
||||
|
||||
''ls -i'' prints out the inode numbers of files, if two files have the same inode number AND are on the same file system it means they are **hardlinked**.
|
||||
|
||||
<code>$ ls -i *
|
||||
5262 a 5262 b 5263 c</code>
|
||||
|
||||
|
||||
hard links don't consume additional space on the filesystem, the space is freed when the last hard link pointing to it is deleted.
|
||||
|
||||
===== See also =====
|
||||
* [[dict:terms:file | file]]
|
||||
* [[dict:terms:filesystem | filesystem]]
|
||||
* [[dict:terms:symlink | symlink]]
|
||||
|
327
original_source/dict/terms/inode.txt
Normal file
327
original_source/dict/terms/inode.txt
Normal file
File diff suppressed because one or more lines are too long
24
original_source/dict/terms/interpreter_directive.txt
Normal file
24
original_source/dict/terms/interpreter_directive.txt
Normal file
@ -0,0 +1,24 @@
|
||||
====== Interpreter Directive ======
|
||||
|
||||
* shebang
|
||||
|
||||
The interpreter directive, usually called shebang, is the character sequence starting with ''#!'' (hash, exclamation-point) at the beginning of the very first line of an executable text file on unixoid operating systems.
|
||||
|
||||
The program loader of the operating system may use this line to load an interpreter for this file when executed. This makes it a self-executable script.
|
||||
|
||||
A shebang will typically look like
|
||||
<code>
|
||||
#!/bin/bash
|
||||
</code>
|
||||
|
||||
Since the line starting with ''#'' is a comment for the shell (and some other scripting languages), it's ignored.
|
||||
|
||||
Regarding the shebang, there are various, differences between operating systems, including:
|
||||
* may require a space after ''#!'' and before the pathname of the interpreter
|
||||
* may be able to take arguments for the interpreter
|
||||
* ...
|
||||
|
||||
POSIX(r) doesn't specify the shebang, though in general it's commonly supported by operating systems.
|
||||
|
||||
===== See also =====
|
||||
* [[http://www.in-ulm.de/~mascheck/various/shebang/ | #!-magic]] - a nice overview of the differences between various operating systems
|
54
original_source/dict/terms/parameter.txt
Normal file
54
original_source/dict/terms/parameter.txt
Normal file
@ -0,0 +1,54 @@
|
||||
====== Parameter ======
|
||||
|
||||
Also the article for: __variable__, __positional parameter__, __special parameter__
|
||||
|
||||
In Bash, a parameter is simply an entity that stores values and can be referenced. Depending on the type, the parameters can be set directly, only indirectly, or only automatically by the shell.
|
||||
|
||||
Bash knows 3 types of parameters:
|
||||
* variables
|
||||
* positional parameters
|
||||
* special parameters
|
||||
|
||||
===== variables =====
|
||||
|
||||
A shell variable is a parameter denoted by a //variable name//:
|
||||
* containing only alphanumeric characters and underscores
|
||||
* beginning with an alphabetic character or an underscore
|
||||
|
||||
A value can be assigned to a variable, using the variable's name and an equal-sign:
|
||||
<code>NAME=VALUE</code>
|
||||
|
||||
Once a variable is set, it exists and can only be unset by the ''unset'' builtin command.
|
||||
|
||||
The nullstring is a valid value:
|
||||
<code>
|
||||
NAME=
|
||||
NAME=""
|
||||
</code>
|
||||
|
||||
|
||||
===== positional parameters =====
|
||||
|
||||
A positional parameter is denoted by a number other than ''0'' (zero).
|
||||
|
||||
Positional parameters reflect the shell's arguments that are not given to the shell itself (in practise, the script arguments, also the function arguments). You can't directly assign to the positional parameters, however, [[commands:builtin:set | the set builtin command]] can be used to indirectly set them.
|
||||
|
||||
The first to ninth positional parameter is referenced by ''$1'' to ''$9''. All following positional parameters (tenth and above) must be referenced by the number given in curly braces, i.e., ''${10}'' or ''${432}''.
|
||||
|
||||
Unlike popular belief, ''$0'' is //not a positional parameter//.
|
||||
|
||||
See also the [[scripting:posparams | scripting article about handling positional parameters]].
|
||||
|
||||
===== special parameters =====
|
||||
|
||||
There are a bunch of special parameters, which are set by the shell. Direct assignment to them is not possible. These parameter names are formed of one character.
|
||||
|
||||
Please see [[syntax:shellvars]].
|
||||
|
||||
|
||||
|
||||
===== See also =====
|
||||
* Syntax article, internal: [[syntax:pe]]
|
||||
* Syntax article, internal: [[syntax:shellvars]]
|
||||
* Scripting article, internal: [[scripting:posparams]]
|
||||
|
12
original_source/dict/terms/posix.txt
Normal file
12
original_source/dict/terms/posix.txt
Normal file
@ -0,0 +1,12 @@
|
||||
====== POSIX ======
|
||||
|
||||
POSIX(r) is a family of standards defined by the IEEE to give a minimum API and interface standardization across the variants of UNIX(r) operating systems.
|
||||
|
||||
One part of it is the standardization of minimum functionality and behaviour of the system shell and some utilities (commands).
|
||||
|
||||
* UNIX is a registered trademark of The Open Group in the US and other countries.
|
||||
* POSIX is a registered trademark of the IEEE Inc.
|
||||
|
||||
===== See also =====
|
||||
* Dictionary, internal: [[dict:terms:shell]]
|
||||
|
12
original_source/dict/terms/shell.txt
Normal file
12
original_source/dict/terms/shell.txt
Normal file
@ -0,0 +1,12 @@
|
||||
====== Shell ======
|
||||
|
||||
On UNIX(r), the shell is the main interaction tool between the user-level and the system. That doesn't necessarily mean the user always sits infront of a shell, but it's integral part of the system, not only an "optional commandline interpreter".
|
||||
|
||||
The main job of a shell is to execute commands as a user requests them. This behaviour alone doesn't help much. A shell knits some intelligence and flow control around the possibility to execute commands - it's a complete commandline-oriented user-interface (UI).
|
||||
|
||||
FIXME
|
||||
|
||||
===== See also =====
|
||||
|
||||
===== See also (external) =====
|
||||
* Wikipedia: [[http://en.wikipedia.org/wiki/Unix_shell | UNIX shell]]
|
13
original_source/dict/terms/special_file.txt
Normal file
13
original_source/dict/terms/special_file.txt
Normal file
@ -0,0 +1,13 @@
|
||||
====== Special file ======
|
||||
|
||||
Unlike a regular file (a bunch of accessible data organized on a filesystem), it's a special filename that points to a ressource or similar:
|
||||
* character special files
|
||||
* block special files
|
||||
* named pipes
|
||||
* socket files
|
||||
Since a directory also is only a file, you can count it as special file, too.
|
||||
|
||||
===== See also =====
|
||||
* [[dict:terms:file | file]]
|
||||
* [[dict:terms:hardlink | filename]]
|
||||
* [[dict:terms:directory | directory]]
|
327
original_source/dict/terms/start.txt
Normal file
327
original_source/dict/terms/start.txt
Normal file
File diff suppressed because one or more lines are too long
11
original_source/dict/terms/symlink.txt
Normal file
11
original_source/dict/terms/symlink.txt
Normal file
@ -0,0 +1,11 @@
|
||||
====== Symlink ======
|
||||
|
||||
A symlink (symbolic link) is a "normal" file, which contains a pointer to another filename. Since it really only points to another **filename** it can
|
||||
* reference filenames on other filesystems
|
||||
* reference filenames that don't actually exist
|
||||
* save a reference to the name of a directory
|
||||
|
||||
===== See also =====
|
||||
* [[dict:terms:hardlink | hardlink]]
|
||||
* [[dict:terms:filesystem | filesystem]]
|
||||
* [[dict:terms:directory | directory]]
|
288
original_source/howto/calculate-dc.txt
Normal file
288
original_source/howto/calculate-dc.txt
Normal file
@ -0,0 +1,288 @@
|
||||
====== Calculating with dc ======
|
||||
|
||||
{{keywords>bash shell scripting arithmetic calculate}}
|
||||
|
||||
===== Introduction =====
|
||||
|
||||
dc(1) is a non standard, but commonly found, reverse-polish Desk
|
||||
Calculator. According to Ken Thompson, "dc is the oldest language on
|
||||
Unix; it was written on the PDP-7 and ported to the PDP-11 before Unix
|
||||
[itself] was ported".
|
||||
|
||||
Historically the standard bc(1) has been implemented as a //front-end to dc//.
|
||||
|
||||
===== Simple calculation =====
|
||||
|
||||
In brief, the //reverse polish notation// means the numbers are put
|
||||
on the stack first, then an operation is applied to them. Instead
|
||||
of writing ''1+1'', you write ''1 1+''.
|
||||
|
||||
By default ''dc'', unlike ''bc'', doesn't print anything, the result is pushed on the stack.
|
||||
You have to use the "p" command to print the element at the top of the stack.
|
||||
Thus a simple operation looks like:
|
||||
|
||||
<code>
|
||||
$ dc <<< '1 1+pq'
|
||||
2
|
||||
</code>
|
||||
I used a "here string" present in bash 3.x, ksh93 and zsh. if your
|
||||
shell doesn't support this, you can use ''echo '1 1+p' | dc'' or if you have GNU ''dc'', you can use ''dc -e '1 1 +p'''.
|
||||
|
||||
Of course, you can also just run ''dc'' and enter the commands.
|
||||
|
||||
The classic operations are:
|
||||
* addition: ''+''
|
||||
* subtraction: ''-''
|
||||
* division: ''/''
|
||||
* multiplication: ''*''
|
||||
* remainder (modulo): ''%''
|
||||
* exponentiation: ''^''
|
||||
* square root: ''v''
|
||||
GNU ''dc'' adds a couple more.
|
||||
|
||||
To input a negative number you need to use the ''_'' (underscore) character:
|
||||
<code>
|
||||
$ dc <<< '1_1-p'
|
||||
2
|
||||
</code>
|
||||
|
||||
You can use the //digits// ''0'' to ''9'' and the //letters// ''A'' to ''F'' as numbers, and a dot (''.'') as a decimal point.
|
||||
The ''A'' to ''F'' **must** be capital letters in order not to be confused with the commands specified with lower case characters.
|
||||
A number with a letter is considered hexadecimal:
|
||||
<code>
|
||||
dc <<< 'Ap'
|
||||
10
|
||||
</code>
|
||||
The **output** is converted to **base 10** by default
|
||||
|
||||
===== Scale And Base =====
|
||||
|
||||
''dc'' is a calulator with abitrary precision, by default this precision is 0.
|
||||
thus ''<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
|
||||
and uses it as the precision argument:
|
||||
<code>
|
||||
dc <<< '2k5 4/p' # prints 1.25
|
||||
dc <<< '4k5 4/p' # prints 1.2500
|
||||
dc <<< '100k 2vp'
|
||||
1.4142135623730950488016887242096980785696718753769480731766797379907\
|
||||
324784621070388503875343276415727
|
||||
</code>
|
||||
dc supports //large// precision arguments.
|
||||
|
||||
You can change the base used to output (//print//) the numbers with ''o'' and the base used to
|
||||
input (//type//) the numbers with ''i'':
|
||||
<code>
|
||||
dc << EOF
|
||||
20 p# prints 20, output is in base 10
|
||||
16o # the output is now in base 2 16
|
||||
20p # prints 14, in hex
|
||||
16i # the output is now in hex
|
||||
p # prints 14 this doesn't modify the number in the stack
|
||||
10p # prints 10 the output is done in base 16
|
||||
EOF
|
||||
</code>
|
||||
|
||||
Note: when the input value is modified, the base is modified for all commands, including ''i'':
|
||||
|
||||
<code>
|
||||
dc << EOF
|
||||
16i 16o # base is 16 for input and output
|
||||
10p # prints 10
|
||||
10i # ! set the base to 10 i.e. to 16 decimal
|
||||
17p # prints 17
|
||||
EOF
|
||||
</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.
|
||||
The problem is 10 was typed while the input base 16, thus the base was set to 10 hexadecimal, i.e. 16 decimal.
|
||||
|
||||
<code>
|
||||
dc << EOF
|
||||
16o16o10p #prints 10
|
||||
Ai # set the base to A in hex i.e. 10
|
||||
17p # prints 11 in base 16
|
||||
EOF
|
||||
</code>
|
||||
|
||||
===== Stack =====
|
||||
|
||||
There are two basic commands to manipulate the stack:
|
||||
* ''d'' duplicates the top of the stack
|
||||
* ''c'' clears the stack
|
||||
|
||||
<code>
|
||||
$ dc << EOF
|
||||
2 # put 2 on the stack
|
||||
d # duplicate i.e. put another 2 on the stack
|
||||
*p # multiply and print
|
||||
c p # clear and print
|
||||
EOF
|
||||
4
|
||||
dc: stack empty
|
||||
</code>
|
||||
|
||||
''c p'' results in an error, as we would expect, as c removes everything
|
||||
on the stack. //Note: we can use ''#'' to put comments in the script.//
|
||||
|
||||
If you are lost, you can inspect (i.e. print) the stack using the command
|
||||
''f''. The stack remains unchanged:
|
||||
|
||||
<code>
|
||||
dc <<< '1 2 d 4+f'
|
||||
6
|
||||
2
|
||||
1
|
||||
</code>
|
||||
|
||||
Note how the first element that will be popped from the stack is printed first, if you are
|
||||
used to an HP calculator, it's the reverse.
|
||||
|
||||
Don't hesitate to put ''f'' in the examples of this tutorial, it doesn't change the result,
|
||||
and it's a good way to see what's going on.
|
||||
|
||||
===== Registers =====
|
||||
|
||||
The GNU ''dc'' manual says that dc has at least **256 registers** depending on
|
||||
the range of unsigned char. I'm not sure how you are supposed to use
|
||||
the NUL byte.
|
||||
Using a register is easy:
|
||||
|
||||
<code>
|
||||
dc <<EOF
|
||||
12 # put 12 on the stack
|
||||
sa # remove it from the stack (s), and put it in register 'a'
|
||||
10 # put 10 on the stack
|
||||
la # read (l) the value of register 'a' and push it on the stack
|
||||
+p # add the 2 values and print
|
||||
EOF
|
||||
</code>
|
||||
|
||||
The above snippet uses newlines to embed comments, but it doesn't
|
||||
really matter, you can use ''echo '12sa10la+p'| dc'', with the same results.
|
||||
|
||||
The register can contain more than just a value, **each register is a
|
||||
stack on its own**.
|
||||
|
||||
<code>
|
||||
dc <<EOF
|
||||
12sa #store 12 in 'a'
|
||||
6Sa # with a capital S the 6 is removed
|
||||
# from the main stack and pushed on the 'a' stack
|
||||
lap # prints 6, the value at the top of the 'a' stack
|
||||
lap # still prints 6
|
||||
Lap # prints 6 also but with a capital L, it pushes the value in 'a'
|
||||
# to the main stack and pulls it from the 'a' stack
|
||||
lap # prints 12, which is now at the top of the stack
|
||||
EOF
|
||||
</code>
|
||||
|
||||
===== Macros =====
|
||||
|
||||
''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
|
||||
evalute it with x: ''<nowiki>dc <<< '[1 2+]xp'</nowiki>''.
|
||||
|
||||
This is not that interesting until combined with registers.
|
||||
First, let's say we want to calculate the square of a number
|
||||
(don't forget to include ''f'' if you get lost!):
|
||||
<code>
|
||||
dc << EOF
|
||||
3 # push our number on the stack
|
||||
d # duplicate it i.e. push 3 on the stack again
|
||||
d**p # duplicate again and calculate the product and print
|
||||
EOF
|
||||
</code>
|
||||
|
||||
Now we have several cubes to calculate, we could use ''<nowiki>dd**</nowiki>'' several times, or
|
||||
use a macro.
|
||||
|
||||
<code>
|
||||
dc << EOF
|
||||
[dd**] # push a string
|
||||
sa # save it in register a
|
||||
3 # push 3 on the stack
|
||||
lax # push the string "dd**" on the stack and execute it
|
||||
p # print the result
|
||||
4laxp # same operation for 4, in one line
|
||||
EOF
|
||||
</code>
|
||||
|
||||
===== Conditionals and Loops =====
|
||||
|
||||
''dc'' can execute a macro stored in a register using the ''lR x'' combo, but
|
||||
it can also execute macros conditionally. ''>a'' will execute the macro
|
||||
stored in the register ''a'', if the top of the stack is //greater than// the second
|
||||
element of the stack. Note: the top of the stack contains the last entry.
|
||||
When written, it appears as the reverse of what we are used to reading:
|
||||
|
||||
<code>
|
||||
dc << EOF
|
||||
[[Hello World]p] sR # store in 'R' a macro that prints Hello World
|
||||
2 1 >R # do nothing 1 is at the top 2 is the second element
|
||||
1 2 >R # prints Hello World
|
||||
EOF
|
||||
</code>
|
||||
|
||||
Some ''dc'' have ''>R <R =R'', GNU ''dc'' had some more, check your manual. Note
|
||||
that the test "consumes" its operands: the 2 first elements are popped
|
||||
off the stack (you can verify that ''<nowiki>dc <<< "[f]sR 2 1 >R 1 2 >R f"</nowiki>''
|
||||
doesn't print anything)
|
||||
|
||||
Have you noticed how we can //include// a macro (string) in a macro? and as ''dc''
|
||||
relies on a stack we can, in fact, use the macro recursively (have your
|
||||
favorite control-c key combo ready ;)) :
|
||||
|
||||
<code>
|
||||
dc << EOF
|
||||
[ [Hello World] p # our macro starts by printing Hello World
|
||||
lRx ] # and then executes the macro in R
|
||||
sR # we store it in the register R
|
||||
lRx # and finally executes it.
|
||||
EOF
|
||||
</code>
|
||||
|
||||
We have recursivity, we have test, we have loops:
|
||||
|
||||
<code>
|
||||
dc << EOF
|
||||
[ li # put our index i on the stack
|
||||
p # print it, to see what's going on
|
||||
1 - # we decrement the index by one
|
||||
si # store decremented index (i=i-1)
|
||||
0 li >L # if i > 0 then execute L
|
||||
] sL # store our macro with the name L
|
||||
|
||||
10 si # let's give to our index the value 10
|
||||
lLx # and start our loop
|
||||
EOF
|
||||
</code>
|
||||
|
||||
Of course code written this way is far too easy to read! Make sure to
|
||||
remove all those extra spaces newlines and comments:
|
||||
|
||||
<code>
|
||||
dc <<< '[lip1-si0li>L]sL10silLx'
|
||||
dc <<< '[p1-d0<L]sL10lLx' # use the stack instead of a register
|
||||
</code>
|
||||
|
||||
I'll let you figure out the second example, it's not hard, it uses the stack
|
||||
instead of a register for the index.
|
||||
|
||||
===== Next =====
|
||||
|
||||
Check your dc manual, i haven't decribed everything, like arrays
|
||||
(only documented with "; : are used by bc(1) for array operations" on solaris,
|
||||
probably because //echo '1 0:a 0Sa 2 0:a La 0;ap' | dc// results in
|
||||
//Segmentation Fault (core dump) //, the latest solaris uses GNU dc)
|
||||
|
||||
You can find more info and dc programs here:
|
||||
* [[http://en.wikipedia.org/wiki/Dc_(Unix)|http://en.wikipedia.org/wiki/Dc_(Unix)]]
|
||||
|
||||
And more example, as well as a dc implementation in python here:
|
||||
* http://en.literateprograms.org/Category:Programming_language:dc
|
||||
* http://en.literateprograms.org/Desk_calculator_%28Python%29
|
||||
|
||||
The manual for the 1971 dc from Bell Labs:
|
||||
* http://cm.bell-labs.com/cm/cs/who/dmr/man12.ps (dead link)
|
89
original_source/howto/collapsing_functions.txt
Normal file
89
original_source/howto/collapsing_functions.txt
Normal file
@ -0,0 +1,89 @@
|
||||
====== Collapsing Functions ======
|
||||
|
||||
{{keywords>bash shell scripting example function collapse}}
|
||||
|
||||
===== What is a "Collapsing Function"? =====
|
||||
A collapsing function is a function whose behavior changes depending upon the circumstances under which it's run. Function collapsing is useful when you find yourself repeatedly checking a variable whose value never changes.
|
||||
|
||||
===== How do I make a function collapse? =====
|
||||
Function collapsing requires some static feature in the environment. A common example is a script that gives the user the option of having "verbose" output.
|
||||
<code>
|
||||
#!/bin/bash
|
||||
|
||||
[[ $1 = -v || $1 = --verbose ]] && verbose=1
|
||||
|
||||
chatter() {
|
||||
if [[ $verbose ]]; then
|
||||
chatter() {
|
||||
echo "$@"
|
||||
}
|
||||
chatter "$@"
|
||||
else
|
||||
chatter() {
|
||||
:
|
||||
}
|
||||
fi
|
||||
}
|
||||
|
||||
echo "Waiting for 10 seconds."
|
||||
for i in {1..10}; do
|
||||
chatter "$i"
|
||||
sleep 1
|
||||
done
|
||||
</code>
|
||||
|
||||
===== How does it work? =====
|
||||
The first time you run chatter(), the function redefines itself based on the value of verbose. Thereafter, chatter doesn't check $verbose, it simply is. Further calls to the function reflect its collapsed nature. If verbose is unset, chatter will echo nothing, with no extra effort from the developer.
|
||||
===== More examples =====
|
||||
FIXME Add more examples!
|
||||
<code>
|
||||
# Somewhat more portable find -executable
|
||||
# FIXME/UNTESTED (I don't have access to all of the different versions of find.)
|
||||
# Usage: find PATH ARGS -- use find like normal, except use -executable instead of
|
||||
# various versions of -perm /+ blah blah and hacks
|
||||
find() {
|
||||
hash find || { echo 'find not found!'; exit 1; }
|
||||
# We can be pretty sure "$0" should be executable.
|
||||
if [[ $(command find "$0" -executable 2> /dev/null) ]]; then
|
||||
unset -f find # We can just use the command find
|
||||
elif [[ $(command find "$0" -perm /u+x 2> /dev/null) ]]; then
|
||||
find() {
|
||||
typeset arg args
|
||||
for arg do
|
||||
[[ $arg = -executable ]] && args+=(-perm /u+x) || args+=("$arg")
|
||||
done
|
||||
command find "${args[@]}"
|
||||
}
|
||||
elif [[ $(command find "$0" -perm +u+x 2> /dev/null) ]]; then
|
||||
find() {
|
||||
typeset arg args
|
||||
for arg do
|
||||
[[ $arg = -executable ]] && args+=(-perm +u+x) || args+=("$arg")
|
||||
done
|
||||
command find "${args[@]}"
|
||||
}
|
||||
else # Last resort
|
||||
find() {
|
||||
typeset arg args
|
||||
for arg do
|
||||
[[ $arg = -executable ]] && args+=(-exec test -x {} \; -print) || args+=("$arg")
|
||||
done
|
||||
command find "${args[@]}"
|
||||
}
|
||||
fi
|
||||
find "$@"
|
||||
}
|
||||
</code>
|
||||
|
||||
<code>
|
||||
#!/bin/bash
|
||||
# Using collapsing functions to turn debug messages on/off
|
||||
|
||||
[ "--debug" = "$1" ] && dbg=echo || dbg=:
|
||||
|
||||
|
||||
# From now on if you use $dbg instead of echo, you can select if messages will be shown
|
||||
|
||||
$dbg "This message will only be displayed if --debug is specified at the command line
|
||||
|
||||
</code>
|
91
original_source/howto/conffile.txt
Normal file
91
original_source/howto/conffile.txt
Normal file
@ -0,0 +1,91 @@
|
||||
====== Config files for your script ======
|
||||
|
||||
{{keywords>bash shell scripting config files include configuration}}
|
||||
|
||||
===== General =====
|
||||
|
||||
For this task, you don't have to write large parser routines (unless you want it 100% secure or you want a special file syntax) - you can use the Bash source command. The file to be sourced should be formated in key="value" format, otherwise bash will try to interpret commands:
|
||||
|
||||
<code>
|
||||
#!/bin/bash
|
||||
echo "Reading config...." >&2
|
||||
source /etc/cool.cfg
|
||||
echo "Config for the username: $cool_username" >&2
|
||||
echo "Config for the target host: $cool_host" >&2
|
||||
</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!
|
||||
The sourced file most likely contains something like:
|
||||
|
||||
<code>
|
||||
cool_username="guest"
|
||||
cool_host="foo.example.com"
|
||||
</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:
|
||||
|
||||
<code>
|
||||
#!/bin/bash
|
||||
echo "Reading config...." >&2
|
||||
. /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 target host: $cool_host" >&2
|
||||
</code>
|
||||
|
||||
===== 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:
|
||||
|
||||
<code>
|
||||
#!/bin/bash
|
||||
echo "Reading system-wide config...." >&2
|
||||
. /etc/cool.cfg
|
||||
if [ -r ~/.coolrc ]; then
|
||||
echo "Reading user config...." >&2
|
||||
. ~/.coolrc
|
||||
fi
|
||||
</code>
|
||||
|
||||
|
||||
|
||||
===== Secure it =====
|
||||
|
||||
As mentioned earlier, the sourced file can contain anything a Bash script can. Essentially, it **is** an included Bash script. That creates security issues. A malicicios person can "execute" arbitrary code when your script is sourcing its config file.
|
||||
You might want to allow only constructs in the form ''NAME=VALUE'' in that file (variable assignment syntax) and maybe comments (though technically, comments are unimportant).
|
||||
Imagine the following "config file", containing some malicious code:
|
||||
|
||||
<code>
|
||||
# cool config file for my even cooler script
|
||||
username=god_only_knows
|
||||
hostname=www.example.com
|
||||
password=secret ; echo rm -rf ~/*
|
||||
parameter=foobar && echo "You've bene pwned!";
|
||||
# hey look, weird code follows...
|
||||
echo "I am the skull virus..."
|
||||
echo rm -fr ~/*
|
||||
mailto=netadmin@example.com
|
||||
</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:
|
||||
|
||||
<code>
|
||||
#!/bin/bash
|
||||
configfile='/etc/cool.cfg'
|
||||
configfile_secured='/tmp/cool.cfg'
|
||||
|
||||
# check if the file contains something we don't want
|
||||
if egrep -q -v '^#|^[^ ]*=[^;]*' "$configfile"; then
|
||||
echo "Config file is unclean, cleaning it..." >&2
|
||||
# filter the original to a new file
|
||||
egrep '^#|^[^ ]*=[^;&]*' "$configfile" > "$configfile_secured"
|
||||
configfile="$configfile_secured"
|
||||
fi
|
||||
|
||||
# now source it, either the original or the filtered variant
|
||||
source "$configfile"
|
||||
</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.
|
||||
|
||||
This filter allows only ''NAME=VALUE'' and comments in the file, but it doesn't prevent all methods of code execution. I will address that later.
|
58
original_source/howto/dissectabadoneliner.txt
Normal file
58
original_source/howto/dissectabadoneliner.txt
Normal file
@ -0,0 +1,58 @@
|
||||
====== 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>
|
||||
|
||||
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>
|
||||
|
||||
(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>
|
||||
|
||||
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>
|
||||
|
||||
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>
|
||||
|
||||
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>
|
||||
|
||||
Let's keep going:
|
||||
|
||||
<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.
|
||||
|
||||
<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:
|
||||
|
||||
<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.)
|
||||
|
||||
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>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:
|
||||
|
||||
<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:
|
||||
|
||||
<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>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.
|
369
original_source/howto/edit-ed.txt
Normal file
369
original_source/howto/edit-ed.txt
Normal file
@ -0,0 +1,369 @@
|
||||
====== Editing files via scripts with ed ======
|
||||
|
||||
{{keywords>bash shell scripting arguments file editor edit ed sed}}
|
||||
|
||||
===== Why ed? =====
|
||||
|
||||
Like ''sed'', ''ed'' is a line editor. However, if you try to change file contents with ''sed'', and the file is open elsewhere and read by some process, you will find out that GNU ''sed'' and its ''-i'' option will not allow you to edit the file. There are circumstances where you may need that, e.g. editing active and open files, the lack of GNU, or other ''sed'', with "in-place" option available.
|
||||
|
||||
Why ''ed''?
|
||||
* maybe your ''sed'' doesn't support in-place edit
|
||||
* maybe you need to be as portable as possible
|
||||
* maybe you need to really edit in-file (and not create a new file like GNU ''sed'')
|
||||
* last but not least: standard ''ed'' has very good editing and addressing possibilities, compared to standard ''sed''
|
||||
|
||||
Don't get me wrong, this is **not** meant as anti-''sed'' article! It's just meant to show you another way to do the job.
|
||||
|
||||
|
||||
|
||||
|
||||
===== Commanding ed =====
|
||||
|
||||
Since ''ed'' is an interactive text editor, it reads and executes commands that come from ''stdin''. There are several ways to feed our commands to ed:
|
||||
|
||||
**__Pipelines__**
|
||||
<code>
|
||||
echo '<ED-COMMANDS>' | ed <FILE>
|
||||
</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:
|
||||
<code>
|
||||
|
||||
# insertHead "$text" "$file"
|
||||
|
||||
insertHead() {
|
||||
printf '%s\n' H 1i "$1" . w | ed -s "$2"
|
||||
}
|
||||
</code>
|
||||
|
||||
**__Here-strings__**
|
||||
<code>
|
||||
ed <FILE> <<< '<ED-COMMANDS>'
|
||||
</code>
|
||||
|
||||
**__Here-documents__**
|
||||
<code>
|
||||
ed <FILE> <<EOF
|
||||
<ED-COMMANDS>
|
||||
EOF
|
||||
</code>
|
||||
|
||||
Which one you prefer is your choice. I will use the here-strings, since it looks best here IMHO.
|
||||
|
||||
There are other ways to provide input to ''ed''. For example, process substitution. But these should be enough for daily needs.
|
||||
|
||||
Since ''ed'' wants commands separated by newlines, I'll use a special Bash quoting method, the C-like strings ''<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.
|
||||
|
||||
|
||||
|
||||
===== The basic interface =====
|
||||
|
||||
Check the ''ed'' manpage for details
|
||||
|
||||
Similar to ''vi'' or ''vim'', ''ed'' has a "command mode" and an "interactive mode". For non-interactive use, the command mode is the usual choice.
|
||||
|
||||
Commands to ''ed'' have a simple and regular structure: zero, one, or two addresses followed by a single-character command, possibly followed by parameters to that command. These addresses specify one or more lines in the text buffer. Every command that requires addresses has default addresses, so the addresses can often be omitted.
|
||||
|
||||
The line addressing is relative to the //current line//. If the edit buffer is not empty, the initial value for the //current line// shall be the last line in the edit buffer, otherwise zero. Generally, the //current line// is the last line affected by a command. All addresses can only address single lines, not blocks of lines!
|
||||
|
||||
Line addresses or commands using //regular expressions// interpret POSIX Basic Regular Expressions (BRE). A null BRE is used to reference the most recently used BRE. Since ''ed'' addressing is only for single lines, no RE can ever match a newline.
|
||||
|
||||
|
||||
|
||||
===== Debugging your ed scripts =====
|
||||
|
||||
By default, ''ed'' is not very talkative and will simply print a "?" when an error occurs. Interactively you can use the ''h'' command to get a short message explaining the last error. You can also turn on a mode that makes ''ed'' automatically print this message with the ''H'' command. It is a good idea to always add this command at the beginning of your ed scripts:
|
||||
|
||||
<code>
|
||||
bash > ed -s file <<< $'H\n,df'
|
||||
?
|
||||
script, line 2: Invalid command suffix
|
||||
</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:
|
||||
<code>
|
||||
# Works, but there is better
|
||||
|
||||
# copy my original file
|
||||
cp file file.test
|
||||
|
||||
# try my script on the file
|
||||
ed -s file.test <<< $'H\n<ed commands>\nw'
|
||||
|
||||
# see the results
|
||||
cat file.test
|
||||
</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:
|
||||
|
||||
<code>
|
||||
ed -s file <<< $'H\n<ed commands>\n,p'
|
||||
</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''.
|
||||
|
||||
Of course, even if the file is not modified by the ''p'' command, **it's always a good idea to have a backup copy!**
|
||||
|
||||
|
||||
===== Editing your files =====
|
||||
|
||||
Most of these things can be done with ''sed''. But there are also things that can't be done in ''sed'' or can only be done with very complex code.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
==== Simple word substitutions ====
|
||||
|
||||
Like ''sed'', ''ed'' also knows the common ''s/FROM/TO/'' command, and it can also take line-addresses. **If no substitution is made on the addressed lines, it's considered an error.**
|
||||
|
||||
=== Substitutions through the whole file ===
|
||||
|
||||
<code>
|
||||
ed -s test.txt <<< $',s/Windows(R)-compatible/POSIX-conform/g\nw'
|
||||
</code>
|
||||
|
||||
__Note:__ The comma as single address operator is an alias for ''1,$'' ("all lines").
|
||||
|
||||
=== Substitutions in specific lines ===
|
||||
|
||||
On a line containing ''fruits'', do the substitution:
|
||||
<code>
|
||||
ed -s test.txt <<< $'/fruits/s/apple/banana/g\nw'
|
||||
</code>
|
||||
|
||||
On the 5th line after the line containing ''fruits'', do the substitution:
|
||||
<code>
|
||||
ed -s test.txt <<< $'/fruits/+5s/apple/banana/g\nw'
|
||||
</code>
|
||||
|
||||
==== Block operations ====
|
||||
|
||||
=== Delete a block of text ===
|
||||
|
||||
The simple one is a well-known (by position) block of text:
|
||||
<code>
|
||||
# delete lines number 2 to 4 (2, 3, 4)
|
||||
ed -s test.txt <<< $'2,5d\nw'
|
||||
</code>
|
||||
|
||||
This deletes all lines matching a specific regular expression:
|
||||
<code>
|
||||
# delete all lines matching foobar
|
||||
ed -s test.txt <<< $'g/foobar/d\nw'
|
||||
</code>
|
||||
g/regexp/ applies the command following it to all the lines matching the regexp
|
||||
|
||||
|
||||
=== Move a block of text ===
|
||||
...using the ''m'' command: ''<ADDRESS> m <TARGET-ADDRESS>''
|
||||
|
||||
This is definitely something that can't be done easily with sed.
|
||||
|
||||
<code>
|
||||
# moving lines 5-9 to the end of the file
|
||||
ed -s test.txt <<< $'5,9m$\nw'
|
||||
|
||||
# moving lines 5-9 to line 3
|
||||
ed -s test.txt <<< $'5,9m3\nw'
|
||||
</code>
|
||||
|
||||
=== Copy a block of text ===
|
||||
...using the ''t'' command: ''<ADDRESS> t <TARGET-ADDRESS>''
|
||||
|
||||
You use the ''t'' command just like you use the ''m'' (move) command.
|
||||
|
||||
<code>
|
||||
# make a copy of lines 5-9 and place it at the end of the file
|
||||
ed -s test.txt <<< $'5,9t$\nw'
|
||||
|
||||
# make a copy of lines 5-9 and place it at line 3
|
||||
ed -s test.txt <<< $'5,9t3\nw'
|
||||
</code>
|
||||
|
||||
=== Join all lines ===
|
||||
...but leave the final newline intact. This is done by an extra command: ''j'' (join).
|
||||
|
||||
<code>
|
||||
ed -s file <<< $'1,$j\nw'
|
||||
</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.
|
||||
|
||||
|
||||
==== File operations ====
|
||||
|
||||
=== Insert another file ===
|
||||
|
||||
How do you insert another file? As with ''sed'', you use the ''r'' (read) command. That inserts another file at the line before the last line (and prints the result to stdout - '',p''):
|
||||
<code>
|
||||
ed -s FILE1 <<< $'$-1 r FILE2\n,p'
|
||||
</code>
|
||||
|
||||
To compare, here's a possible ''sed'' solution which must use Bash arithmetic and the external program ''wc'':
|
||||
<code>
|
||||
sed "$(($(wc -l < FILE1)-1))r FILE2" FILE1
|
||||
|
||||
# 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
|
||||
# security risk, but well, sometimes GNU > security, you know...
|
||||
sed '${h;s/.*/cat FILE2/e;G}' FILE1
|
||||
</code>
|
||||
|
||||
Another approach, in two invocations of sed, that avoids the use of external commands completely:
|
||||
|
||||
<code>
|
||||
sed $'${s/$/\\n-||-/;r FILE2\n}' FILE1 | sed '0,/-||-/{//!h;N;//D};$G'
|
||||
</code>
|
||||
|
||||
===== Pitfalls =====
|
||||
|
||||
|
||||
|
||||
==== ed is not sed ===
|
||||
|
||||
ed and sed might look similar, but the same command(s) might act differently:
|
||||
|
||||
**__ /foo/d __**
|
||||
|
||||
In sed /foo/d will delete all lines matching foo, in ed the commands are not repeated
|
||||
on each line so this command will search the next line matching foo and delete it.
|
||||
If you want to delete all lines matching foo, or do a subsitution on all lines matching foo
|
||||
you have to tell ed about it with the g (global) command:
|
||||
|
||||
<code>
|
||||
echo $'1\n1\n3' > file
|
||||
|
||||
#replace all lines matching 1 by "replacement"
|
||||
ed -s file <<< $'g/1/s/1/replacement/\n,p'
|
||||
|
||||
#replace the first line matching 1 by "replacement"
|
||||
#(because it starts searching from the last line)
|
||||
ed -s file <<< $'s/1/replacement/\n,p'
|
||||
</code>
|
||||
|
||||
**__ an error stops the script __**
|
||||
|
||||
You might think that it's not a problem and that the same thing happens with sed and you're right, with the exception that if ed
|
||||
does not find a pattern it's an error, while sed just continues with the next line.
|
||||
For instance, let's say that you want to change foo to bar on the first line of the file and add something after the next line,
|
||||
ed will stop if it cannot find foo on the first line, sed will continue.
|
||||
|
||||
<code>
|
||||
#Gnu sed version
|
||||
sed -e '1s/foo/bar/' -e '$a\something' file
|
||||
|
||||
#First ed version, does nothing if foo is not found on the first line:
|
||||
ed -s file <<< $'H\n1s/foo/bar/\na\nsomething\n.\nw'
|
||||
</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,
|
||||
thus the substitution will succeed and ed will not produce an error when foo is not found:
|
||||
|
||||
<code>
|
||||
#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'
|
||||
</code>
|
||||
|
||||
In fact, even a substitution that fails after a g/ / command does not seem to cause an error, i.e. you can
|
||||
use a trick like g/./s/foo/bar/ to attempt the substitution on all non blank lines
|
||||
|
||||
==== here documents ====
|
||||
|
||||
**__ shell parameters are expanded __**
|
||||
|
||||
If you don't quote the delimiter, $ has a special meaning. This sounds obvious but it's
|
||||
easy to forget this fact when you use addresses like $-1 or commands like $a. Either quote the $ or the delimiter:
|
||||
<code>
|
||||
#fails
|
||||
ed -s file << EOF
|
||||
$a
|
||||
last line
|
||||
.
|
||||
w
|
||||
EOF
|
||||
|
||||
#ok
|
||||
ed -s file << EOF
|
||||
\$a
|
||||
last line
|
||||
.
|
||||
w
|
||||
EOF
|
||||
|
||||
#ok again
|
||||
ed -s file << 'EOF'
|
||||
$a
|
||||
last line
|
||||
.
|
||||
w
|
||||
EOF
|
||||
</code>
|
||||
|
||||
**__ "." is not a command __**
|
||||
|
||||
The . used to terminate the command "a" must be the only thing on the line.
|
||||
take care if you indent the commands:
|
||||
|
||||
<code>
|
||||
#ed doesn't care about the spaces before the commands, but the . must be the only thing on the line:
|
||||
ed -s file << EOF
|
||||
a
|
||||
my content
|
||||
.
|
||||
w
|
||||
EOF
|
||||
</code>
|
||||
|
||||
===== Simulate other commands =====
|
||||
|
||||
Keep in mind that in all the examples below, the entire file will be read into memory.
|
||||
|
||||
==== A simple grep ====
|
||||
|
||||
<code>
|
||||
ed -s file <<< 'g/foo/p'
|
||||
|
||||
# equivalent
|
||||
ed -s file <<< 'g/foo/'
|
||||
</code>
|
||||
|
||||
The name ''grep'' is derived from the notaion ''g/RE/p'' (global => regular expression => print).
|
||||
ref http://www.catb.org/~esr/jargon/html/G/grep.html
|
||||
|
||||
|
||||
|
||||
==== wc -l ====
|
||||
|
||||
Since the default for the ''ed'' "print line number" command is the last line, a simple ''='' (equal sign) will print this line number and thus the number of lines of the file:
|
||||
|
||||
<code>
|
||||
ed -s file <<< '='
|
||||
</code>
|
||||
|
||||
|
||||
==== cat ====
|
||||
Yea, it's a joke...
|
||||
|
||||
<code>
|
||||
ed -s file <<< $',p'
|
||||
</code>
|
||||
|
||||
...but a similar thing to ''cat'' showing line-endings and escapes can be done with the ''list'' command (l):
|
||||
|
||||
<code>
|
||||
ed -s file <<< $',l'
|
||||
</code>
|
||||
|
||||
|
||||
FIXME to be continued
|
||||
|
||||
===== Links =====
|
||||
|
||||
Reference:
|
||||
* [[http://www.gnu.org/software/ed/manual/ed_manual.html | Gnu ed]] - if we had to guess, you're probably using this one.
|
||||
* POSIX [[http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ed.html#tag_20_38 | ed]], [[http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ex.html#tag_20_40 | ex ]], and [[http://pubs.opengroup.org/onlinepubs/9699919799/utilities/vi.html#tag_20_152 | vi ]]
|
||||
* [[ http://sdf.lonestar.org/index.cgi?tutorials/ed ]] - ed cheatsheet on sdf.org
|
||||
|
||||
Misc info / tutorials:
|
||||
* [[ http://mywiki.wooledge.org/BashFAQ/021 | How can I replace a string with another string in a variable, a stream, a file, or in all the files in a directory? ]] - BashFAQ
|
||||
* [[ http://wolfram.schneider.org/bsd/7thEdManVol2/edtut/edtut.pdf ]] - Old but still relevant ed tutorial.
|
298
original_source/howto/getopts_tutorial.txt
Normal file
298
original_source/howto/getopts_tutorial.txt
Normal file
@ -0,0 +1,298 @@
|
||||
====== Small getopts tutorial ======
|
||||
|
||||
{{keywords>bash shell scripting arguments positional parameters options getopt getopts}}
|
||||
|
||||
===== Description =====
|
||||
**Note that** ''getopts'' is neither able to parse GNU-style long options (''<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.
|
||||
* Being a builtin, ''getopts'' can set shell variables to use for parsing (impossible for an //external// process!)
|
||||
* There's no need to argue with several ''getopt'' implementations which had buggy concepts in the past (whitespace, ...)
|
||||
* ''getopts'' is defined in POSIX(r).
|
||||
|
||||
----
|
||||
|
||||
Some other methods to parse positional parameters - using neither **getopt** nor **getopts** - are described in: [[scripting:posparams | How to handle positional parameters]].
|
||||
|
||||
|
||||
==== Terminology ====
|
||||
|
||||
It's useful to know what we're talking about here, so let's see... Consider the following command line:
|
||||
<code>
|
||||
mybackup -x -f /etc/mybackup.conf -r ./foo.txt ./bar.txt
|
||||
</code>
|
||||
|
||||
These are all positional parameters, but they can be divided into several logical groups:
|
||||
* ''-x'' is an **option** (aka **flag** or **switch**). It consists of a dash (''-'') followed by **one** character.
|
||||
* ''-f'' is also an option, but this option has an associated **option argument** (an argument to the option ''-f''): ''/etc/mybackup.conf''. The option argument is usually the argument following the option itself, but that isn't mandatory. Joining the option and option argument into a single argument ''-f/etc/mybackup.conf'' is valid.
|
||||
* ''-r'' depends on the configuration. In this example, ''-r'' doesn't take arguments so it's a standalone option like ''-x''.
|
||||
* ''./foo.txt'' and ''./bar.txt'' are remaining arguments without any associated options. These are often used as **mass-arguments**. For example, the filenames specified for ''cp(1)'', or arguments that don't need an option to be recognized because of the intended behavior of the program. POSIX(r) calls them **operands**.
|
||||
|
||||
To give you an idea about why ''getopts'' is useful, The above command line is equivalent to:
|
||||
<code>
|
||||
mybackup -xrf /etc/mybackup.conf ./foo.txt ./bar.txt
|
||||
</code>
|
||||
|
||||
which is complex to parse without the help of ''getopts''.
|
||||
|
||||
The option flags can be **upper- and lowercase** characters, or **digits**. It may recognize other characters, but that's not recommended (usability and maybe problems with special characters).
|
||||
|
||||
==== How it works ====
|
||||
|
||||
In general you need to call ''getopts'' several times. Each time it will use the next positional parameter and a possible argument, if parsable, and provide it to you. ''getopts'' will not change the set of positional parameters. If you want to shift them, it must be done manually:
|
||||
|
||||
<code>
|
||||
shift $((OPTIND-1))
|
||||
# now do something with $@
|
||||
</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:
|
||||
|
||||
<code>
|
||||
while getopts ...; do
|
||||
...
|
||||
done
|
||||
</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]].
|
||||
|
||||
|
||||
|
||||
|
||||
==== Used variables ====
|
||||
|
||||
^variable^description^
|
||||
|[[syntax:shellvars#OPTIND|OPTIND]]|Holds the index to the next argument to be processed. This is how ''getopts'' "remembers" its own status between invocations. Also useful to shift the positional parameters after processing with ''getopts''. ''OPTIND'' is initially set to 1, and **needs to be re-set to 1 if you want to parse anything again with getopts**|
|
||||
|[[syntax:shellvars#OPTARG|OPTARG]]|This variable is set to any argument for an option found by ''getopts''. It also contains the option flag of an unknown option.|
|
||||
|[[syntax:shellvars#OPTERR|OPTERR]]|(Values 0 or 1) Indicates if Bash should display error messages generated by the ''getopts'' builtin. The value is initialized to **1** on every shell startup - so be sure to always set it to **0** if you don't want to see annoying messages! **''OPTERR'' is not specified by POSIX for the ''getopts'' builtin utility --- only for the C ''getopt()'' function in ''unistd.h'' (''opterr'').** ''OPTERR'' is bash-specific and not supported by shells such as ksh93, mksh, zsh, or dash. |
|
||||
|
||||
''getopts'' also uses these variables for error reporting (they're set to value-combinations which arent possible in normal operation).
|
||||
==== Specify what you want ====
|
||||
|
||||
The base-syntax for ''getopts'' is:
|
||||
<code>
|
||||
getopts OPTSTRING VARNAME [ARGS...]
|
||||
</code>
|
||||
where:
|
||||
^''OPTSTRING''|tells ''getopts'' which options to expect and where to expect arguments (see below)|
|
||||
^''VARNAME''|tells ''getopts'' which shell-variable to use for option reporting|
|
||||
^''ARGS''|tells ''getopts'' to parse these optional words instead of the positional parameters|
|
||||
|
||||
=== The option-string ===
|
||||
|
||||
The option-string tells ''getopts'' which options to expect and which of them must have an argument. The syntax is very simple --- every option character is simply named as is, this example-string would tell ''getopts'' to look for ''-f'', ''-A'' and ''-x'':
|
||||
<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:
|
||||
<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.
|
||||
|
||||
=== Custom arguments to parse ===
|
||||
|
||||
The ''getopts'' utility parses the [[scripting:posparams|positional parameters]] of the current shell or function by default (which means it parses ''"$@"'').
|
||||
|
||||
You can give your own set of arguments to the utility to parse. Whenever additional arguments are given after the ''VARNAME'' parameter, ''getopts'' doesn't try to parse the positional parameters, but these given words.
|
||||
|
||||
This way, you are able to parse any option set you like, here for example from an array:
|
||||
|
||||
<code>
|
||||
while getopts :f:h opt "${MY_OWN_SET[@]}"; do
|
||||
...
|
||||
done
|
||||
</code>
|
||||
|
||||
A call to ''getopts'' **without** these additional arguments is **equivalent** to explicitly calling it with ''"$@"'':
|
||||
<code>
|
||||
getopts ... "$@"
|
||||
</code>
|
||||
|
||||
==== Error Reporting ====
|
||||
|
||||
Regarding error-reporting, there are two modes ''getopts'' can run in:
|
||||
* verbose mode
|
||||
* silent mode
|
||||
|
||||
For productive scripts I recommend to use the silent mode, since everything looks more professional, when you don't see annoying standard messages. Also it's easier to handle, since the failure cases are indicated in an easier way.
|
||||
|
||||
=== Verbose Mode ===
|
||||
|
||||
^invalid option|''VARNAME'' is set to ''?'' (question-mark) and ''OPTARG'' is unset|
|
||||
^required argument not found|''VARNAME'' is set to ''?'' (question-mark), ''OPTARG'' is unset and an //error message is printed//|
|
||||
|
||||
|
||||
=== Silent Mode ===
|
||||
|
||||
^invalid option|''VARNAME'' is set to ''?'' (question-mark) and ''OPTARG'' is set to the (invalid) option character|
|
||||
^required argument not found|''VARNAME'' is set to '':'' (colon) and ''OPTARG'' contains the option-character in question|
|
||||
|
||||
===== Using it =====
|
||||
|
||||
==== A first example ====
|
||||
|
||||
Enough said - action!
|
||||
|
||||
Let's play with a very simple case: only one option (''-a'') expected, without any arguments. Also we disable the //verbose error handling// by preceding the whole option string with a colon ('':''):
|
||||
<code bash>
|
||||
#!/bin/bash
|
||||
|
||||
while getopts ":a" opt; do
|
||||
case $opt in
|
||||
a)
|
||||
echo "-a was triggered!" >&2
|
||||
;;
|
||||
\?)
|
||||
echo "Invalid option: -$OPTARG" >&2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
</code>
|
||||
|
||||
I put that into a file named ''go_test.sh'', which is the name you'll see below in the examples.
|
||||
|
||||
Let's do some tests:
|
||||
|
||||
=== Calling it without any arguments ===
|
||||
|
||||
<code>
|
||||
$ ./go_test.sh
|
||||
$
|
||||
</code>
|
||||
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 ===
|
||||
|
||||
<code>
|
||||
$ ./go_test.sh /etc/passwd
|
||||
$
|
||||
</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.
|
||||
|
||||
The arguments given to your script are of course accessible as ''$1'' - ''${N}''.
|
||||
|
||||
=== Calling it with option-arguments ===
|
||||
|
||||
Now let's trigger ''getopts'': Provide options.
|
||||
|
||||
First, an **invalid** one:
|
||||
<code>
|
||||
$ ./go_test.sh -b
|
||||
Invalid option: -b
|
||||
$
|
||||
</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.
|
||||
|
||||
Now, a **valid** one (''-a''):
|
||||
<code>
|
||||
$ ./go_test.sh -a
|
||||
-a was triggered!
|
||||
$
|
||||
</code>
|
||||
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:
|
||||
<code>
|
||||
$ ./go_test.sh -a -x -b -c
|
||||
-a was triggered!
|
||||
Invalid option: -x
|
||||
Invalid option: -b
|
||||
Invalid option: -c
|
||||
$
|
||||
</code>
|
||||
|
||||
Finally, it's of course possible, to give our option **multiple times**:
|
||||
<code>
|
||||
$ ./go_test.sh -a -a -a -a
|
||||
-a was triggered!
|
||||
-a was triggered!
|
||||
-a was triggered!
|
||||
-a was triggered!
|
||||
$
|
||||
</code>
|
||||
|
||||
The last examples lead us to some points you may consider:
|
||||
* **invalid options don't stop the processing**: If you want to stop the script, you have to do it yourself (''exit'' in the right place)
|
||||
* **multiple identical options are possible**: If you want to disallow these, you have to check manually (e.g. by setting a variable or so)
|
||||
|
||||
==== An option with argument ====
|
||||
|
||||
Let's extend our example from above. Just a little bit:
|
||||
* ''-a'' now takes an argument
|
||||
* on an error, the parsing exits with ''exit 1''
|
||||
|
||||
<code bash>
|
||||
#!/bin/bash
|
||||
|
||||
while getopts ":a:" opt; do
|
||||
case $opt in
|
||||
a)
|
||||
echo "-a was triggered, Parameter: $OPTARG" >&2
|
||||
;;
|
||||
\?)
|
||||
echo "Invalid option: -$OPTARG" >&2
|
||||
exit 1
|
||||
;;
|
||||
:)
|
||||
echo "Option -$OPTARG requires an argument." >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
</code>
|
||||
|
||||
Let's do the very same tests we did in the last example:
|
||||
|
||||
=== Calling it without any arguments ===
|
||||
|
||||
<code>
|
||||
$ ./go_test.sh
|
||||
$
|
||||
</code>
|
||||
|
||||
As above, nothing happened. It wasn't triggered.
|
||||
|
||||
=== Calling it with non-option arguments ===
|
||||
|
||||
<code>
|
||||
$ ./go_test.sh /etc/passwd
|
||||
$
|
||||
</code>
|
||||
|
||||
The **very same** case: It wasn't triggered.
|
||||
|
||||
=== Calling it with option-arguments ===
|
||||
|
||||
**Invalid** option:
|
||||
|
||||
<code>
|
||||
$ ./go_test.sh -b
|
||||
Invalid option: -b
|
||||
$
|
||||
</code>
|
||||
|
||||
As expected, as above, ''getopts'' didn't accept this option and acted like programmed.
|
||||
|
||||
**Valid** option, but without the mandatory **argument**:
|
||||
|
||||
<code>
|
||||
$ ./go_test.sh -a
|
||||
Option -a requires an argument.
|
||||
$
|
||||
</code>
|
||||
|
||||
The option was okay, but there is an argument missing.
|
||||
|
||||
Let's provide **the argument**:
|
||||
|
||||
<code>
|
||||
$ ./go_test.sh -a /etc/passwd
|
||||
-a was triggered, Parameter: /etc/passwd
|
||||
$
|
||||
</code>
|
||||
|
||||
===== See also =====
|
||||
* Internal: [[scripting:posparams]]
|
||||
* Internal: [[syntax:ccmd:case]]
|
||||
* Internal: [[syntax:ccmd:while_loop]]
|
||||
* POSIX [[http://pubs.opengroup.org/onlinepubs/9699919799/utilities/getopts.html#tag_20_54|getopts(1)]] and [[http://pubs.opengroup.org/onlinepubs/9699919799/functions/getopt.html|getopt(3)]]
|
||||
* [[https://stackoverflow.com/questions/192249/how-do-i-parse-command-line-arguments-in-bash| parse CLI ARGV ]]
|
||||
* [[http://mywiki.wooledge.org/BashFAQ/035|handle command-line arguments (options) to a script]]
|
153
original_source/howto/mutex.txt
Normal file
153
original_source/howto/mutex.txt
Normal file
@ -0,0 +1,153 @@
|
||||
====== Lock your script (against parallel execution) ======
|
||||
|
||||
{{keywords>bash shell scripting mutex locking run-control}}
|
||||
|
||||
===== Why lock? =====
|
||||
|
||||
Sometimes there's a need to ensure only one copy of a script runs, i.e prevent two or more copies running simultaneously. Imagine an important cronjob doing something very important, which will fail or corrupt data if two copies of the called program were to run at the same time. To prevent this, a form of ''MUTEX'' (**mutual exclusion**) lock is needed.
|
||||
|
||||
The basic procedure is simple: The script checks if a specific condition (locking) is present at startup, if yes, it's locked - the scipt doesn't start.
|
||||
|
||||
This article describes locking with common UNIX(r) tools. There are other special locking tools available, But they're not standardized, or worse yet, you can't be sure they're present when you want to run your scripts. **A tool designed for specifically for this purpose does the job much better than general purpose code.**
|
||||
|
||||
==== Other, special locking tools ====
|
||||
|
||||
As told above, a special tool for locking is the preferred solution. Race conditions are avoided, as is the need to work around specific limits.
|
||||
|
||||
* ''flock'': http://www.kernel.org/pub/software/utils/script/flock/
|
||||
* ''solo'': http://timkay.com/solo/
|
||||
|
||||
===== Choose the locking method =====
|
||||
|
||||
The best way to set a global lock condition is the UNIX(r) filesystem. Variables aren't enough, as each process has its own private variable space, but the filesystem is global to all processes (yes, I know about chroots, namespaces, ... special case).
|
||||
You can "set" several things in the filesystem that can be used as locking indicator:
|
||||
|
||||
* create files
|
||||
* update file timestamps
|
||||
* create directories
|
||||
|
||||
|
||||
To create a file or set a file timestamp, usually the command touch is used. The following problem is implied:
|
||||
A locking mechanism checks for the existance of the lockfile, if no lockfile exists, it creates one and continues. Those are **two separate steps**! That means it's **not an atomic operation**. There's a small amount of time between checking and creating, where another instance of the same script could perform locking (because when it checked, the lockfile wasn't there)! In that case you would have 2 instances of the script running, both thinking they are succesfully locked, and can operate without colliding.
|
||||
Setting the timestamp is similar: One step to check the timespamp, a second step to set the timestamp.
|
||||
|
||||
<WRAP center round tip 60%>
|
||||
__**Conclusion:**__ We need an operation that does the check and the locking in one step.
|
||||
</WRAP>
|
||||
|
||||
A simple way to get that is to create a **lock directory** - with the mkdir command. It will:
|
||||
|
||||
* create a given directory only if it does not exist, and set a successful exit code
|
||||
* it will set an unsuccesful exit code if an error occours - for example, if the directory specified already exists
|
||||
|
||||
|
||||
With mkdir it seems, we have our two steps in one simple operation. A (very!) simple locking code might look like this:
|
||||
<code bash>
|
||||
if mkdir /var/lock/mylock; then
|
||||
echo "Locking succeeded" >&2
|
||||
else
|
||||
echo "Lock failed - exit" >&2
|
||||
exit 1
|
||||
fi
|
||||
</code>
|
||||
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//
|
||||
|
||||
**Note:** While perusing the Internet, I found some people asking if the ''mkdir'' method works "on all filesystems". Well, let's say it should. The syscall under ''mkdir'' is guarenteed to work atomicly in all cases, at least on Unices. Two examples of problems are NFS filesystems and filesystems on cluster servers. With those two scenarios, dependencies exist related to the mount options and implementation. However, I successfully use this simple method on an Oracle OCFS2 filesystem in a 4-node cluster environment. So let's just say "it should work under normal conditions".
|
||||
|
||||
Another atomic method is setting the ''noclobber'' shell option (''set -C''). That will cause redirection to fail, if the file the redirection points to already exists (using diverse ''open()'' methods). Need to write a code example here.
|
||||
|
||||
<code bash>
|
||||
|
||||
if ( set -o noclobber; echo "locked" > "$lockfile") 2> /dev/null; then
|
||||
trap 'rm -f "$lockfile"; exit $?' INT TERM EXIT
|
||||
echo "Locking succeeded" >&2
|
||||
rm -f "$lockfile"
|
||||
else
|
||||
echo "Lock failed - exit" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
</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]].
|
||||
===== An example =====
|
||||
|
||||
This code was taken from a production grade script that controls PISG to create statistical pages from my IRC logfiles.
|
||||
There are some differences compared to the very simple example above:
|
||||
|
||||
* the locking stores the process ID of the locked instance
|
||||
* if a lock fails, the script tries to find out if the locked instance still is active (unreliable!)
|
||||
* traps are created to automatically remove the lock when the script terminates, or is killed
|
||||
|
||||
|
||||
Details on how the script is killed aren't given, only code relevant to the locking process is shown:
|
||||
<code bash>
|
||||
#!/bin/bash
|
||||
|
||||
# lock dirs/files
|
||||
LOCKDIR="/tmp/statsgen-lock"
|
||||
PIDFILE="${LOCKDIR}/PID"
|
||||
|
||||
# exit codes and text
|
||||
ENO_SUCCESS=0; ETXT[0]="ENO_SUCCESS"
|
||||
ENO_GENERAL=1; ETXT[1]="ENO_GENERAL"
|
||||
ENO_LOCKFAIL=2; ETXT[2]="ENO_LOCKFAIL"
|
||||
ENO_RECVSIG=3; ETXT[3]="ENO_RECVSIG"
|
||||
|
||||
###
|
||||
### start locking attempt
|
||||
###
|
||||
|
||||
trap 'ECODE=$?; echo "[statsgen] Exit: ${ETXT[ECODE]}($ECODE)" >&2' 0
|
||||
echo -n "[statsgen] Locking: " >&2
|
||||
|
||||
if mkdir "${LOCKDIR}" &>/dev/null; then
|
||||
|
||||
# lock succeeded, install signal handlers before storing the PID just in case
|
||||
# storing the PID fails
|
||||
trap 'ECODE=$?;
|
||||
echo "[statsgen] Removing lock. Exit: ${ETXT[ECODE]}($ECODE)" >&2
|
||||
rm -rf "${LOCKDIR}"' 0
|
||||
echo "$$" >"${PIDFILE}"
|
||||
# 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!
|
||||
trap 'echo "[statsgen] Killed by a signal." >&2
|
||||
exit ${ENO_RECVSIG}' 1 2 3 15
|
||||
echo "success, installed signal handlers"
|
||||
|
||||
else
|
||||
|
||||
# lock failed, check if the other PID is alive
|
||||
OTHERPID="$(cat "${PIDFILE}")"
|
||||
|
||||
# if cat isn't able to read the file, another instance is probably
|
||||
# about to remove the lock -- exit, we're *still* locked
|
||||
# Thanks to Grzegorz Wierzowiecki for pointing out this race condition on
|
||||
# http://wiki.grzegorz.wierzowiecki.pl/code:mutex-in-bash
|
||||
if [ $? != 0 ]; then
|
||||
echo "lock failed, PID ${OTHERPID} is active" >&2
|
||||
exit ${ENO_LOCKFAIL}
|
||||
fi
|
||||
|
||||
if ! kill -0 $OTHERPID &>/dev/null; then
|
||||
# lock is stale, remove it and restart
|
||||
echo "removing stale lock of nonexistant PID ${OTHERPID}" >&2
|
||||
rm -rf "${LOCKDIR}"
|
||||
echo "[statsgen] restarting myself" >&2
|
||||
exec "$0" "$@"
|
||||
else
|
||||
# lock is valid and OTHERPID is active - exit, we're locked!
|
||||
echo "lock failed, PID ${OTHERPID} is active" >&2
|
||||
exit ${ENO_LOCKFAIL}
|
||||
fi
|
||||
|
||||
fi
|
||||
</code>
|
||||
|
||||
===== Related links =====
|
||||
|
||||
* [[http://mywiki.wooledge.org/BashFAQ/045 | BashFAQ/045]]
|
||||
* [[http://wiki.grzegorz.wierzowiecki.pl/code:mutex-in-bash | Implementation of a shell locking utility]]
|
||||
* [[http://en.wikipedia.org/wiki/File_locking | Wikipedia article on File Locking]], including a discussion of potential [[http://en.wikipedia.org/wiki/File_locking#Problems | problems ]] with flock and certain versions of NFS.
|
327
original_source/howto/obsolete.txt
Normal file
327
original_source/howto/obsolete.txt
Normal file
File diff suppressed because one or more lines are too long
310
original_source/howto/pax.txt
Normal file
310
original_source/howto/pax.txt
Normal file
@ -0,0 +1,310 @@
|
||||
====== pax - the POSIX archiver ======
|
||||
|
||||
{{keywords>bash shell scripting POSIX archive tar packing zip}}
|
||||
|
||||
pax can do a lot of fancy stuff, feel free to contribute more awesome pax tricks!
|
||||
|
||||
===== Introduction =====
|
||||
|
||||
The POSIX archiver, ''pax'', is an attempt at a standardized archiver with
|
||||
the best features of ''tar'' and ''cpio'', able to handle all common archive types.
|
||||
|
||||
However, this is **not a manpage**, it will **not** list all possible options,
|
||||
it will **not** you detailed information about ''pax''. It's only an introduction.
|
||||
|
||||
This article is based on the debianized Berkeley implementation of ''pax'', but implementation-specific things should be tagged as such. Unfortunately, the Debian package doesn't seem to be maintained anymore.
|
||||
|
||||
|
||||
===== Overview =====
|
||||
|
||||
==== Operation modes ====
|
||||
|
||||
There are four basic operation modes to //list//, //read//, //write// and
|
||||
//copy// archives. They're switched with combinations of ''-r'' and ''-w''
|
||||
command line options:
|
||||
|
||||
^ Mode ^ RW-Options ^
|
||||
| List | //no RW-options// |
|
||||
| Read | ''-r'' |
|
||||
| Write | ''-w'' |
|
||||
| Copy | ''-r -w'' |
|
||||
|
||||
|
||||
=== List ===
|
||||
|
||||
In //list mode//, ''pax'' writes the list of archive members to standard
|
||||
output (a table of contents). If a pattern match is specified on the
|
||||
command line, only matching filenames are printed.
|
||||
|
||||
=== Read ===
|
||||
|
||||
//Read// an archive. ''pax'' will read archive data and extract the members to the
|
||||
current directory. If a pattern match is specified on the command line, only matching
|
||||
filenames are extracted.
|
||||
|
||||
When reading an archive, the archive type is determined from the archive data.
|
||||
|
||||
=== Write ===
|
||||
|
||||
//Write// an archive, which means create a new one or append to an
|
||||
existing one. All files and directories specified on the command line are inserted into
|
||||
the archive. The archive is written to standard output by default.
|
||||
|
||||
If no files are specified on the command line, filenames are read from ''STDIN''.
|
||||
|
||||
The write mode is the only mode where you need to specify the archive type
|
||||
with ''-x <TYPE>'', e.g. ''-x ustar''.
|
||||
|
||||
=== Copy ===
|
||||
|
||||
//Copy// mode is similar to ''cpio'' passthrough mode. It provides a way to replicate a complete or partial file hierarchy
|
||||
(with all the ''pax'' options, e.g. rewriting groups) to another location.
|
||||
|
||||
==== Archive data ====
|
||||
|
||||
When you don't specify anything special, ''pax'' will attempt to read archive
|
||||
data from standard input (read/list modes) and write archive data to
|
||||
standard output (write mode). This ensures ''pax'' can be easily
|
||||
used as part of a shell pipe construct, e.g. to read a compressed
|
||||
archive that's decompressed in the pipe.
|
||||
|
||||
The option to specify the pathname of a file to be archived is ''-f''
|
||||
This file will be used as input or output, depending on the operation
|
||||
(read/write/list).
|
||||
|
||||
When pax reads an archive, it tries to guess the archive type.
|
||||
However, in //write// mode, you must specify which type of archive
|
||||
to append using the ''-x <TYPE>'' switch. If you omit this switch,
|
||||
a default archive will be created (POSIX says it's implementation defined,
|
||||
Berkeley ''pax'' creates ''ustar'' if no options are specified).
|
||||
|
||||
The following archive formats are supported (Berkeley implementation):
|
||||
|ustar |POSIX TAR format (default)|
|
||||
|cpio |POSIX CPIO format |
|
||||
|tar |classic BSD TAR format |
|
||||
|bcpio |old binary CPIO format |
|
||||
|sv4cpio |SVR4 CPIO format |
|
||||
|sv4crc |SVR4 CPIO format with CRC |
|
||||
|
||||
Berkeley ''pax'' supports options ''-z'' and ''-j'', similar to GNU ''tar'', to filter archive files through GZIP/BZIP2.
|
||||
|
||||
==== Matching archive members ====
|
||||
|
||||
In //read// and //list// modes, you can specify patterns to determine which files to list or extract.
|
||||
|
||||
* the pattern notation is the one known by a POSIX-shell, i.e. the one known by Bash without ''extglob''
|
||||
* if the specified pattern matches a complete directory, it affects all files and subdirectories of the specified directory
|
||||
* if you specify the ''-c'' option, ''pax'' will invert the matches, i.e. it matches all filenames **except** those matching the specified patterns
|
||||
* if no patterns are given, ''pax'' will "match" (list or extract) all files from the archive
|
||||
* **To avoid conflicts with shell pathname expansion, it's wise to quote patterns!**
|
||||
|
||||
=== Some assorted examples of patterns ===
|
||||
|
||||
<code>
|
||||
pax -r <myarchive.tar 'data/sales/*.txt' 'data/products/*.png'
|
||||
</code>
|
||||
|
||||
<code>
|
||||
pax -r <myarchive.tar 'data/sales/year_200[135].txt'
|
||||
# should be equivalent to
|
||||
pax -r <myarchive.tar 'data/sales/year_2001.txt' 'data/sales/year_2003.txt' 'data/sales/year_2005.txt'
|
||||
</code>
|
||||
|
||||
===== Using pax =====
|
||||
|
||||
This is a brief description of using ''pax'' as a normal archiver
|
||||
system, like you would use ''tar''.
|
||||
|
||||
|
||||
==== Creating an archive ====
|
||||
|
||||
This task is done with basic syntax
|
||||
<code>
|
||||
# archive contents to stdout
|
||||
pax -w >archive.tar README.txt *.png data/
|
||||
|
||||
# equivalent, extract archive contents directly to a file
|
||||
pax -w -x ustar -f archive.tar README.txt *.png data/
|
||||
</code>
|
||||
|
||||
''pax'' is in //write// mode, the given filenames are packed into an
|
||||
archive:
|
||||
* ''README.txt'' is a normal file, it will be packed
|
||||
* ''*.png'' is a pathname glob **for your shell**, the shell will substitute all matching filenames **before** ''pax'' is executed. The result is a list of filenames that will be packed like the ''README.txt'' example above
|
||||
* ''data/'' is a directory. **Everything** in this directory will be packed into the archive, i.e. not just an empty directory
|
||||
|
||||
When you specify the ''-v'' option, ''pax'' will write the pathnames of the
|
||||
files inserted into the archive to ''STDERR''.
|
||||
|
||||
When, and only when, no filename arguments are specified, ''pax'' attempts to
|
||||
read filenames from ''STDIN'', separated by newlines.
|
||||
This way you can easily combine ''find'' with ''pax'':
|
||||
<code>
|
||||
find . -name '*.txt' | pax -wf textfiles.tar -x ustar
|
||||
</code>
|
||||
|
||||
==== Listing archive contents ====
|
||||
|
||||
The standard output format to list archive members simply is to print each
|
||||
filename to a separate line. But the output format can be customized to include
|
||||
permissions, timestamps, etc. with the ''-o listopt=<FORMAT>'' specification.
|
||||
The syntax of the format specification is strongly derived from the
|
||||
''printf(3)'' format specification.
|
||||
|
||||
**Unfortunately** the ''pax'' utility delivered with Debian doesn't seem to
|
||||
support these extended listing formats.
|
||||
|
||||
However, ''pax'' lists archive members in a ''ls -l''-like format, when you
|
||||
give the ''-v'' option:
|
||||
<code>
|
||||
pax -v <myarchive.tar
|
||||
# or, of course
|
||||
pax -vf myarchive.tar
|
||||
</code>
|
||||
|
||||
==== Extracting from an archive ====
|
||||
|
||||
You can extract all files, or files (not) matching specific patterns from an
|
||||
archive using constructs like:
|
||||
<code>
|
||||
# "normal" extraction
|
||||
pax -rf myarchive.tar '*.txt'
|
||||
</code>
|
||||
|
||||
<code>
|
||||
# with inverted pattern
|
||||
pax -rf myarchive.tar -c '*.txt'
|
||||
</code>
|
||||
|
||||
|
||||
==== Copying files ====
|
||||
|
||||
To copy directory contents to another directory, similar to a
|
||||
''cp -a'' command, use:
|
||||
<code>
|
||||
mkdir destdir
|
||||
pax -rw dir destdir #creates a copy of dir in destdir/, i.e. destdir/dir
|
||||
</code>
|
||||
|
||||
|
||||
|
||||
|
||||
==== Copying files via ssh ====
|
||||
|
||||
To copy directory contents to another directory on a remote system, use:
|
||||
<code>
|
||||
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
|
||||
</code>
|
||||
These commands create a copy of localdir in distandir (distantdir/dir) on the remote machine.
|
||||
|
||||
===== Advanced usage =====
|
||||
|
||||
|
||||
==== Backup your daily work ====
|
||||
|
||||
__**Note:**__ ''-T'' is an extension and is not defined by POSIX.
|
||||
|
||||
Say you have write-access to a fileserver mounted on your filesystem tree.
|
||||
In //copy// mode, you can tell ''pax'' to copy only files that were
|
||||
modified today:
|
||||
<code>
|
||||
mkdir /n/mybackups/$(date +%A)/
|
||||
pax -rw -T 0000 data/ /n/mybackups/$(date +%A)/
|
||||
</code>
|
||||
This is done using the ''-T'' switch, which normally allows you to specify a
|
||||
time window, but in this case, only the start time which means "today at midnight".
|
||||
|
||||
When you execute this "very simple backup" after your daily work,
|
||||
you will have a copy of the modified files.
|
||||
|
||||
__**Note:**__ The ''%A'' format from ''date'' expands to the name of the
|
||||
current day, localized, e.g. "Friday" (en) or "Mittwoch" (de).
|
||||
|
||||
The same, but with an archive, can be accomplished by:
|
||||
<code>
|
||||
pax -w -T 0000 -f /n/mybackups/$(date +%A)
|
||||
</code>
|
||||
In this case, the day-name is an archive-file (you don't need a filename
|
||||
extension like ''.tar'' but you can add one, if desired).
|
||||
|
||||
|
||||
|
||||
==== Changing filenames while archiving ====
|
||||
|
||||
''pax'' is able to rewrite filenames while archiving or while extracting from an archive. This example creates a tar archive containing the ''holiday_2007/'' directory, but the directory name inside the archive will be ''holiday_pics/'':
|
||||
<code>
|
||||
pax -x ustar -w -f holiday_pictures.tar -s '/^holiday_2007/holiday_pics/' holiday_2007/
|
||||
</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 optional ''g'' and ''p'' flags are used to apply substitution **(g)**lobally to the line or to **(p)**rint the original and rewritten strings to ''STDERR''.
|
||||
|
||||
Multiple ''-s'' options can be specified on the command line. They are applied to the pathname strings of the files or archive members. This happens in the order they are specified.
|
||||
|
||||
==== Excluding files from an archive ====
|
||||
|
||||
The -s command seen above can be used to exclude a file. The substitution must result in a null string:
|
||||
For example, let's say that you want to exclude all the CVS directories to create a source code archive.
|
||||
We are going to replace the names containing /CVS/ with nothing, note the .* they are needed because we need to match the entire pathname.
|
||||
<code>
|
||||
pax -w -x ustar -f release.tar -s',.*/CVS/.*,,' myapplication
|
||||
</code>
|
||||
You can use several -s options, for instance, let's say you also want to remove files ending in ~:
|
||||
<code>
|
||||
pax -w -x ustar -f release.tar -'s,.*/CVS/.*,,' -'s/.*~//' myapplication
|
||||
</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:
|
||||
<code>
|
||||
pax -r -f archive.tar -s',^etc/.*,,' #the etc/ dir is not extracted
|
||||
</code>
|
||||
|
||||
==== 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:
|
||||
|
||||
<code>
|
||||
# Back up config files changed less than 3 days ago
|
||||
find /etc -type f -mtime -3 | pax -x ustar -w -f /backups/etc.tar
|
||||
|
||||
# Copy only the directories, not the files
|
||||
mkdir /target
|
||||
find . -type d -print | pax -r -w -d /target
|
||||
|
||||
# Back up anything that changed since the last backup
|
||||
find . -newer /var/run/mylastbackup -print0 |
|
||||
pax -0 -x ustar -w -d -f /backups/mybackup.tar
|
||||
touch /var/run/mylastbackup
|
||||
</code>
|
||||
|
||||
The ''-d'' option tells pax ''not'' to recurse into directories it reads (''cpio''-style). Without ''-d'', pax recurses into all directories (''tar''-style).
|
||||
|
||||
**Note**: the ''-0'' option is not standard, but is present in some implementations.
|
||||
|
||||
===== From tar to pax =====
|
||||
|
||||
''pax'' can handle the ''tar'' archive format, if you want to switch to the standard tool an alias like:
|
||||
<code>
|
||||
alias tar='echo USE PAX, idiot. pax is the standard archiver!; # '
|
||||
</code>
|
||||
in your ''~/.bashrc'' can be useful :-D.
|
||||
|
||||
Here is a quick table comparing (GNU) ''tar'' and ''pax'' to help you to make the switch:
|
||||
^ TAR ^ PAX ^ Notes ^
|
||||
| ''tar xzvf file.tar.gz'' | ''pax -rvz -f file.tar.gz'' | ''-z'' is an extension, POSIXly: ''gunzip <file.tar.gz %%|%% pax -rv'' |
|
||||
| ''tar czvf archive.tar.gz path ...'' | ''pax -wvz -f archive.tar.gz path ...''| ''-z'' is an extension, POSIXly: ''pax -wv path %%|%% gzip > archive.tar.gz'' |
|
||||
| ''tar xjvf file.tar.bz2'' | ''bunzip2 <file.tar.bz2 %%|%% pax -rv'' | |
|
||||
| ''tar cjvf archive.tar.bz2 path ...'' | ''pax -wv path %%|%% bzip2 > archive.tar.bz2'' | |
|
||||
| ''tar tzvf file.tar.gz'' | ''pax -vz -f file.tar.gz'' | ''-z'' is an extension, POSIXly: ''gunzip <file.tar.gz %%|%% pax -v'' |
|
||||
|
||||
''pax'' might not create ustar (''tar'') archives by default but its own pax format, add ''-x ustar'' if you want to ensure pax creates tar archives!
|
||||
|
||||
===== Implementations =====
|
||||
|
||||
* [[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://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.
|
||||
* [[http://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html | SUS pax specification]]
|
711
original_source/howto/redirection_tutorial.txt
Normal file
711
original_source/howto/redirection_tutorial.txt
Normal file
@ -0,0 +1,711 @@
|
||||
|
||||
|
||||
====== Illustrated Redirection Tutorial ======
|
||||
|
||||
{{keywords>bash shell scripting tutorial redirection redirect file descriptor}}
|
||||
|
||||
This tutorial is not a complete guide to redirection, it will not
|
||||
cover here docs, here strings, name pipes etc... I just hope it'll help
|
||||
you to understand what things like ''3>&2'', ''2>&1'' or ''1>&3-'' do.
|
||||
|
||||
|
||||
====== stdin, stdout, stderr ======
|
||||
|
||||
When Bash starts, normally, 3 file descriptors are opened, ''0'', ''1'' and ''2''
|
||||
also known as standard input (''stdin''), standard output (''stdout'') and
|
||||
standard error (''stderr'').
|
||||
|
||||
For example, with Bash running in a Linux terminal emulator, you'll see:
|
||||
|
||||
<code>
|
||||
# lsof +f g -ap $BASHPID -d 0,1,2
|
||||
COMMAND PID USER FD TYPE FILE-FLAG DEVICE SIZE/OFF NODE NAME
|
||||
bash 12135 root 0u CHR RW,LG 136,13 0t0 16 /dev/pts/5
|
||||
bash 12135 root 1u CHR RW,LG 136,13 0t0 16 /dev/pts/5
|
||||
bash 12135 root 2u CHR RW,LG 136,13 0t0 16 /dev/pts/5
|
||||
</code>
|
||||
|
||||
This ''/dev/pts/5'' is a pseudo terminal used to emulate a real
|
||||
terminal. Bash reads (''stdin'') from this terminal and prints
|
||||
via ''stdout'' and ''stderr'' to this terminal.
|
||||
|
||||
<code>
|
||||
--- +-----------------------+
|
||||
standard input ( 0 ) ---->| /dev/pts/5 |
|
||||
--- +-----------------------+
|
||||
|
||||
--- +-----------------------+
|
||||
standard output ( 1 ) ---->| /dev/pts/5 |
|
||||
--- +-----------------------+
|
||||
|
||||
--- +-----------------------+
|
||||
standard error ( 2 ) ---->| /dev/pts/5 |
|
||||
--- +-----------------------+
|
||||
</code>
|
||||
|
||||
When a command, a compound command, a subshell etc. is executed, it inherits
|
||||
these file descriptors. For instance ''echo foo'' will send the text ''foo'' to the file
|
||||
descriptor ''1'' inherited from the shell, which is connected to ''/dev/pts/5''.
|
||||
|
||||
====== Simple Redirections ======
|
||||
===== Output Redirection "n> file" =====
|
||||
|
||||
''>'' is probably the simplest redirection.
|
||||
|
||||
''echo foo > file''
|
||||
|
||||
the ''> file'' after the command alters the file descriptors belonging to the
|
||||
command ''echo''. It changes the file descriptor ''1'' (''> file'' is the same as
|
||||
''1>file'') so that it points to the file ''file''. They will look like:
|
||||
|
||||
<code>
|
||||
--- +-----------------------+
|
||||
standard input ( 0 ) ---->| /dev/pts/5 |
|
||||
--- +-----------------------+
|
||||
|
||||
--- +-----------------------+
|
||||
standard output ( 1 ) ---->| file |
|
||||
--- +-----------------------+
|
||||
|
||||
--- +-----------------------+
|
||||
standard error ( 2 ) ---->| /dev/pts/5 |
|
||||
--- +-----------------------+
|
||||
</code>
|
||||
|
||||
Now characters written by our command, ''echo'', that are sent to the
|
||||
standard output, i.e., the file descriptor ''1'', end up in the file
|
||||
named ''file''.
|
||||
|
||||
In the same way, command ''2> file'' will change the standard error and
|
||||
will make it point to ''file''. Standard error is used by applications to print errors.
|
||||
|
||||
What will ''command 3> file'' do? It will open a new file descriptor
|
||||
pointing to ''file''. The command will then start with:
|
||||
|
||||
<code>
|
||||
--- +-----------------------+
|
||||
standard input ( 0 ) ---->| /dev/pts/5 |
|
||||
--- +-----------------------+
|
||||
|
||||
--- +-----------------------+
|
||||
standard output ( 1 ) ---->| /dev/pts/5 |
|
||||
--- +-----------------------+
|
||||
|
||||
--- +-----------------------+
|
||||
standard error ( 2 ) ---->| /dev/pts/5 |
|
||||
--- +-----------------------+
|
||||
|
||||
--- +-----------------------+
|
||||
new descriptor ( 3 ) ---->| file |
|
||||
--- +-----------------------+
|
||||
</code>
|
||||
|
||||
What will the command do with this descriptor? It depends. Often nothing.
|
||||
We will see later why we might want other file descriptors.
|
||||
|
||||
===== Input Redirection "n< file" =====
|
||||
|
||||
When you run a commandusing ''command < file'', it changes the
|
||||
file descriptor ''0'' so that it looks like:
|
||||
|
||||
<code>
|
||||
--- +-----------------------+
|
||||
standard input ( 0 ) <----| file |
|
||||
--- +-----------------------+
|
||||
|
||||
--- +-----------------------+
|
||||
standard output ( 1 ) ---->| /dev/pts/5 |
|
||||
--- +-----------------------+
|
||||
|
||||
--- +-----------------------+
|
||||
standard error ( 2 ) ---->| /dev/pts/5 |
|
||||
--- +-----------------------+
|
||||
</code>
|
||||
|
||||
If the command reads from ''stdin'', it now will read from ''file'' and not
|
||||
from the console.
|
||||
|
||||
As with ''>'', ''<'' can be used to open a new file descriptor for reading,
|
||||
''command 3<file''. Later we will see how this can be useful.
|
||||
|
||||
|
||||
===== Pipes | =====
|
||||
|
||||
What does this ''|'' do? Among other things, it connects the standard output of
|
||||
the command on the left to the standard input of the command on the right.
|
||||
That is, it creates a special file, a pipe, which is opened as a write destinaton
|
||||
for the left command, and as a read source for the right command.
|
||||
|
||||
<code>
|
||||
echo foo | cat
|
||||
|
||||
--- +--------------+ --- +--------------+
|
||||
( 0 ) ---->| /dev/pts/5 | ------> ( 0 ) ---->|pipe (read) |
|
||||
--- +--------------+ / --- +--------------+
|
||||
/
|
||||
--- +--------------+ / --- +--------------+
|
||||
( 1 ) ---->| pipe (write) | / ( 1 ) ---->| /dev/pts |
|
||||
--- +--------------+ --- +--------------+
|
||||
|
||||
--- +--------------+ --- +--------------+
|
||||
( 2 ) ---->| /dev/pts/5 | ( 2 ) ---->| /dev/pts/ |
|
||||
--- +--------------+ --- +--------------+
|
||||
|
||||
</code>
|
||||
|
||||
This is possible because the redirections are set up by the shell
|
||||
**before** the commands are executed, and the commands inherit the file
|
||||
descriptors.
|
||||
|
||||
====== More On File Descriptors ======
|
||||
===== Duplicating File Descriptor 2>&1 =====
|
||||
|
||||
We have seen how to open (or redirect) file descriptors. Let us see
|
||||
how to duplicate them, starting with the classic ''2>&1''. What does this
|
||||
mean? That something written on the file descriptor ''2'' will go where
|
||||
file descriptor ''1'' goes. In a shell ''command 2>&1'' is not a very
|
||||
interesting example so we will use ''ls /tmp/ doesnotexist 2>&1 | less''
|
||||
|
||||
<code>
|
||||
ls /tmp/ doesnotexist 2>&1 | less
|
||||
|
||||
--- +--------------+ --- +--------------+
|
||||
( 0 ) ---->| /dev/pts/5 | ------> ( 0 ) ---->|from the pipe |
|
||||
--- +--------------+ / ---> --- +--------------+
|
||||
/ /
|
||||
--- +--------------+ / / --- +--------------+
|
||||
( 1 ) ---->| to the pipe | / / ( 1 ) ---->| /dev/pts |
|
||||
--- +--------------+ / --- +--------------+
|
||||
/
|
||||
--- +--------------+ / --- +--------------+
|
||||
( 2 ) ---->| to the pipe | / ( 2 ) ---->| /dev/pts/ |
|
||||
--- +--------------+ --- +--------------+
|
||||
</code>
|
||||
|
||||
Why is it called //duplicating//? Because after ''2>&1'', we have 2 file
|
||||
descriptors pointing to the same file. Take care not to call this
|
||||
"File Descriptor Aliasing"; if we redirect ''stdout'' after ''2>&1'' to a
|
||||
file ''B'', file descriptor ''2'' will still be opened on the file ''A'' where it was. This is
|
||||
often misunderstood by people wanting to redirect both standard input
|
||||
and standard output to the file. Continue reading for more on this.
|
||||
|
||||
So if you have two file descriptors ''s'' and ''t'' like:
|
||||
<code>
|
||||
--- +-----------------------+
|
||||
a descriptor ( s ) ---->| /some/file |
|
||||
--- +-----------------------+
|
||||
--- +-----------------------+
|
||||
a descriptor ( t ) ---->| /another/file |
|
||||
--- +-----------------------+
|
||||
</code>
|
||||
|
||||
Using a ''t>&s'' (where ''t'' and ''s'' are numbers) it means:
|
||||
|
||||
> Copy whatever file descriptor ''s'' contains into file descriptor ''t''
|
||||
|
||||
So you got a copy of this descriptor:
|
||||
|
||||
<code>
|
||||
--- +-----------------------+
|
||||
a descriptor ( s ) ---->| /some/file |
|
||||
--- +-----------------------+
|
||||
--- +-----------------------+
|
||||
a descriptor ( t ) ---->| /some/file |
|
||||
--- +-----------------------+
|
||||
</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'').
|
||||
|
||||
Note that the file reading or writing positions are also duplicated. If you have already
|
||||
read a line of ''s'', then after ''t>&s'' if you read a line from ''t'', you will
|
||||
get the second line of the file.
|
||||
|
||||
Similarly for output file descriptors, writing a line to file descriptor ''s'' will append a line to a file as will writing a line to file descriptor ''t''.
|
||||
|
||||
<note tip>The syntax is somewhat confusing in that you would think that the arrow would point in the direction of the copy, but it's reversed. So it's ''target>&source'' effectively.</note>
|
||||
|
||||
So, as a simple example (albeit slightly contrived), is the following:
|
||||
|
||||
<code>
|
||||
exec 3>&1 # Copy 1 into 3
|
||||
exec 1> logfile # Make 1 opened to write to logfile
|
||||
lotsa_stdout # Outputs to fd 1, which writes to logfile
|
||||
exec 1>&3 # Copy 3 back into 1
|
||||
echo Done # Output to original stdout
|
||||
</code>
|
||||
|
||||
|
||||
===== Order Of Redirection, i.e., "> file 2>&1" vs. "2>&1 >file" =====
|
||||
|
||||
While it doesn't matter where the redirections appears on the command
|
||||
line, their order does matter. They are set up from left to right.
|
||||
|
||||
* ''2>&1 >file''
|
||||
|
||||
A common error, is to do ''command 2>&1 > file'' to redirect both ''stderr''
|
||||
and ''stdout'' to ''file''. Let's see what's going on. First we type the
|
||||
command in our terminal, the descriptors look like this:
|
||||
|
||||
<code>
|
||||
--- +-----------------------+
|
||||
standard input ( 0 ) ---->| /dev/pts/5 |
|
||||
--- +-----------------------+
|
||||
|
||||
--- +-----------------------+
|
||||
standard output ( 1 ) ---->| /dev/pts/5 |
|
||||
--- +-----------------------+
|
||||
|
||||
--- +-----------------------+
|
||||
standard error ( 2 ) ---->| /dev/pts/5 |
|
||||
--- +-----------------------+
|
||||
</code>
|
||||
|
||||
Then our shell, Bash sees ''2>&1'' so it duplicates 1, and the file
|
||||
descriptor look like this:
|
||||
|
||||
<code>
|
||||
--- +-----------------------+
|
||||
standard input ( 0 ) ---->| /dev/pts/5 |
|
||||
--- +-----------------------+
|
||||
|
||||
--- +-----------------------+
|
||||
standard output ( 1 ) ---->| /dev/pts/5 |
|
||||
--- +-----------------------+
|
||||
|
||||
--- +-----------------------+
|
||||
standard error ( 2 ) ---->| /dev/pts/5 |
|
||||
--- +-----------------------+
|
||||
</code>
|
||||
|
||||
That's right, nothing has changed, 2 was already pointing to the same
|
||||
place as 1. Now Bash sees ''> file'' and thus changes ''stdout'':
|
||||
|
||||
<code>
|
||||
--- +-----------------------+
|
||||
standard input ( 0 ) ---->| /dev/pts/5 |
|
||||
--- +-----------------------+
|
||||
|
||||
--- +-----------------------+
|
||||
standard output ( 1 ) ---->| file |
|
||||
--- +-----------------------+
|
||||
|
||||
--- +-----------------------+
|
||||
standard error ( 2 ) ---->| /dev/pts/5 |
|
||||
--- +-----------------------+
|
||||
</code>
|
||||
|
||||
And that's not what we want.
|
||||
|
||||
* ''>file 2>&1''
|
||||
|
||||
Now let's look at the correct ''command >file 2>&1''. We start as in the
|
||||
previous example, and Bash sees ''> file'':
|
||||
|
||||
<code>
|
||||
--- +-----------------------+
|
||||
standard input ( 0 ) ---->| /dev/pts/5 |
|
||||
--- +-----------------------+
|
||||
|
||||
--- +-----------------------+
|
||||
standard output ( 1 ) ---->| file |
|
||||
--- +-----------------------+
|
||||
|
||||
--- +-----------------------+
|
||||
standard error ( 2 ) ---->| /dev/pts/5 |
|
||||
--- +-----------------------+
|
||||
</code>
|
||||
|
||||
Then it sees our duplication ''2>&1'':
|
||||
|
||||
<code>
|
||||
--- +-----------------------+
|
||||
standard input ( 0 ) ---->| /dev/pts/5 |
|
||||
--- +-----------------------+
|
||||
|
||||
--- +-----------------------+
|
||||
standard output ( 1 ) ---->| file |
|
||||
--- +-----------------------+
|
||||
|
||||
--- +-----------------------+
|
||||
standard error ( 2 ) ---->| file |
|
||||
--- +-----------------------+
|
||||
</code>
|
||||
|
||||
And voila, both ''1'' and ''2'' are redirected to file.
|
||||
|
||||
===== Why sed 's/foo/bar/' file >file Doesn't Work =====
|
||||
|
||||
This is a common error, we want to modify a file using something that
|
||||
reads from a file and writes the result to ''stdout''. To do this, we redirect stdout
|
||||
to the file we want to modify. The problem here is that, as we
|
||||
have seen, the redirections are setup before the command is actually
|
||||
executed.
|
||||
|
||||
So **BEFORE** sed starts, standard output has already been redirected, with
|
||||
the additional side effect that, because we used >, "file" gets truncated. When ''sed'' starts to
|
||||
read the file, it contains nothing.
|
||||
|
||||
|
||||
===== exec =====
|
||||
|
||||
In Bash the ''exec'' built-in replaces the shell with the specified
|
||||
program. So what does this have to do with redirection?
|
||||
''exec'' also allow us to manipulate the file descriptors.
|
||||
If you don't specify a program, the redirection after ''exec'' modifies the
|
||||
file descriptors of the current shell.
|
||||
|
||||
For example, all the commands after ''exec 2>file'' will have file
|
||||
descriptors like:
|
||||
<code>
|
||||
--- +-----------------------+
|
||||
standard input ( 0 ) ---->| /dev/pts/5 |
|
||||
--- +-----------------------+
|
||||
|
||||
--- +-----------------------+
|
||||
standard output ( 1 ) ---->| /dev/pts/5 |
|
||||
--- +-----------------------+
|
||||
|
||||
--- +-----------------------+
|
||||
standard error ( 2 ) ---->| file |
|
||||
--- +-----------------------+
|
||||
</code>
|
||||
|
||||
All the the errors sent to ''stderr'' by the commands after the ''exec 2>file''
|
||||
will go to the file, just as if you had the command in a script and
|
||||
ran ''myscript 2>file''.
|
||||
|
||||
''exec'' can be used, if, for instance, you want to log the errors the
|
||||
commands in your script produce, just add ''exec 2>myscript.errors'' at
|
||||
the beginning of your script.
|
||||
|
||||
Let's see another use case. We want to read a file line by line, this
|
||||
is easy, we just do:
|
||||
|
||||
<code>
|
||||
while read -r line;do echo "$line";done < file
|
||||
</code>
|
||||
|
||||
Now, we want, after printing each line, to do a pause, waiting for
|
||||
the user to press a key:
|
||||
|
||||
<code>
|
||||
while read -r line;do echo "$line"; read -p "Press any key" -n 1;done < file
|
||||
</code>
|
||||
|
||||
And, surprise this doesn't work. Why? because the shell descriptor of
|
||||
the while loop looks like:
|
||||
|
||||
<code>
|
||||
--- +-----------------------+
|
||||
standard input ( 0 ) ---->| file |
|
||||
--- +-----------------------+
|
||||
|
||||
--- +-----------------------+
|
||||
standard output ( 1 ) ---->| /dev/pts/5 |
|
||||
--- +-----------------------+
|
||||
|
||||
--- +-----------------------+
|
||||
standard error ( 2 ) ---->| /dev/pts/5 |
|
||||
--- +-----------------------+
|
||||
</code>
|
||||
|
||||
and our read inherits these descriptors, and our command (''read -p "Press any key" -n 1'')
|
||||
inherits them, and thus reads from file and not from our terminal.
|
||||
|
||||
A quick look at ''help read'' tells us that we can specify a file
|
||||
descriptor from which ''read'' should read. Cool. Now let's use ''exec'' to
|
||||
get another descriptor:
|
||||
|
||||
<code>
|
||||
exec 3<file
|
||||
while read -u 3 line;do echo "$line"; read -p "Press any key" -n 1;done
|
||||
</code>
|
||||
|
||||
Now the file descriptors look like:
|
||||
|
||||
<code>
|
||||
--- +-----------------------+
|
||||
standard input ( 0 ) ---->| /dev/pts/5 |
|
||||
--- +-----------------------+
|
||||
|
||||
--- +-----------------------+
|
||||
standard output ( 1 ) ---->| /dev/pts/5 |
|
||||
--- +-----------------------+
|
||||
|
||||
--- +-----------------------+
|
||||
standard error ( 2 ) ---->| /dev/pts/5 |
|
||||
--- +-----------------------+
|
||||
|
||||
--- +-----------------------+
|
||||
new descriptor ( 3 ) ---->| file |
|
||||
--- +-----------------------+
|
||||
</code>
|
||||
|
||||
and it works.
|
||||
|
||||
|
||||
===== Closing The File Descriptors =====
|
||||
|
||||
Closing a file through a file descriptor is easy, just make it a duplicate of -.
|
||||
For instance, let's close ''stdin <&-'' and ''stderr 2>&-'':
|
||||
|
||||
<code>
|
||||
bash -c '{ lsof -a -p $$ -d0,1,2 ;} <&- 2>&-'
|
||||
COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
|
||||
bash 10668 pgas 1u CHR 136,2 4 /dev/pts/2
|
||||
</code>
|
||||
we see that inside the ''{}'' that only ''1'' is still here.
|
||||
|
||||
Though the OS will probably clean up the mess, it is perhaps a
|
||||
good idea to close the file descriptors you open. For instance, if you
|
||||
open a file descriptor with ''exec 3>file'', all the commands afterwards
|
||||
will inherit it. It's probably better to do something like:
|
||||
|
||||
<code>
|
||||
exec 3>file
|
||||
.....
|
||||
#commands that uses 3
|
||||
.....
|
||||
exec 3>&-
|
||||
|
||||
#we don't need 3 any more
|
||||
</code>
|
||||
|
||||
I've seen some people using this as a way to discard, say stderr, using something like: command 2>&-.
|
||||
Though it might work, I'm not sure if you can expect all applications to behave correctly with a closed stderr.
|
||||
|
||||
When in doubt, I use 2>/dev/null.
|
||||
|
||||
|
||||
====== An Example ======
|
||||
|
||||
This example comes from [[http://groups.google.com/group/comp.unix.shell/browse_thread/thread/64206d154894a4ef/ffe4c2e382034ed9#ffe4c2e382034ed9|this post (ffe4c2e382034ed9)]] on the comp.unix.shell group:
|
||||
|
||||
<code>
|
||||
{
|
||||
{
|
||||
cmd1 3>&- |
|
||||
cmd2 2>&3 3>&-
|
||||
} 2>&1 >&4 4>&- |
|
||||
cmd3 3>&- 4>&-
|
||||
|
||||
} 3>&2 4>&1
|
||||
</code>
|
||||
|
||||
The redirections are processed from left to right, but as the file
|
||||
descriptors are inherited we will also have to work from the outer to
|
||||
the inner contexts. We will assume that we run this command in a
|
||||
terminal. Let's start with the outer ''{ } 3>&2 4>&1''.
|
||||
|
||||
<code>
|
||||
--- +-------------+ --- +-------------+
|
||||
( 0 ) ---->| /dev/pts/5 | ( 3 ) ---->| /dev/pts/5 |
|
||||
--- +-------------+ --- +-------------+
|
||||
|
||||
--- +-------------+ --- +-------------+
|
||||
( 1 ) ---->| /dev/pts/5 | ( 4 ) ---->| /dev/pts/5 |
|
||||
--- +-------------+ --- +-------------+
|
||||
|
||||
--- +-------------+
|
||||
( 2 ) ---->| /dev/pts/5 |
|
||||
--- +-------------+
|
||||
</code>
|
||||
|
||||
We only made 2 copies of ''stderr'' and ''stdout''. ''3>&1 4>&1'' would have
|
||||
produced the same result here because we ran the command in a terminal
|
||||
and thus ''1'' and ''2'' go to the terminal. As an exercise, you can start
|
||||
with ''1'' pointing to ''file.stdout'' and 2 pointing to ''file.stderr'', you will
|
||||
see why these redirections are very nice.
|
||||
|
||||
Let's continue with the right part of the second pipe: ''| cmd3 3>&- 4>&-''
|
||||
|
||||
<code>
|
||||
--- +-------------+
|
||||
( 0 ) ---->| 2nd pipe |
|
||||
--- +-------------+
|
||||
|
||||
--- +-------------+
|
||||
( 1 ) ---->| /dev/pts/5 |
|
||||
--- +-------------+
|
||||
|
||||
--- +-------------+
|
||||
( 2 ) ---->| /dev/pts/5 |
|
||||
--- +-------------+
|
||||
</code>
|
||||
|
||||
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>&- |''
|
||||
|
||||
<code>
|
||||
--- +-------------+ --- +-------------+
|
||||
( 0 ) ---->| /dev/pts/5 | ( 3 ) ---->| /dev/pts/5 |
|
||||
--- +-------------+ --- +-------------+
|
||||
|
||||
--- +-------------+
|
||||
( 1 ) ---->| /dev/pts/5 |
|
||||
--- +-------------+
|
||||
|
||||
--- +-------------+
|
||||
( 2 ) ---->| 2nd pipe |
|
||||
--- +-------------+
|
||||
</code>
|
||||
|
||||
First, The file descriptor ''1'' is connected to the pipe (''|''), then ''2'' is
|
||||
made a copy of ''1'' and thus is made an fd to the pipe (''2>&1''), then ''1'' is
|
||||
made a copy of ''4'' (''>&4''), then ''4'' is closed. These are the file
|
||||
descriptors of the inner ''{}''. Lcet's go inside and have a look at the
|
||||
right part of the first pipe: ''| cmd2 2>&3 3>&-''
|
||||
|
||||
<code>
|
||||
--- +-------------+
|
||||
( 0 ) ---->| 1st pipe |
|
||||
--- +-------------+
|
||||
|
||||
--- +-------------+
|
||||
( 1 ) ---->| /dev/pts/5 |
|
||||
--- +-------------+
|
||||
|
||||
--- +-------------+
|
||||
( 2 ) ---->| /dev/pts/5 |
|
||||
--- +-------------+
|
||||
</code>
|
||||
|
||||
It inherits the previous file descriptors, connects 0 to the 1st pipe,
|
||||
the file descriptor 2 is made a copy of 3, and 3 is closed. Finally, for the left
|
||||
part of the pipe:
|
||||
|
||||
<code>
|
||||
--- +-------------+
|
||||
( 0 ) ---->| /dev/pts/5 |
|
||||
--- +-------------+
|
||||
|
||||
--- +-------------+
|
||||
( 1 ) ---->| 1st pipe |
|
||||
--- +-------------+
|
||||
|
||||
--- +-------------+
|
||||
( 2 ) ---->| 2nd pipe |
|
||||
--- +-------------+
|
||||
</code>
|
||||
|
||||
It also inherits the file descriptor of the left part of the 2nd
|
||||
pipe, file descriptor ''1'' is connected to the first pipe, ''3'' is closed.
|
||||
|
||||
The purpose of all this becomes clear if we take only the commands:
|
||||
|
||||
<code>
|
||||
cmd2
|
||||
|
||||
--- +-------------+
|
||||
-->( 0 ) ---->| 1st pipe |
|
||||
/ --- +-------------+
|
||||
/
|
||||
/ --- +-------------+
|
||||
cmd 1 / ( 1 ) ---->| /dev/pts/5 |
|
||||
/ --- +-------------+
|
||||
/
|
||||
--- +-------------+ / --- +-------------+
|
||||
( 0 ) ---->| /dev/pts/5 | / ( 2 ) ---->| /dev/pts/5 |
|
||||
--- +-------------+ / --- +-------------+
|
||||
/
|
||||
--- +-------------+ / cmd3
|
||||
( 1 ) ---->| 1st pipe | /
|
||||
--- +-------------+ --- +-------------+
|
||||
------------>( 0 ) ---->| 2nd pipe |
|
||||
--- +-------------+ / --- +-------------+
|
||||
( 2 ) ---->| 2nd pipe |/
|
||||
--- +-------------+ --- +-------------+
|
||||
( 1 ) ---->| /dev/pts/5 |
|
||||
--- +-------------+
|
||||
|
||||
--- +-------------+
|
||||
( 2 ) ---->| /dev/pts/5 |
|
||||
--- +-------------+
|
||||
|
||||
</code>
|
||||
|
||||
|
||||
As said previously, as an exercise, you can start with ''1'' open on a file
|
||||
and ''2'' open on another file to see how the ''stdin'' from ''cmd2'' and ''cmd3'' goes
|
||||
to the original ''stdin'' and how the ''stderr'' goes to the original ''stderr''.
|
||||
|
||||
|
||||
====== Syntax ======
|
||||
|
||||
I used to have trouble choosing between ''0&<3'' ''3&>1'' ''3>&1'' ''<nowiki>->2</nowiki>'' ''-<&0'' ''&-<0''
|
||||
''0<&-'' etc... (I think probably because the syntax is more representative of the result, i.e., the redirection, than
|
||||
what is done, i.e., opening, closing, or duplicating file descriptors).
|
||||
|
||||
If this fits your situation, then maybe the following "rules" will help
|
||||
you, a redirection is always like the following:
|
||||
|
||||
<code>
|
||||
lhs op rhs
|
||||
</code>
|
||||
|
||||
* ''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.
|
||||
|
||||
|
||||
* ''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.
|
||||
|
||||
|
||||
* ''rhs'' is the thing that the file descriptor will describe:
|
||||
* It can be the name of a file, the place where another descriptor goes (''&1''), or, ''&-'', which will close the file descriptor.
|
||||
|
||||
|
||||
You might not like this description, and find it a bit incomplete or
|
||||
inexact, but I think it really helps to easily find that, say ''<nowiki>&->0</nowiki>'' is
|
||||
incorrect.
|
||||
|
||||
==== A note on style ====
|
||||
|
||||
The shell is pretty loose about what it considers a valid redirect. While opinions probably differ, this author has some (strong) recommendations:
|
||||
|
||||
* **Always** keep redirections "tightly grouped" -- that is, **do not** include whitespace anywhere within the redirection syntax except within quotes if required on the RHS (e.g. a filename that contains a space). Since shells fundamentally use whitespace to delimit fields in general, it is visually much clearer for each redirection to be separated by whitespace, but grouped in chunks that contain no unnecessary whitespace.
|
||||
|
||||
* **Do** always put a space between each redirection, and between the argument list and the first redirect.
|
||||
|
||||
* **Always** place redirections together at the very end of a command after all arguments. Never precede a command with a redirect. Never put a redirect in the middle of the arguments.
|
||||
|
||||
* **Never** use the Csh ''&>foo'' and ''>&foo'' shorthand redirects. Use the long form ''>foo 2>&1''. (see: [[obsolete]])
|
||||
|
||||
<code>
|
||||
# Good! This is clearly a simple commmand with two arguments and 4 redirections
|
||||
cmd arg1 arg2 <myFile 3<&1 2>/dev/null >&2
|
||||
|
||||
# Good!
|
||||
{ cmd1 <<<'my input'; cmd2; } >someFile
|
||||
|
||||
# Bad. Is the "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.
|
||||
cmd 2>& 1 <<< stuff
|
||||
|
||||
# Hideously Bad. It's difficult to tell where the redirects are and whether they're even valid redirects.
|
||||
# This is in fact one command with one argument, an assignment, and three redirects.
|
||||
foo=bar<baz bork<<< blarg>bleh
|
||||
</code>
|
||||
|
||||
====== Conclusion ======
|
||||
|
||||
I hope this tutorial worked for you.
|
||||
|
||||
I lied, I did not explain ''1>&3-'', go check the manual ;-)
|
||||
|
||||
Thanks to Stéphane Chazelas from whom I stole both the intro and the
|
||||
example....
|
||||
|
||||
The intro is inspired by this introduction, you'll find a nice
|
||||
exercise there too:
|
||||
|
||||
* [[http://tldp.org/LDP/abs/html/ioredirintro.html| A Detailed Introduction to I/O and I/O Redirection]]
|
||||
|
||||
The last example comes from this post:
|
||||
|
||||
* [[http://groups.google.com/group/comp.unix.shell/browse_thread/thread/64206d154894a4ef/ffe4c2e382034ed9#ffe4c2e382034ed9 | comp.unix.shell: piping stdout and stderr to different processes]]
|
||||
|
||||
|
||||
====== See also ======
|
||||
|
||||
* Internal: [[syntax:redirection | Redirection syntax overview]]
|
4
original_source/howto/start.txt
Normal file
4
original_source/howto/start.txt
Normal file
@ -0,0 +1,4 @@
|
||||
====== HOWTO ======
|
||||
|
||||
|
||||
{{indexmenu>:howto#10#sort+title}}
|
103
original_source/howto/testing-your-scripts.txt
Normal file
103
original_source/howto/testing-your-scripts.txt
Normal file
@ -0,0 +1,103 @@
|
||||
The one of the simplest way to check your bash/sh scripts is run it and check it output or run it and check the result. This tutorial shows how-to use [[https://github.com/pahaz/bashtest|bashtest]] tool for testing your scripts.
|
||||
|
||||
==== Write simple util ====
|
||||
|
||||
We have a simple **stat.sh** script:
|
||||
|
||||
<code>
|
||||
#!/usr/bin/env bash
|
||||
|
||||
if [ -z "$1" ]
|
||||
then
|
||||
DIR=./
|
||||
else
|
||||
DIR=$1
|
||||
fi
|
||||
|
||||
echo "Evaluate *.py statistics"
|
||||
FILES=$(find $DIR -name '*.py' | wc -l)
|
||||
LINES=$((find $DIR -name '*.py' -print0 | xargs -0 cat) | wc -l)
|
||||
echo "PYTHON FILES: $FILES"
|
||||
echo "PYTHON LINES: $LINES"
|
||||
</code>
|
||||
|
||||
This script evaluate the number of python files and the number of python code lines in the files.
|
||||
We can use it like **./stat.sh <dir>**
|
||||
|
||||
==== Create testsuit ====
|
||||
|
||||
Then make test suits for **stat.sh**. We make a directory **testsuit** which contain test python files.
|
||||
|
||||
**testsuit/main.py**
|
||||
<code>
|
||||
import foo
|
||||
print(foo)
|
||||
</code>
|
||||
|
||||
**testsuit/foo.py**
|
||||
<code>
|
||||
BAR = 1
|
||||
BUZ = BAR + 2
|
||||
</code>
|
||||
|
||||
Ok! Our test suit is ready!
|
||||
We have 2 python files which contains 4 lines of code.
|
||||
|
||||
==== Write bashtests ====
|
||||
|
||||
Lets write tests. We just write a shell command for testing our work.
|
||||
|
||||
Create file **tests.bashtest**:
|
||||
|
||||
<code>
|
||||
$ ./stat.sh testsuit/
|
||||
Evaluate *.py statistics
|
||||
PYTHON FILES: 2
|
||||
PYTHON LINES: 4
|
||||
|
||||
</code>
|
||||
|
||||
This is our test! This is simple. Try to run it.
|
||||
|
||||
<code>
|
||||
# install bashtest if required!
|
||||
$ pip install bashtest
|
||||
</code>
|
||||
|
||||
<code>
|
||||
# run tests
|
||||
$ bashtest *.bashtest
|
||||
1 items passed all tests:
|
||||
1 tests in tests.bashtest
|
||||
1 tests in 1 items.
|
||||
1 passed and 0 failed.
|
||||
Test passed.
|
||||
</code>
|
||||
|
||||
Thats all. We wrote one test. You can write more tests if you want.
|
||||
|
||||
<code>
|
||||
$ ls testsuit/
|
||||
foo.py main.py
|
||||
|
||||
$ ./stat.sh testsuit/
|
||||
Evaluate *.py statistics
|
||||
PYTHON FILES: 2
|
||||
PYTHON LINES: 4
|
||||
|
||||
</code>
|
||||
|
||||
And run tests again:
|
||||
|
||||
<code>
|
||||
$ bashtest *.bashtest
|
||||
1 items passed all tests:
|
||||
2 tests in tests.bashtest
|
||||
2 tests in 1 items.
|
||||
2 passed and 0 failed.
|
||||
Test passed.
|
||||
</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]].
|
||||
|
||||
Happy testing!
|
341
original_source/internals/shell_options.txt
Normal file
341
original_source/internals/shell_options.txt
Normal file
@ -0,0 +1,341 @@
|
||||
====== List of shell options ======
|
||||
|
||||
{{keywords>bash shell scripting options runtime variable behaviour}}
|
||||
|
||||
This information was taken from a Bash version "''4.1''", every now and then new options are added, so likely, this list isn't complete.
|
||||
|
||||
The shell-options can be set with the [[commands:builtin:shopt | shopt builtin command]].
|
||||
|
||||
===== Shell options =====
|
||||
|
||||
==== autocd ====
|
||||
^Option: |''autocd'' ^Since: |4.0-alpha |
|
||||
^Shell mode: |interactive only ^Default: |off |
|
||||
|
||||
If set, a command name that is the name of a directory is executed as if it were the argument to the cd command.
|
||||
|
||||
==== assoc_expand_once ====
|
||||
^Option: |''assoc_expand_once'' ^Since: |5.0-alpha |
|
||||
^Shell mode: |all ^Default: |off |
|
||||
|
||||
If set, Bash attempts to expand associative array options only once.
|
||||
|
||||
|
||||
==== cdable_vars ====
|
||||
^Option: |''cdable_vars'' ^Since: |unknown |
|
||||
^Shell mode: |all ^Default: |off |
|
||||
|
||||
Treat every **non-directory argument** to the ''cd''-command as variable name containing a directory to ''cd'' into.
|
||||
|
||||
==== cdspell ====
|
||||
^Option: |''cdspell'' ^Since: |unknown |
|
||||
^Shell mode: |interactive only ^Default: |off |
|
||||
|
||||
If set, minor errors in the spelling of a directory component in a cd command will be corrected. The errors checked for are transposed characters, a missing character, and one character too many. If a correction is found, the corrected file name is printed, and the command proceeds.
|
||||
|
||||
==== checkhash ====
|
||||
^Option: |''checkhash'' ^Since: |unknown |
|
||||
^Shell mode: |all ^Default: |off |
|
||||
|
||||
If set, Bash checks that a command found in the hash table exists before trying to execute it. If a hashed command no longer exists, a normal path search is performed.
|
||||
|
||||
==== checkjobs ====
|
||||
^Option: |''checkjobs'' ^Since: |4.0-alpha |
|
||||
^Shell mode: |interactive only ^Default: |off |
|
||||
|
||||
If set, Bash lists the status of any stopped and running jobs before exiting an interactive shell. If any jobs are running, this causes the exit to be deferred until a second exit is attempted without an intervening command. The shell always postpones exiting if any jobs are stopped.
|
||||
|
||||
==== checkwinsize ====
|
||||
^ Option: | ''checkwinsize'' ^ Since: | unknown |
|
||||
^ Shell mode: | all ^ Default: | on |
|
||||
|
||||
If set, Bash checks the window size after each command and, if necessary, updates the values of the variables [[syntax:shellvars#LINES|LINES]] and [[syntax:shellvars#COLUMNS|COLUMNS]].
|
||||
|
||||
==== cmdhist ====
|
||||
^Option: |''cmdhist'' ^Since: |unknown |
|
||||
^Shell mode: |all ^Default: |off |
|
||||
|
||||
If set, Bash attempts to save all lines of a multiple-line command in the same history entry. This allows easy re-editing of multi-line commands.
|
||||
|
||||
==== compat31 ====
|
||||
^Option: |''compat31'' ^Since: |3.2 |
|
||||
^Shell mode: |all ^Default: |off |
|
||||
|
||||
Compatiblity mode for Bash 3.1
|
||||
|
||||
==== compat32 ====
|
||||
^Option: |''compat32'' ^Since: |4.0 |
|
||||
^Shell mode: |all ^Default: |off |
|
||||
|
||||
Compatiblity mode for Bash 3.2
|
||||
|
||||
==== compat40 ====
|
||||
^Option: |''compat40'' ^Since: |4.1-beta |
|
||||
^Shell mode: |all ^Default: |off |
|
||||
|
||||
Compatiblity mode for Bash 4.0
|
||||
|
||||
==== compat41 ====
|
||||
^Option: |''compat41'' ^Since: |4.2-alpha |
|
||||
^Shell mode: |all ^Default: |off |
|
||||
|
||||
Compatiblity mode for Bash 4.1
|
||||
|
||||
==== compat42 ====
|
||||
^Option: |''compat42'' ^Since: |4.3-alpha |
|
||||
^Shell mode: |all ^Default: |off |
|
||||
|
||||
Compatiblity mode for Bash 4.2
|
||||
|
||||
==== compat43 ====
|
||||
^Option: |''compat43'' ^Since: |4.4-alpha |
|
||||
^Shell mode: |all ^Default: |off |
|
||||
|
||||
Compatiblity mode for Bash 4.3
|
||||
|
||||
==== compat44 ====
|
||||
^Option: |''compat44'' ^Since: |5.0-alpha |
|
||||
^Shell mode: |all ^Default: |off |
|
||||
|
||||
Compatiblity mode for Bash 4.4
|
||||
|
||||
==== direxpand ====
|
||||
^Option: |''direxpand'' ^Since: |4.3-alpha |
|
||||
^Shell mode: |all ^Default: |off (unless changed on compile-time with ''--enable-direxpand-default'') |
|
||||
|
||||
If set, bash replaces directory names with the results of word expansion when performing filename completion. This changes the contents of the readline editing buffer. If not set, bash attempts to preserve what the user typed.
|
||||
|
||||
==== dirspell ====
|
||||
^Option: |''dirspell'' ^Since: |4.0-alpha |
|
||||
^Shell mode: |all ^Default: |off |
|
||||
|
||||
If set, Bash will perform spelling corrections on directory names to match a glob.
|
||||
|
||||
==== dotglob ====
|
||||
^Option: |''dotglob'' ^Since: |unknown |
|
||||
^Shell mode: |all ^Default: |off |
|
||||
|
||||
If set, Bash includes filenames beginning with a ''.'' (dot) in the results of [[syntax:expansion:globs | pathname expansion]].
|
||||
|
||||
==== execfail ====
|
||||
^Option: |''execfail'' ^Since: |unknown |
|
||||
^Shell mode: |non-interactive ^Default: |off |
|
||||
|
||||
If set, a non-interactive shell will not exit if it cannot execute the file specified as an argument to the ''exec''-builtin command. An interactive shell does not exit if ''exec'' fails.
|
||||
|
||||
==== expand_aliases ====
|
||||
^Option: |''expand_aliases'' ^Since: |unknown |
|
||||
^Shell mode: |all ^Default: |on (interactive), off (non-interactive) |
|
||||
|
||||
If set, aliases are expanded. This option is enabled by default for interactive shells.
|
||||
|
||||
==== extdebug ====
|
||||
^Option: |''extdebug'' ^Since: |3.0-alpha |
|
||||
^Shell mode: |all ^Default: |off |
|
||||
|
||||
If set, behavior intended for use by debuggers is enabled.
|
||||
|
||||
==== extglob ====
|
||||
^Option: |''extglob'' ^Since: |2.02-alpha1 |
|
||||
^Shell mode: |all ^Default: |off |
|
||||
|
||||
If set, the extended [[syntax:pattern | pattern matching]] features are enabled. See the important note below under [[#parser_configurations | Parser configurations]].
|
||||
|
||||
==== extquote ====
|
||||
^Option: |''extquote'' ^Since: |3.0-alpha (?) |
|
||||
^Shell mode: |all ^Default: |on |
|
||||
|
||||
If set, ''<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 ====
|
||||
^Option: |''failglob'' ^Since: |3.0-alpha |
|
||||
^Shell mode: |all ^Default: |off |
|
||||
|
||||
If set, patterns which fail to match filenames during pathname expansion result in an error message.
|
||||
|
||||
==== force_fignore ====
|
||||
^Option: |''force_fignore'' ^Since: |3.0-alpha |
|
||||
^Shell mode: |interactive ^Default: |on |
|
||||
|
||||
If set, the suffixes specified by the [[syntax:shellvars#FIGNORE|FIGNORE]] shell variable cause words to be ignored when performing word completion even if the ignored words are the only possible completions. This option is enabled by default.
|
||||
|
||||
==== globasciiranges ====
|
||||
^Option: |''globasciiranges'' ^Since: |4.3-alpha |
|
||||
^Shell mode: | all ^Default: |on (configurable at compile time) |
|
||||
|
||||
If set, range expressions used in pattern matching behave as if in the traditional C locale when performing comparisons.
|
||||
That is, the current locale's collating sequence is not taken into account, so b will not collate between A and B, and upper-case and lower-case ASCII
|
||||
characters will collate together.
|
||||
|
||||
==== globstar ====
|
||||
^Option: |''globstar'' ^Since: |4.0-alpha |
|
||||
^Shell mode: |all ^Default: |off |
|
||||
|
||||
If set, recursive globbing with ''<nowiki>**</nowiki>'' is enabled.
|
||||
|
||||
==== gnu_errfmt ====
|
||||
^Option: |''gnu_errfmt'' ^Since: |3.0-alpha |
|
||||
^Shell mode: |all ^Default: |off |
|
||||
|
||||
If set, shell error messages are written in the "standard GNU error message format".
|
||||
|
||||
==== histappend ====
|
||||
^Option: |''histappend'' ^Since: |unknown |
|
||||
^Shell mode: |interactive (?) ^Default: |off |
|
||||
|
||||
If set, the history list is appended to the file named by the value of the [[syntax:shellvars#HISTFILE|HISTFILE]] variable when the shell exits, rather than overwriting the file.
|
||||
|
||||
==== histreedit ====
|
||||
^Option: |''histreedit'' ^Since: |unknown |
|
||||
^Shell mode: |interactive (?) ^Default: |off |
|
||||
|
||||
If set, and readline is being used, a user is given the opportunity to re-edit a failed history substitution.
|
||||
|
||||
==== histverify ====
|
||||
^Option: |''histverify'' ^Since: |unknown |
|
||||
^Shell mode: |interactive (?) ^Default: |off |
|
||||
|
||||
Allow to review a history substitution result by loading the resulting line into the editing buffer, rather than directly executing it.
|
||||
|
||||
==== hostcomplete ====
|
||||
^Option: |''hostcomplete'' ^Since: |2.0-alpha3 |
|
||||
^Shell mode: |interactive (?) ^Default: |on |
|
||||
|
||||
If set, Bash completion also completes hostnames. On by default.
|
||||
|
||||
==== huponexit ====
|
||||
^Option: |''huponexit'' ^Since: |2.02-alpha1 |
|
||||
^Shell mode: |interactive login ^Default: |off |
|
||||
|
||||
If set, Bash will send the ''SIGHUP'' signal to all jobs when an interactive login shell exits.
|
||||
|
||||
==== interactive_comments ====
|
||||
^Option: |''interactive_comments'' ^Since: |unknown |
|
||||
^Shell mode: |interactive ^Default: |on |
|
||||
|
||||
Allow [[scripting:basics#comments | commenting]] in interactive shells, on by default.
|
||||
|
||||
==== lastpipe ====
|
||||
^Option: |''lastpipe'' ^Since: |4.2-alpha |
|
||||
^Shell mode: |all ^Default: |off |
|
||||
|
||||
If set, **and job control is not active**, the shell runs the last command of a pipeline not executed in the background in the current shell environment.
|
||||
|
||||
==== lithist ====
|
||||
^Option: |''lithist'' ^Since: |unknown |
|
||||
^Shell mode: |interactive ^Default: |off |
|
||||
|
||||
If set, and the [[#cmdhist]] option is enabled, multi-line commands are saved to the history with embedded newlines rather than using semicolon separators where possible.
|
||||
|
||||
==== localvar_inherit ====
|
||||
^Option: |''localvar_inherit'' ^Since: |5.0-alpha |
|
||||
^Shell mode: |all ^Default: |off |
|
||||
|
||||
If this option is set, a local variable inherits the value of a variable with the same name at the nearest preceding scope.
|
||||
|
||||
==== login_shell ====
|
||||
^Option: |''login_shell'' ^Since: |2.05a-alpha1 |
|
||||
^Shell mode: |all ^Default: |n/a |
|
||||
|
||||
The option is set when Bash is a login shell. This is a readonly option.
|
||||
|
||||
==== mailwarn ====
|
||||
^Option: |''mailwarn'' ^Since: |unknown |
|
||||
^Shell mode: |interactive (?) ^Default: |off |
|
||||
|
||||
If set, and a file that Bash is checking for mail has been accessed since the last time it was checked, the message "The mail in mailfile has been read" is displayed.
|
||||
|
||||
==== no_empty_cmd_completion ====
|
||||
^Option: |''mailwarn'' ^Since: |unknown |
|
||||
^Shell mode: |interactive (?) ^Default: |off |
|
||||
|
||||
If set, and readline is being used, Bash will not attempt to search the PATH for possible completions when completion is attempted on an empty line.
|
||||
|
||||
==== nocaseglob ====
|
||||
^Option: |''nocaseglob'' ^Since: |2.02-alpha1 |
|
||||
^Shell mode: |all ^Default: |off |
|
||||
|
||||
If set, Bash matches filenames in a case-insensitive fashion when performing pathname expansion.
|
||||
|
||||
==== nocasematch ====
|
||||
^Option: |''nocasematch'' ^Since: |3.1-alpha1 |
|
||||
^Shell mode: |all ^Default: |off |
|
||||
|
||||
If set, Bash matches patterns in a case-insensitive fashion when performing matching while executing ''case'' or ''<nowiki>[[</nowiki>'' conditional commands.
|
||||
|
||||
==== nullglob ====
|
||||
^Option: |''nullglob'' ^Since: |unknown |
|
||||
^Shell mode: |all ^Default: |off |
|
||||
|
||||
If set, Bash allows patterns which match no files to expand to a null string, rather than themselves.
|
||||
|
||||
==== progcomp ====
|
||||
^Option: |''progcomp'' ^Since: |2.04-alpha1 |
|
||||
^Shell mode: |interactive (?) ^Default: |on |
|
||||
|
||||
If set, the programmable completion facilities are enabled. This option is enabled by default.
|
||||
|
||||
==== promptvars ====
|
||||
^Option: |''promptvars'' ^Since: |unknown |
|
||||
^Shell mode: |interactive ^Default: |on |
|
||||
|
||||
If set, prompt strings undergo parameter expansion, command substitution, arithmetic expansion, and quote removal after being expanded using the prompt special sequences. This option is enabled by default.
|
||||
|
||||
==== restricted_shell ====
|
||||
^Option: |''restricted_shell'' ^Since: |2.03-alpha |
|
||||
^Shell mode: |interactive (?) ^Default: |off |
|
||||
|
||||
The option is set when Bash is a restricted shell. This is a readonly option.
|
||||
|
||||
==== shift_verbose ====
|
||||
^ Option: | ''shift_verbose'' ^ Since: | unknown |
|
||||
^ Shell mode: |all ^ Default: | off, on in POSIX mode |
|
||||
|
||||
If set, the shift builtin prints an error message when the shift count exceeds the number of positional parameters.
|
||||
|
||||
==== sourcepath ====
|
||||
^Option: |''sourcepath'' ^Since: |unknown |
|
||||
^Shell mode: |all ^Default: |on |
|
||||
|
||||
If set, the source builtin command uses the value of PATH to find the directory containing the file supplied as an argument. This option is enabled by default.
|
||||
|
||||
==== syslog_history ====
|
||||
^Option: |''syslog_history'' ^Since: |5.0-alpha |
|
||||
^Shell mode: |unknown ^Default: |off |
|
||||
|
||||
If set, the shell history is sent to syslog.
|
||||
|
||||
This option is undocumented and available only if the shell supports syslog.
|
||||
|
||||
|
||||
==== xpg_echo ====
|
||||
^Option: |''xpg_echo'' ^Since: |2.04-beta1 |
|
||||
^Shell mode: |all ^Default: |off |
|
||||
|
||||
If set, the ''echo''-builtin command expands backslash-escape sequences by default (POSIX, SUS, XPG).
|
||||
|
||||
===== Parser configurations =====
|
||||
|
||||
Parser configurations change the way the Bash parser recognizes the syntax when parsing a line. This, of course, is impossible for a line that already was parsed.
|
||||
|
||||
There are two options that influence the parsing this way:
|
||||
* ''extglob''
|
||||
* ''extquote''
|
||||
|
||||
Consequence: You **can't** use the new syntax (e.g. the extended globbing syntax) and the command to enable it **in the same line**.
|
||||
<code>
|
||||
$ shopt -s extglob; echo !(*.txt) # this is the WRONG way!
|
||||
-bash: syntax error near unexpected token `('
|
||||
</code>
|
||||
|
||||
You have to configure the parser **before** a line with new syntax is parsed:
|
||||
<code>
|
||||
$ shopt -s extglob # standalone - CORRECT way!
|
||||
$ echo !(*.txt)
|
||||
...
|
||||
</code>
|
||||
|
||||
===== See also =====
|
||||
|
||||
* Internal: [[commands:builtin:shopt | shopt builtin command]]
|
||||
* Internal: [[commands:builtin:set | set builtin command]]
|
327
original_source/internals/start.txt
Normal file
327
original_source/internals/start.txt
Normal file
File diff suppressed because one or more lines are too long
327
original_source/media/howto/smallcaps.txt
Normal file
327
original_source/media/howto/smallcaps.txt
Normal file
File diff suppressed because one or more lines are too long
327
original_source/media/howto/start.txt
Normal file
327
original_source/media/howto/start.txt
Normal file
File diff suppressed because one or more lines are too long
327
original_source/media/start.txt
Normal file
327
original_source/media/start.txt
Normal file
File diff suppressed because one or more lines are too long
46
original_source/meta/need_love.txt
Normal file
46
original_source/meta/need_love.txt
Normal file
@ -0,0 +1,46 @@
|
||||
====== Bash Hackers Wiki needs love ======
|
||||
|
||||
There are things to do. Always.
|
||||
|
||||
===== Article optics and structure =====
|
||||
|
||||
Target: A more or less unique structure (done)
|
||||
|
||||
* [[syntax:ccmd:intro]] - bring all mentioned compound command description pages into a format
|
||||
* SYNOPSIS
|
||||
* DESCRIPTION
|
||||
* (with as many sub-topics as needed)
|
||||
* EXAMPLES
|
||||
* PORTABILITY CONSIDERATIONS
|
||||
* SEE ALSO
|
||||
* do the same with command descriptions (printf, read, ...)
|
||||
|
||||
|
||||
===== Article content and correctness =====
|
||||
|
||||
In general all pages need review, all the time. If you find anything incorrect, stupid, unclear, just edit the page or write a mail. It's also possible to use the integrated discussion option (below every article).
|
||||
|
||||
Specific needs:
|
||||
* [[scripting:tutoriallist]] - new tutorials? opinions? etc.
|
||||
|
||||
===== Article code examples =====
|
||||
* [[howto:getopts_tutorial]] - lacks some complex or more sophisticated examples
|
||||
|
||||
===== Article completeness =====
|
||||
* [[scripting:newbie_traps]] - add your personal top newbie traps
|
||||
* [[internals:shell_options]] - are these all options?
|
||||
* [[commands:builtin:printf]] - can printf do more?
|
||||
* [[syntax:shellvars]] - needs complete shell variables
|
||||
|
||||
===== Greg's BashFAQ mirror =====
|
||||
|
||||
The code rewrite to mirror Greg's BashFAQ was a bad idea, it doesn't work good. A fix to not lose the links would be to invent some nifty mod_rewrite rules to forward any requests to the namespace to the real BashFAQ wiki.
|
||||
|
||||
**Update:** Just hacked the rewrite rules - the mirror is gone and the mirror URLs redirect to Greg's BashFAQ.
|
||||
|
||||
**Future:** Mirror it as real MoinMoinWiki instance. This won't happen here on the webspace, more on a dedicated server.
|
||||
|
||||
===== Getopts tutorial =====
|
||||
|
||||
* more examples
|
||||
* workaround to use long options: translate long option to short option and replace positional parameters before using getopts
|
327
original_source/meta/start.txt
Normal file
327
original_source/meta/start.txt
Normal file
File diff suppressed because one or more lines are too long
47
original_source/misc/bashphorisms.txt
Normal file
47
original_source/misc/bashphorisms.txt
Normal file
@ -0,0 +1,47 @@
|
||||
====== The Bashphorisms ======
|
||||
|
||||
Bashphorisms are aphorisms for the IRC channel ''#bash'' on Freenode. Keep in mind that this version is a snapshot, the bashphorisms are changed here and there. Also, [[http://mywiki.wooledge.org/BashFAQ/064 | another snapshot]].
|
||||
|
||||
I think ''greycat'' was the first one who had the idea, but I'm not sure.
|
||||
|
||||
Our bashphorisms can be queried from ''greybot'' using ''!bN'', where ''N'' is the bashphorism number.
|
||||
|
||||
And yes, these bashphorisms reflect the daily reality in ''#bash''.
|
||||
|
||||
^ Number ^ Bashphorism ^
|
||||
| 0 | The questioner will never tell you what they are really doing the first time they ask. |
|
||||
| 1 | The questioner's first description of the problem/question will be misleading. |
|
||||
| 2 | The questioner will keep changing the question until it drives the helpers in the channel insane. |
|
||||
| 3 | Offtopicness will continue until someone asks a bash question that falls under bashphorisms 1 and/or 2, and ''greycat'' gets pissed off. |
|
||||
| 4 | The questioner will not read and apply the answers he is given but will instead continue to practice bashphorism #1 and bashphorism #2. |
|
||||
| 5 | The ignorant will continually mis-educate the other noobies. |
|
||||
| 6 | When given a choice of solutions, the newbie will always choose the wrong one. |
|
||||
| 7 | The newbie will always find a reason to say, "It doesn't work." |
|
||||
| 8 | If you don't know to whom the bashphorism's referring, it's you. |
|
||||
| 9 | All examples given by the questioner will be broken, misleading, wrong, and not representative of the actual question. |
|
||||
| 10 | See B1 |
|
||||
| 11 | Please apply ''%%(( % 10 ))%%'' to the bashphorism value. |
|
||||
| 12 | All logic is deniable; however, some logic will *plonk* you if you deny it. |
|
||||
| 13 | Everyone ignores greycat when he is right. When he is wrong, it is !b1 |
|
||||
| 14 | The newbie doesn't actually know what he's asking. If he did, he wouldn't need to ask. |
|
||||
| 15 | The more advanced you are, the more likely you are to be overcomplicating it. |
|
||||
| 16 | The more beginner you are, the more likely you are to be overcomplicating it. |
|
||||
| 17 | A newbie comes to #bash to get his script confirmed. He leaves disappointed. |
|
||||
| 18 | The newbie will not accept the answer you give, no matter how right it is. |
|
||||
| 19 | The newbie is a bloody loon. |
|
||||
| 20 | The newbie will always have some excuse for doing it wrong. |
|
||||
| 21 | When the newbie's question is ambiguous, the proper interpretation will be whichever one makes the problem the hardest to solve. |
|
||||
| 22 | The newcomer will abuse the bot's factoid triggers for their own entertainment until someone gets annoyed enough to ask them to message it privately instead. |
|
||||
| 23 | Everyone is a newcomer. |
|
||||
| 24 | The newcomer will address greybot as if it were human. |
|
||||
| 25 | The newbie won't accept any answer that uses practical or standard tools. |
|
||||
| 26 | The newbie will not TELL you about this restriction until you have wasted half an hour. |
|
||||
| 27 | The newbie will lie. |
|
||||
| 28 | When the full horror of the newbie's true goal is revealed, the newbie will try to restate the goal to trick you into answering. Newbies are stupid. |
|
||||
| 29 | It's always git. Or python virtualenv. Or docker. One of those pieces of shit. ALWAYS. |
|
||||
| 30 | They won't show you the homework assignment. That would make it too easy. |
|
||||
| 31 | Your teacher is a f**king idiot. |
|
||||
| 32 | The more horrifyingly wrong a proposed solution is, the more likely it will be used. |
|
||||
| 33 | The newbie cannot explain what he is doing, or why. He will show you incomprehensible, nonworking code instead. What? You can't read his mind?! |
|
||||
|
||||
Please feel free to correct or extend this page whenever needed.
|
65
original_source/misc/readthesourceluke.txt
Normal file
65
original_source/misc/readthesourceluke.txt
Normal file
@ -0,0 +1,65 @@
|
||||
Comments extracted from the bash source and therefore Copyright (C) 1987-2004 Free Software Foundation, Inc. under
|
||||
the terms of the GNU General Public License etc..
|
||||
|
||||
from ''mailcheck.c'':
|
||||
<code C>
|
||||
/* check_mail () is useful for more than just checking mail. Since it has
|
||||
the paranoids dream ability of telling you when someone has read your
|
||||
mail, it can just as easily be used to tell you when someones .profile
|
||||
file has been read, thus letting one know when someone else has logged
|
||||
in. Pretty good, huh? */
|
||||
</code>
|
||||
|
||||
From ''builtins/read.def'':
|
||||
|
||||
<code C>
|
||||
/* If there are no variables, save the text of the line read to the
|
||||
variable $REPLY. ksh93 strips leading and trailing IFS whitespace,
|
||||
so that `read x ; echo "$x"' and `read ; echo "$REPLY"' behave the
|
||||
same way, but I believe that the difference in behaviors is useful
|
||||
enough to not do it. Without the bash behavior, there is no way
|
||||
to read a line completely without interpretation or modification
|
||||
unless you mess with $IFS (e.g., setting it to the empty string).
|
||||
If you disagree, change the occurrences of `#if 0' to `#if 1' below. */
|
||||
</code>
|
||||
|
||||
from ''variables.c'':
|
||||
<code C>
|
||||
/*
|
||||
* 24 October 2001
|
||||
*
|
||||
* I'm tired of the arguing and bug reports. Bash now leaves SSH_CLIENT
|
||||
* and SSH2_CLIENT alone. I'm going to rely on the shell_level check in
|
||||
* isnetconn() to avoid running the startup files more often than wanted.
|
||||
* That will, of course, only work if the user's login shell is bash, so
|
||||
* I've made that behavior conditional on SSH_SOURCE_BASHRC being defined
|
||||
* in config-top.h.
|
||||
*/
|
||||
</code>
|
||||
|
||||
From ''shell.h'':
|
||||
<code C>
|
||||
/* Values that can be returned by execute_command (). */
|
||||
#define EXECUTION_FAILURE 1
|
||||
#define EXECUTION_SUCCESS 0
|
||||
|
||||
/* Usage messages by builtins result in a return status of 2. */
|
||||
#define EX_BADUSAGE 2
|
||||
|
||||
/* Special exit statuses used by the shell, internally and externally. */
|
||||
#define EX_RETRYFAIL 124
|
||||
#define EX_WEXPCOMSUB 125
|
||||
#define EX_BINARY_FILE 126
|
||||
#define EX_NOEXEC 126
|
||||
#define EX_NOINPUT 126
|
||||
#define EX_NOTFOUND 127
|
||||
|
||||
#define EX_SHERRBASE 256 /* all special error values are > this. */
|
||||
|
||||
#define EX_BADSYNTAX 257 /* shell syntax error */
|
||||
#define EX_USAGE 258 /* syntax error in usage */
|
||||
#define EX_REDIRFAIL 259 /* redirection failed */
|
||||
#define EX_BADASSIGN 260 /* variable assignment error */
|
||||
#define EX_EXPFAIL 261 /* word expansion failed */
|
||||
</code>
|
||||
|
52
original_source/misc/shell_humor.txt
Normal file
52
original_source/misc/shell_humor.txt
Normal file
@ -0,0 +1,52 @@
|
||||
====== Shell Humor ======
|
||||
|
||||
Nothing special, just my private collection of some more or less funny shell stuff I saw during the years.
|
||||
|
||||
Usually Bash and/or Linux (GNU Toolset) specific.
|
||||
|
||||
<code>
|
||||
$ %blow
|
||||
-bash: fg: %blow: no such job
|
||||
</code>
|
||||
|
||||
<code>
|
||||
$ ar m god
|
||||
ar: creating god
|
||||
</code>
|
||||
|
||||
<code>
|
||||
$ touch /pussy
|
||||
touch: cannot touch `/pussy': Permission denied
|
||||
</code>
|
||||
|
||||
<code>
|
||||
$ mount; fsck; fsck; fsck; umount; sleep
|
||||
</code>
|
||||
|
||||
<code>
|
||||
# the lover variant
|
||||
$ unzip; strip; touch; finger; grep; mount; fsck; more; yes; fsck; fsck; umount; sleep
|
||||
</code>
|
||||
|
||||
<code>
|
||||
# it's not directly funny, only because it's not obvious that this is an sed command
|
||||
# for the <<<, it works only in Bash
|
||||
$ sed streetlight <<< reeter
|
||||
lighter
|
||||
</code>
|
||||
|
||||
<code>
|
||||
# see above for comments
|
||||
$ sed statement <<< cat
|
||||
cement
|
||||
</code>
|
||||
|
||||
<code>
|
||||
$ \(-
|
||||
bash: (-: command not found
|
||||
</code>
|
||||
|
||||
<code>
|
||||
$ echo '[q]sa[ln0=aln256%Pln256/snlbx]sb3135071790101768542287578439snlbxq'|dc
|
||||
GET A LIFE!
|
||||
</code>
|
327
original_source/misc/start.txt
Normal file
327
original_source/misc/start.txt
Normal file
File diff suppressed because one or more lines are too long
325
original_source/scripting/bashbehaviour.txt
Normal file
325
original_source/scripting/bashbehaviour.txt
Normal file
@ -0,0 +1,325 @@
|
||||
====== Bash's behaviour ======
|
||||
|
||||
{{keywords>bash shell scripting startup files dotfiles modes POSIX}}
|
||||
|
||||
FIXME incomplete
|
||||
|
||||
|
||||
===== Bash startup modes =====
|
||||
|
||||
|
||||
==== Login shell ====
|
||||
|
||||
|
||||
As a "login shell", Bash reads and sets (executes) the user's profile from ''/etc/profile'' and one of ''~/.bash_profile'', ''~/.bash_login'', or ''~/.profile'' (in that order, using the first one that's readable!).
|
||||
|
||||
When a login shell exits, Bash reads and executes commands from the file ''~/.bash_logout'', if it exists.
|
||||
|
||||
Why an extra login shell mode? There are many actions and variable sets that only make sense for the initial user login. That's why all UNIX(r) shells have (should have) a "login" mode.
|
||||
|
||||
__**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
|
||||
* Bash is started with the ''-l'' option
|
||||
* Bash is started with the ''<nowiki>--login</nowiki>'' option
|
||||
|
||||
__**Methods to test for login shell mode:**__
|
||||
* the shell option ''[[internals:shell_options#login_shell |login_shell]]'' is set
|
||||
|
||||
__**Related switches:**__
|
||||
* ''<nowiki>--noprofile</nowiki>'' disables reading of all profile files
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
==== Interactive shell ====
|
||||
|
||||
When Bash starts as an interactive non-login shell, it reads and executes commands from ''~/.bashrc''. This file should contain, for example, aliases, since they need to be defined in every shell as they're not inherited from the parent shell.
|
||||
|
||||
The feature to have a system-wide ''/etc/bash.bashrc'' or a similar system-wide rc-file is specific to vendors and distributors that ship //their own, patched variant of Bash//. The classic way to have a system-wide rc file is to ''source /etc/bashrc'' from every user's ''~/.bashrc''.
|
||||
|
||||
__**Methods to test for interactive-shell mode:**__
|
||||
* the special parameter ''$-'' contains the letter ''i'' (lowercase I)
|
||||
|
||||
__**Related switches:**__
|
||||
* ''-i'' forces the interactive mode
|
||||
* ''<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'')
|
||||
|
||||
|
||||
|
||||
|
||||
==== SH mode ====
|
||||
|
||||
When Bash starts in SH compatiblity mode, it tries to mimic the startup behaviour of historical versions of ''sh'' as closely as possible, while conforming to the POSIX(r) standard as well. The profile files read are ''/etc/profile'' and ''~/.profile'', if it's a login shell.
|
||||
|
||||
If it's not a login shell, the environment variable [[syntax:shellvars#ENV|ENV]] is evaluated and the resulting filename is used as the name of the startup file.
|
||||
|
||||
After the startup files are read, Bash enters the [[#posix_run_mode | POSIX(r) compatiblity mode (for running, not for starting!)]].
|
||||
|
||||
__**Bash starts in ''sh'' compatiblity mode when:**__
|
||||
* the base filename in ''argv[0]'' is ''sh'' (:!: NB: ''/bin/sh'' may be linked to ''/bin/bash'', but that doesn't mean it acts like ''/bin/bash'' :!:)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
==== POSIX mode ====
|
||||
|
||||
When Bash is started in POSIX(r) mode, it follows the POSIX(r) standard for startup files. In this mode, **interactive shells** expand the [[syntax:shellvars#ENV|ENV]] variable and commands are read and executed from the file whose name is the expanded value.\\
|
||||
No other startup files are read. Hence, a non-interactive shell doesn't read any startup files in POSIX(r) mode.
|
||||
|
||||
__**Bash starts in POSIX(r) mode when:**__
|
||||
* the commandline option ''<nowiki>--posix</nowiki>'' is specified
|
||||
* the environment variable [[syntax:shellvars#POSIXLY_CORRECT|POSIXLY_CORRECT]] is set
|
||||
|
||||
|
||||
|
||||
|
||||
==== Quick startup file reference ====
|
||||
|
||||
* Eventual system-wide rc-files are usually read when ''~/.bashrc'' would be read (at least Debian GNU/Linux behaves like that)
|
||||
* Regardless of the system-wide files in ''/etc'' which are always read, Bash usually reads the first file found, when multiple choices are given (for user files in ''~/'')
|
||||
|
||||
^Mode^''/etc/profile''^''~/.bash_profile''^''~/.bash_login''^''~/.profile''^''~/.bashrc''^''${ENV}''^
|
||||
|Login shell | X | X | X | X | - | - |
|
||||
|Interactive shell | - | - | - | - | X | - |
|
||||
|SH compatible login | X | - | - | X | - | - |
|
||||
|SH compatible | - | - | - | - | - | X |
|
||||
|POSIX(r) compatiblity | - | - | - | - | - | X |
|
||||
|
||||
|
||||
===== Bash run modes =====
|
||||
|
||||
|
||||
==== Normal Bash ====
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
==== POSIX run mode ====
|
||||
|
||||
In POSIX(r) mode, Bash follows the POSIX(r) standard regarding behaviour and parsing (excerpt from a Bash maintainer's document):
|
||||
<code>
|
||||
Starting Bash with the `--posix' command-line option or executing `set
|
||||
-o posix' while Bash is running will cause Bash to conform more closely
|
||||
to the POSIX standard by changing the behavior to match that specified
|
||||
by POSIX in areas where the Bash default differs.
|
||||
|
||||
When invoked as `sh', Bash enters POSIX mode after reading the startup
|
||||
files.
|
||||
|
||||
The following lists what's changed when Bash is in `POSIX mode':
|
||||
|
||||
1. When a command in the hash table no longer exists, Bash will
|
||||
re-search `$PATH' to find the new location. This is also
|
||||
available with `shopt -s checkhash'.
|
||||
|
||||
2. The message printed by the job control code and builtins when a job
|
||||
exits with a non-zero status is `Done(status)'.
|
||||
|
||||
3. The message printed by the job control code and builtins when a job
|
||||
is stopped is `Stopped(SIGNAME)', where SIGNAME is, for example,
|
||||
`SIGTSTP'.
|
||||
|
||||
4. The `bg' builtin uses the required format to describe each job
|
||||
placed in the background, which does not include an indication of
|
||||
whether the job is the current or previous job.
|
||||
|
||||
5. Reserved words appearing in a context where reserved words are
|
||||
recognized do not undergo alias expansion.
|
||||
|
||||
6. The POSIX `PS1' and `PS2' expansions of `!' to the history number
|
||||
and `!!' to `!' are enabled, and parameter expansion is performed
|
||||
on the values of `PS1' and `PS2' regardless of the setting of the
|
||||
`promptvars' option.
|
||||
|
||||
7. The POSIX startup files are executed (`$ENV') rather than the
|
||||
normal Bash files.
|
||||
|
||||
8. Tilde expansion is only performed on assignments preceding a
|
||||
command name, rather than on all assignment statements on the line.
|
||||
|
||||
9. The default history file is `~/.sh_history' (this is the default
|
||||
value of `$HISTFILE').
|
||||
|
||||
10. The output of `kill -l' prints all the signal names on a single
|
||||
line, separated by spaces, without the `SIG' prefix.
|
||||
|
||||
11. The `kill' builtin does not accept signal names with a `SIG'
|
||||
prefix.
|
||||
|
||||
12. Non-interactive shells exit if FILENAME in `.' FILENAME is not
|
||||
found.
|
||||
|
||||
13. Non-interactive shells exit if a syntax error in an arithmetic
|
||||
expansion results in an invalid expression.
|
||||
|
||||
14. Redirection operators do not perform filename expansion on the word
|
||||
in the redirection unless the shell is interactive.
|
||||
|
||||
15. Redirection operators do not perform word splitting on the word in
|
||||
the redirection.
|
||||
|
||||
16. Function names must be valid shell names. That is, they may not
|
||||
contain characters other than letters, digits, and underscores, and
|
||||
may not start with a digit. Declaring a function with an invalid
|
||||
name causes a fatal syntax error in non-interactive shells.
|
||||
|
||||
17. POSIX special builtins are found before shell functions during
|
||||
command lookup.
|
||||
|
||||
18. If a POSIX special builtin returns an error status, a
|
||||
non-interactive shell exits. The fatal errors are those listed in
|
||||
the POSIX standard, and include things like passing incorrect
|
||||
options, redirection errors, variable assignment errors for
|
||||
assignments preceding the command name, etc.
|
||||
|
||||
19. If `CDPATH' is set, the `cd' builtin will not implicitly append
|
||||
the current directory to it. This means that `cd' will fail if no
|
||||
valid directory name can be constructed from any of the entries in
|
||||
`$CDPATH', even if the a directory with the same name as the name
|
||||
given as an argument to `cd' exists in the current directory.
|
||||
|
||||
20. A non-interactive shell exits with an error status if a variable
|
||||
assignment error occurs when no command name follows the assignment
|
||||
statements. A variable assignment error occurs, for example, when
|
||||
trying to assign a value to a readonly variable.
|
||||
|
||||
21. A non-interactive shell exits with an error status if the iteration
|
||||
variable in a `for' statement or the selection variable in a
|
||||
`select' statement is a readonly variable.
|
||||
|
||||
22. Process substitution is not available.
|
||||
|
||||
23. Assignment statements preceding POSIX special builtins persist in
|
||||
the shell environment after the builtin completes.
|
||||
|
||||
24. Assignment statements preceding shell function calls persist in the
|
||||
shell environment after the function returns, as if a POSIX
|
||||
special builtin command had been executed.
|
||||
|
||||
25. The `export' and `readonly' builtin commands display their output
|
||||
in the format required by POSIX.
|
||||
|
||||
26. The `trap' builtin displays signal names without the leading `SIG'.
|
||||
|
||||
27. The `trap' builtin doesn't check the first argument for a possible
|
||||
signal specification and revert the signal handling to the original
|
||||
disposition if it is, unless that argument consists solely of
|
||||
digits and is a valid signal number. If users want to reset the
|
||||
handler for a given signal to the original disposition, they
|
||||
should use `-' as the first argument.
|
||||
|
||||
28. The `.' and `source' builtins do not search the current directory
|
||||
for the filename argument if it is not found by searching `PATH'.
|
||||
|
||||
29. Subshells spawned to execute command substitutions inherit the
|
||||
value of the `-e' option from the parent shell. When not in POSIX
|
||||
mode, Bash clears the `-e' option in such subshells.
|
||||
|
||||
30. Alias expansion is always enabled, even in non-interactive shells.
|
||||
|
||||
31. When the `alias' builtin displays alias definitions, it does not
|
||||
display them with a leading `alias ' unless the `-p' option is
|
||||
supplied.
|
||||
|
||||
32. When the `set' builtin is invoked without options, it does not
|
||||
display shell function names and definitions.
|
||||
|
||||
33. When the `set' builtin is invoked without options, it displays
|
||||
variable values without quotes, unless they contain shell
|
||||
metacharacters, even if the result contains nonprinting characters.
|
||||
|
||||
34. When the `cd' builtin is invoked in LOGICAL mode, and the pathname
|
||||
constructed from `$PWD' and the directory name supplied as an
|
||||
argument does not refer to an existing directory, `cd' will fail
|
||||
instead of falling back to PHYSICAL mode.
|
||||
|
||||
35. When the `pwd' builtin is supplied the `-P' option, it resets
|
||||
`$PWD' to a pathname containing no symlinks.
|
||||
|
||||
36. The `pwd' builtin verifies that the value it prints is the same as
|
||||
the current directory, even if it is not asked to check the file
|
||||
system with the `-P' option.
|
||||
|
||||
37. When listing the history, the `fc' builtin does not include an
|
||||
indication of whether or not a history entry has been modified.
|
||||
|
||||
38. The default editor used by `fc' is `ed'.
|
||||
|
||||
39. The `type' and `command' builtins will not report a non-executable
|
||||
file as having been found, though the shell will attempt to
|
||||
execute such a file if it is the only so-named file found in
|
||||
`$PATH'.
|
||||
|
||||
40. The `vi' editing mode will invoke the `vi' editor directly when
|
||||
the `v' command is run, instead of checking `$FCEDIT' and
|
||||
`$EDITOR'.
|
||||
|
||||
41. When the `xpg_echo' option is enabled, Bash does not attempt to
|
||||
interpret any arguments to `echo' as options. Each argument is
|
||||
displayed, after escape characters are converted.
|
||||
|
||||
|
||||
There is other POSIX behavior that Bash does not implement by default
|
||||
even when in POSIX mode. Specifically:
|
||||
|
||||
1. The `fc' builtin checks `$EDITOR' as a program to edit history
|
||||
entries if `FCEDIT' is unset, rather than defaulting directly to
|
||||
`ed'. `fc' uses `ed' if `EDITOR' is unset.
|
||||
|
||||
2. As noted above, Bash requires the `xpg_echo' option to be enabled
|
||||
for the `echo' builtin to be fully conformant.
|
||||
|
||||
|
||||
Bash can be configured to be POSIX-conformant by default, by specifying
|
||||
the `--enable-strict-posix-default' to `configure' when building.
|
||||
</code>
|
||||
|
||||
|
||||
FIXME help me to find out what breaks in POSIX(r) mode!
|
||||
|
||||
__**The POSIX(r) mode can be switched on by:**__
|
||||
* Bash starting as ''sh'' (the basename of ''argv[0]'' is ''sh'')
|
||||
* starting Bash with the commandline option ''<nowiki>--posix</nowiki>''
|
||||
* on startup, the environment variable [[syntax:shellvars#POSIXLY_CORRECT|POSIXLY_CORRECT]] is set
|
||||
* the command ''set -o posix''
|
||||
|
||||
__**Tests for the POSIX(r) mode:**__
|
||||
* the variable [[syntax:shellvars#SHELLOPTS|SHELLOPTS]] contains ''posix'' in its list
|
||||
|
||||
==== Restricted shell ====
|
||||
|
||||
In restricted mode, Bash sets up (and runs) a shell environment that's far more controlled and limited than the standard shell mode. It acts like normal Bash with the following restrictions:
|
||||
|
||||
* the ''cd'' command can't be used to change directories
|
||||
* the variables [[syntax:shellvars#SHELL|SHELL]], [[syntax:shellvars#PATH|PATH]], [[syntax:shellvars#ENV|ENV]] and [[syntax:shellvars#BASH_ENV|BASH_ENV]] can't be set or unset
|
||||
* command names that contain a ''/'' (slash) can't be called (hence you're limited to ''PATH'')
|
||||
* filenames containing a ''/'' (slash) can't be specified as argument to the ''source'' or ''.'' builtin command
|
||||
* filenames containing a ''/'' (slash) can't be specified as argument to the ''-p'' option of the ''hash'' builtin command
|
||||
* function definitions are not inherited from the environment at shell startup
|
||||
* the environment variable [[syntax:shellvars#SHELLOPTS|SHELLOPTS]] is ignored at startup
|
||||
* redirecting output using the ''>'', ''>|'', ''<>'', ''>&'', ''&>'', and ''<nowiki>>></nowiki>'' redirection operators isn't allowed
|
||||
* the ''exec'' builtin command can't replace the shell with another process
|
||||
* adding or deleting builtin commands with the ''-f'' and ''-d'' options to the enable builtin command is forbidden
|
||||
* using the ''enable'' builtin command to enable disabled shell builtins doesn't work
|
||||
* the ''-p'' option to the ''command'' builtin command doesn't work
|
||||
* turning off restricted mode with ''set +r'' or ''set +o restricted'' is (of course) forbidden
|
||||
|
||||
The "-r" restrictions are turned on **after** Bash has read its startup files.
|
||||
|
||||
When the command that is run is a shell script, then the restrictions are **turned off** for the (sub-)shell that runs that shell script.
|
||||
|
||||
|
||||
__**The restricted shell can be switched on by:**__
|
||||
* calling Bash as ''rbash'' (the basename of ''argv[0]'' is ''rbash'')
|
||||
* calling Bash with the ''-r'' option
|
||||
* calling Bash with the ''<nowiki>--restricted</nowiki>'' option
|
||||
|
||||
__**Tests for restricted mode:**__
|
||||
* the special parameter ''$-'' contains the letter ''r'' (lowercase R)
|
||||
* the shell option ''[[internals:shell_options#restricted_shell |restricted_shell]]'' is set and can be checked by the ''shopt'' builtin command
|
313
original_source/scripting/bashchanges.txt
Normal file
313
original_source/scripting/bashchanges.txt
Normal file
@ -0,0 +1,313 @@
|
||||
====== Bash changes ======
|
||||
|
||||
This article is an **incomplete overview** of changes to Bash over time. Not all changes are listed, just the ones most likely to be useful for normal scripting. The overviews are categorized by topic and ordered by version.
|
||||
|
||||
A useful starting point is the [[https://github.com/bminor/bash/blob/master/NEWS|NEWS file]] in bash sources. If you have more detailed information, or historical information about Bash versions earlier than V2, feel free to mail me, or use the discussion below.
|
||||
|
||||
Status: 5.1 (alpha)
|
||||
|
||||
===== Shell options =====
|
||||
Note that the ''shopt'' builtin command first appeared in Bash 2.0.
|
||||
|
||||
For this topic, see also
|
||||
* [[internals:shell_options]]
|
||||
* [[commands:builtin:set]]
|
||||
|
||||
^ Feature or change description ^ Appeared in Bash version ^ See also/remarks ^
|
||||
| ''posix'' (for ''set -o'') | 1.14.0 | |
|
||||
| ''hostcomplete'' | 2.0-alpha3 | |
|
||||
| ''expand_aliases'' | 2.0 | |
|
||||
| ''huponexit'' | 2.02-alpha1 | |
|
||||
| ''nocaseglob'' | 2.02-alpha1 | |
|
||||
| ''extglob'' | 2.02-alpha1 | together with extended globbing, KSH88 |
|
||||
| ''restricted_shell'' | 2.03-alpha | |
|
||||
| ''xpg_echo'' | 2.04-beta1 | |
|
||||
| ''progcomp'' | 2.04-alpha1 | |
|
||||
| ''no_empty_command_completion'' | 2.04 | |
|
||||
| ''login_shell'' | 2.05a-alpha1 | |
|
||||
| ''nolog'' (for ''set -o'') | 2.05a | |
|
||||
| ''gnu_errfmt'' | 3.0-alpha | |
|
||||
| ''force_fignore'' | 3.0-alpha | |
|
||||
| ''failglob'' | 3.0-alpha | |
|
||||
| ''extquote'' | 3.0-alpha | unsure -- verify! |
|
||||
| ''extdebug'' | 3.0-alpha | |
|
||||
| ''pipefail'' (for ''set -o'') | 3.0 | |
|
||||
| ''functrace'' (for ''set -o'') | 3.0 | |
|
||||
| ''errtrace'' (for ''set -o'') | 3.0 | |
|
||||
| ''nocasematch'' | 3.1-alpha1 | |
|
||||
| ''dirspell'' | 4.0-alpha | |
|
||||
| ''globstar'' | 4.0-alpha | |
|
||||
| ''checkjobs'' | 4.0-alpha | |
|
||||
| ''autocd'' | 4.0-alpha | |
|
||||
| ''set -e'' effects more intuitive | 4.0 | not directly specified by POSIX, but in consensus with POSIX WG |
|
||||
| ''compat40'' | 4.1-beta | |
|
||||
| ''lastpipe'' | 4.2-alpha | only works with job control disabled |
|
||||
| ''compat41'' | 4.2-alpha | |
|
||||
| ''globasciiranges'' | 4.3-alpha | enable "character range globbing" to always act as if in ''C'' locale |
|
||||
| ''compat42'' | 4.3-alpha | |
|
||||
| ''compat43'' | 4.4-alpha | |
|
||||
| ''compat44'' | 5.0-alpha | |
|
||||
| ''localvar_inherit'' | 5.0-alpha | local variables inherit preceeding scope values if they have the same name |
|
||||
| ''syslog_history'' | 5.0-alpha | send history lines to syslog (undocumented, default off) if syslog is supported |
|
||||
| ''assoc_expand_once'' | 5.0-alpha | expand associative array subscripts only one |
|
||||
| ''globasciiranges'' | 5.0-beta | New default: on (default may be configured at compile time) |
|
||||
| ''localvar_inherit'' | 5.0-beta | guard code against inheriting from an incompatible data type |
|
||||
| ''checkwinsize'' | 5.0-beta2 | New default: on |
|
||||
| ''shift_verbose'' | 5.0-beta2 | Default on when in POSIX mode |
|
||||
|
||||
==== General (all/many builtins) ====
|
||||
^Feature or change description ^Appeared in Bash version ^See also/remarks ^
|
||||
|generally return 2 on usage error |2.0 | |
|
||||
|generally accept ''<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 |
|
||||
|
||||
==== printf ====
|
||||
|
||||
For this topic, see also
|
||||
* [[commands:builtin:printf]]
|
||||
|
||||
^ Feature or change description ^ Appeared in Bash version ^ See also/remarks ^
|
||||
| new ''printf'' command | 2.02-alpha1 | |
|
||||
| respects ''0..'' and ''0x..'' prefixed numbers | 2.04-beta1 | consistency with arithmetic |
|
||||
| POSIX(r) length specifiers ''j'', ''t'' and ''z'' | 2.05a-alpha1 | ISO C99 |
|
||||
| POSIX(r) flag ''%%'%%'' | 2.05a-alpha1 | |
|
||||
| conversion ''a'' and ''A'' | 2.05a-rc1 | if provided by the underlying printf(3) |
|
||||
| conversion ''F'' | 2.05a-rc1 | |
|
||||
| conversion ''n'' | 2.05a-rc1 | |
|
||||
| new option ''-v'' | 3.1-alpha1 | |
|
||||
| escape sequences ''\"'' and ''\?'' | 3.0-beta1 | |
|
||||
| modified option ''-v'' to assign to individual array elements | 4.1-alpha | |
|
||||
| conversion ''(...)T'' | 4.2-alpha | support stftime(3) date/time format; uses current time |
|
||||
| ''\uNNNN'' and ''\UNNNNNNNN'' escape sequences | 4.2-alpha | for: ''printf'', ''echo -e'', ''%%$'...'%%'' |
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
==== Conditional expressions and test command ====
|
||||
|
||||
For this topic, see also
|
||||
* [[syntax:ccmd:conditional_expression]]
|
||||
* [[commands:classictest]]
|
||||
|
||||
^ Feature or change description ^ Appeared in Bash version ^ See also/remarks ^
|
||||
| ''test'': ''-o'', ''=='', ''<'' and ''>'' | 2.0 | |
|
||||
| ''test'': ''-N'' | 2.02 | |
|
||||
| ''%%[[...]]%%'': new | 2.02-alpha1 | KSH93 |
|
||||
| ''%%[[...]]%%'': regex support (''=~'') | 3.0-alpha | |
|
||||
| ''%%[[...]]%%'': quotable right-hand-side of ''=~'' forces string matching | 3.2-alpha | for consistency with pattern matching |
|
||||
| ''%%[[...]]%%'': ''<'' and ''>'' operators respect locale | 4.1-alpha | for consistency, since 4.1-beta: ensure you have set compatiblity to >4.0 (default) |
|
||||
| ''test''/''%%[%%''/''%%[[%%'': ''-v'' | 4.2-alpha | check if a variable is set |
|
||||
| ''test''/''%%[%%''/''%%[[%%'': ''-v'' | 4.2-alpha | support array syntax to check for elements |
|
||||
| ''test''/''%%[%%''/''%%[[%%'': ''-N'' accepts nanoseconds | 5.1-alpha | |
|
||||
| ''test''/''%%[%%''/''%%[[%%'': ''-v'' accepts positional parameters | 5.1-alpha | |
|
||||
|
||||
==== Other builtins and keywords ====
|
||||
^ Builtin ^ Feature or change description ^ Appeared in Bash version ^ See also/remarks ^
|
||||
| ''bashbug'' | new | 1.14.0 | |
|
||||
| ''select'' | new | 1.14.0 | |
|
||||
| ''disown'' | new | 2.0 | |
|
||||
| ''shopt'' | new | 2.0 | [[commands:builtin:shopt]] |
|
||||
| ''declare'' | new options ''-a'' and ''-F'' | 2.0 | |
|
||||
| ''enable'' | builtin has basic plugin support (dlopen) | 2.0 | |
|
||||
| ''exec'' | options ''-l'', ''-c'' and ''-a'' | 2.0 | |
|
||||
| ''read'' | options ''-p'', ''-e'' and ''-a'' | 2.0 | [[commands:builtin:read]] |
|
||||
| ''readonly'' | option ''-a'' | 2.0 | [[syntax:arrays]] |
|
||||
| ''time'' | new keyword | 2.0 | |
|
||||
| ''shopt'' | ''-p'' (reusable output) | 2.02 | |
|
||||
| ''umask'' | ''-p'' (reusable output) | 2.02 | |
|
||||
| ''complete'' | new | 2.04-devel | for and together with support for programmable completion |
|
||||
| ''compgen'' | new | 2.04-devel | for and together with support for programmable completion |
|
||||
| ''read'' | options ''-t'', ''-n'', ''-d'', ''-s'' | 2.04-devel | [[commands:builtin:read]] |
|
||||
| ''%%for ((...;...;...))%%'' | new | 2.04-devel | KSH93 |
|
||||
| ''set'' | print shell functions in a format reusable as input | 2.05-beta1 | |
|
||||
| ''for'' | allow an empty word list | 2.05a-alpha1 | |
|
||||
| ''read'' | new option ''-u'' | 2.05b-alpha1 | [[commands:builtin:read]] |
|
||||
| ''caller'' | new | 3.0 | [[commands:builtin:caller]] |
|
||||
| ''coproc'' | new | 4.0-alpha | |
|
||||
| ''declare'' | new options ''-l'' and ''-u'' | 4.0-alpha | together with case-changing expansion forms |
|
||||
| ''case'' | new action list terminators ''%%;;&%% and ''%%;&%% | 4.0-alpha | ksh93: only '';&''. zsh and mksh: '';|''. mksh: all 4, ('';;&'' is undocumented Bash compatibility) |
|
||||
| ''read'' | changed ''-t'' (fractional seconds) | 4.0-alpha | |
|
||||
| ''mapfile'' | new | 4.0-alpha | |
|
||||
| ''read'' | new option ''-i'' | 4.0-alpha | |
|
||||
| ''compopt'' | new | 4.0-alpha | |
|
||||
| ''read'' | modified option ''-t'' to test for data | 4.0-beta | |
|
||||
| ''read'' | new option ''-N'' | 4.1-alpha | |
|
||||
| ''mapfile'' | changed behaviour regarding history spamming | 4.1-alpha | |
|
||||
| ''declare'' | new option ''-g'' | 4.2-alpha | |
|
||||
| ''mapfile'' | calls the callback with an additional argument: The line (data) | 4.2-alpha | |
|
||||
| ''cd'' | new option ''-e'' | 4.2-alpha | |
|
||||
| ''echo'' | ''\uNNNN'' and ''\UNNNNNNNN'' escape sequences | 4.2-alpha | for: ''printf'', ''echo -e'', ''%%$'...'%%'' |
|
||||
| ''exec'' | option ''-a'' to give a ''argv[0]'' string | 4.2-alpha | |
|
||||
| ''time'' | allowed as a command by itself to display timing values of the shell and its children | 4.2-alpha | POSIX change |
|
||||
| ''help'' | ''help'' now searches exact topic-strings (i.e. ''help read'' won't find ''readonly'' anymore) | 4.3-alpha | |
|
||||
| ''return'' | accept negative values as return value (e.g. ''return -1'' will show as (8 bit) 255 in the caller) | 4.3-alpha | |
|
||||
| ''exit'' | accept negative values as return value (e.g. ''return -1'' will show as (8 bit) 255 in the caller) | 4.3-alpha | |
|
||||
| ''read'' | ''read'' skips ''NUL'' (ASCII Code 0) in input | 4.3-alpha | |
|
||||
| ''declare'' | new option ''-n''/''+n'' to support nameref variable type | 4.3-alpha | |
|
||||
| ''wait'' | new option ''-n'' to wait for the next background job to finish, returning its exit status. | 4.3-alpha | |
|
||||
| ''read'' | ''read'' checks first variable argument for validity before trying to read inout | 4.3-beta | |
|
||||
| ''help'' | attempts substring matching (as it did through bash-4.2) if exact string matching fails | 4.3-beta2 | |
|
||||
| ''fc'' | interprets option ''-0'' (zero) as the current command line | 4.3-beta2 | |
|
||||
| ''cd'' | new option ''-@'' to browse a file's extended attributes (on systems that support ''O_XATTR'') | 4.3-rc1 | |
|
||||
| ''kill'' | new option ''-L'' (upper case ell) to list signals like the normal lowercase option ''-l'' (compatiblity with some standalone ''kill'' commands) | 4.4-beta | |
|
||||
| ''mapfile'' | new option ''-d'' | 4.4-alpha | |
|
||||
| ''wait'' | new option ''-f'' | 5.0-alpha | |
|
||||
| ''history'' | option ''-d'' allows negative numbers to index from the end of the history list | 5.0-alpha | |
|
||||
| ''umask'' | allows modes greater than octal 777 | 5.0-alpha | |
|
||||
| ''times'' | honors current locale settings when printing decimal points | 5.0-alpha | |
|
||||
| ''kill'' | New options ''-n SIGNUMBER'' and ''-s SIGNAME'' | 5.0-beta2 | [[commands:builtin:kill]] |
|
||||
| ''select'' | Support for an empty wordlist following ''in'' | 5.0-beta2 | |
|
||||
| ''read'' | Option ''-e'' (use ReadLine to obtain input) now works with arbitrary file descriptors (given by ''-u'' option) | 5.1-alpha | |
|
||||
| ''trap'' | ''-p'' option prints signals with SIG_DFL/SIG_IGN on shell start (POSIX mode) | 5.1-alpha | |
|
||||
| ''unset'' | automatically tries to unset a function if the given name is an invalid variable name | 5.1-aplha | |
|
||||
| ''wait'' | option ''-n'' now accepts a list of jobs | 5.1-alpha | |
|
||||
| ''wait'' | new option ''-p NAME'' to store PID/JobID (useful when waiting for a list of jobs) | 5.1-alpha | |
|
||||
| ''local'' | new option ''-p'' to print local variables in the current scope | 5.1-alpha | |
|
||||
| ''ulimit'' | new option ''-R'' to get/set ''RLIMIT_RTTIME'' resource | 5.1-alpha | |
|
||||
|
||||
|
||||
===== Builtin variables =====
|
||||
^ Feature or change description ^ Appeared in Bash version ^ See also ^
|
||||
| ''HISTCMD'' | 1.14.0 | interactive usage |
|
||||
| ''PS1'', ''PS2'', ''PATH'', and ''IFS'' are unsettable | 2.0 | |
|
||||
| ''DIRSTACK'' array variable | 2.0 | |
|
||||
| ''PIPESTATUS'' array variable | 2.0 | |
|
||||
| ''BASH_VERSINFO'' array variable | 2.0 | |
|
||||
| ''HOSTNAME'' | 2.0 | |
|
||||
| ''SHELLOPTS'' | 2.0 | |
|
||||
| ''MACHTYPE'' | 2.0 | |
|
||||
| ''GLOBIGNORE'' | 2.0 | |
|
||||
| ''HISTIGNORE'' | 2.0 | |
|
||||
| respect ''LC_ALL'' | 2.0 | |
|
||||
| respect ''LC_MESSAGES'' | 2.0 | |
|
||||
| respect ''LC_CTYPE'' | 2.0 | |
|
||||
| respect ''LC_COLLATE'' | 2.0 | |
|
||||
| respect ''LANG'' | 2.0 | |
|
||||
| ''GROUPS'' array variable | 2.01 | |
|
||||
| ''GROUPS'' unsettable/takes (discarded) assignments | 2.04 | |
|
||||
| ''FUNCNAME'' | 2.04 | |
|
||||
| respect ''LC_NUMERIC'' | 2.04 | |
|
||||
| ''TMOUT'' | 2.05b | |
|
||||
| ''BASH_REMATCH'' | 3.0 | together with regex support in ''%%[[...]]%%'' |
|
||||
| ''BASH_ARGC'' | 3.0 | debugger support |
|
||||
| ''BASH_ARGV'' | 3.0 | debugger support |
|
||||
| ''BASH_SOURCE'' | 3.0 | debugger support |
|
||||
| ''BASH_LINENO'' | 3.0 | debugger support |
|
||||
| ''BASH_SUBSHELL'' | 3.0 | debugger support |
|
||||
| ''BASH_EXECUTION_STRING'' | 3.0 | debugger support |
|
||||
| ''BASH_COMMAND'' | 3.0 | debugger support |
|
||||
| ''HISTTIMEFORMAT'' | 3.0 | |
|
||||
| ''COMP_WORDBREAKS'' | 3.0 | |
|
||||
| respect ''LC_TIME'' | 3.1 | |
|
||||
| ''BASHPID'' | 4.0-alpha | Added to mksh R41. |
|
||||
| ''PROMPT_DIRTRIM'' | 4.0 | |
|
||||
| ''BASH_XTRACEFD'' | 4.1-alpha | |
|
||||
| ''BASHOPTS'' | 4.1-alpha | |
|
||||
| ''FUNCNEST'' | 4.2-alpha | |
|
||||
| ''HISTSIZE'' | 4.3-alpha | can be set to negative values for unlimited history length |
|
||||
| ''HISTFILESIZE'' | 4.3-alpha | can be set to negative values for unlimit history file size |
|
||||
| ''CHILD_MAX'' | 4.3-alpha | max. number of exit status of children the shell remembers |
|
||||
| ''BASH_COMPAT'' | 4.3-alpha | set shell compatiblity levels |
|
||||
| ''EPOCHSECONDS'' | 5.0-alpha | expands to the time in seconds since Unix epoch |
|
||||
| ''EPOCHREALTIME'' | 5.0-alpha | expands to the time in seconds since Unix epoch with microsecond granularity |
|
||||
| ''BASH_ARGV0'' | 5.0-alpha | get/set ''$0'' |
|
||||
| ''PATH'' | 5.0-alpha | Possibility to set a static path for use in a restricted shell (at compile time) |
|
||||
| ''HISTSIZE'' | 5.0-beta | Default can now be set at runtime |
|
||||
| ''SRANDOM'' | 5.1-alpha | New random generator for 32bit numbers (using various methods in the backend) |
|
||||
| ''ARGV0'' | 5.1-alpha | Respected when set in initial shell environment, then initially used to set ''$0'' |
|
||||
| ''BASH_REMATCH'' | 5.1-alpha | Not readonly anymore |
|
||||
| ''PROMPT_COMMANDS'' | 5.1-alpha | New array variable. List of commands to be executed like ''PROMPT_COMMAND'' |
|
||||
| ''SECONDS'' | 5.1-alpha | Assignment using arithmetic expressions (is nominally an integer variabnle) |
|
||||
| ''RANDOM'' | 5.1-alpha | Assignment using arithmetic expressions (is nominally an integer variabnle) |
|
||||
| ''LINENO'' | 5.1-alpha | Not an integer variabe |
|
||||
|
||||
===== Quoting, expansions, substitutions and related =====
|
||||
For this topic, see also
|
||||
* [[syntax:pe]].
|
||||
|
||||
^ Feature or change description ^ Appeared in Bash version ^ Remarks ^
|
||||
| Support for integer-indexed arrays | 2.0 | relevant builtins also got array support |
|
||||
| ''%%${PARAMETER//PATTERN/REPLACEMENT}%%'' | 2.0 | |
|
||||
| ''%%${PARAMETER:OFFSET:LENGTH}%%'' | 2.0 | |
|
||||
| ''%%${!PARAMETER}%%'' (indirection) | 2.0 | |
|
||||
| ''%%$"..."%%'' (localized strings) | 2.0 | |
|
||||
| ''%%$'...'%%'' (ANSI-C-like strings) | 2.0 | |
|
||||
| ''%%\xNNN%%'' in ''%%$'...'%%'' (and ''echo -e'') | 2.02-alpha1 | |
|
||||
| ''%%$(< FILENAME)%%'' (file content) | 2.02-alpha1 | |
|
||||
| globbing (''fnmatch()'') capable of POSIX(r) character classes etc. | 2.02-alpha1 | |
|
||||
| extended globbing | 2.02-alpha1 | KSH88 |
|
||||
| globbing inside array mass-assignment: ''ARRAY=(*.txt)'' | 2.03-alpha | |
|
||||
| ''%%$'...\'...'%%'' escaped single quote inside ANSI-C-like strings | 2.04-devel | KSH93 |
|
||||
| ''%%${!PREFIX*}%%'' (parameter name expansion) | 2.04 | KSH93 |
|
||||
| ''%%$'...'%%'' expands ''\cx'' (Control-x) | 2.05b | |
|
||||
| ''[:class:]'' syntax for pattern matching | 2.05b | KSH93 |
|
||||
| ''${!ARRAY[@]}'' (array index expansion) | 3.0-alpha | KSH93 |
|
||||
| ''{x..y}'' (range brace expansion) | 3.0-alpha | |
|
||||
| ''%%$'...'%%'' expands ''\xNNN'' (Hexdigits) | 3.0 | |
|
||||
| ''+='' operator for arrays and strings | 3.1-alpha1 | |
|
||||
| ''%%${PARAMETER//PATTERN/REPLACEMENT}%%'' behaviour changed | 3.2-alpha | anchoring for global substitution is no longer allowed, changes the way old syntax may work |
|
||||
| ''${@:0:x}'' includes ''$0'' | 4.0-alpha | |
|
||||
| Support for associative arrays | 4.0-alpha | relevant builtins also got associative array support |
|
||||
| case modification operators for expansions | 4.0-alpha | |
|
||||
| ''{0x..0y}'' (zeropadding brace expansion) | 4.0-alpha | |
|
||||
| numerically indexed arrays can be accessed (on expansion time) from the end using negative indexes | 4.1-alpha | |
|
||||
| ''%%\uNNNN%%'' and ''%%\uNNNNNNNN%%'' in ''%%$'...'%%'' | 4.2-alpha | for: ''printf'', ''echo -e'', ''%%$'...'%%'' |
|
||||
| ''%%${PARAMETER:OFFSET:LENGTH}%%'': Negative ''LENGTH'' values are used as offset from the end of the string | 4.2-alpha | Substrings only for Bash and ksh93. Works also for argument expansions in zsh. ksh93 can use ''${arr[n..-m]}''. |
|
||||
| Word expansions like ''${foo##bar}'' understand indirect variable references | 4.3-beta | |
|
||||
| Transformations | 4.4 | |
|
||||
| Process substitution now works in POSIX mode | 5.1-alpha | |
|
||||
| New transformations: ''U'', ''u'', ''L'' | 5.1-alpha | Case-transformation |
|
||||
| New transformation: ''K'' | 5.1-alpha | Display associative arrays as key/value pairs |
|
||||
|
||||
|
||||
|
||||
|
||||
===== Arithmetic =====
|
||||
For this topic, see also
|
||||
* [[syntax:arith_expr]]
|
||||
* [[syntax:expansion:arith]]
|
||||
|
||||
^Feature or change description ^Appeared in Bash version ^Remarks ^
|
||||
|''<nowiki>((...))</nowiki>'' |2.0-beta2 |KSH93 |
|
||||
|ternary operator |2.0 | |
|
||||
|base 64 integer constants |2.0 |the max. base before is unknown. Anybody? |
|
||||
|deprecated ''$[...]'' in favor of ''<nowiki>$((...))</nowiki>'' |2.0 | |
|
||||
|exponentiaition operator (''<nowiki>**</nowiki>'') |2.02-alpha1 | |
|
||||
|comma operator ''EXPR,EXPR'' |2.04-devel | |
|
||||
|pre- and postfix operators |2.04-devel | |
|
||||
|
||||
|
||||
===== Redirection and related =====
|
||||
For this topic, see also
|
||||
* [[syntax:redirection]]
|
||||
|
||||
^ Feature or change description ^ Appeared in Bash version ^ Remarks ^
|
||||
| socket redirection (''/dev/tcp/'', ''/dev/udp/'') | 2.04-devel | |
|
||||
| OS/filesystem-independent support for ''/dev/std(in|out|err)'' and ''/dev/fd/*'' | 2.04 | |
|
||||
| socket redirection accepts service names | 2.05 | |
|
||||
| ''[n]<&word-'' and ''[n]>&word-'' FD-duplicate/closing | 2.05b-alpha1 | KSH93 |
|
||||
| Here strings: ''%%<<< WORD%%'' | 2.05b-alpha1 | |
|
||||
| ''|&'' (synonym for ''2>&1 |'') | 4.0-alpha | |
|
||||
| ''%%&>>%%'' (equiv. to ''%%>>FILE 2>&1%%'') | 4.0-alpha | |
|
||||
| ''<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 |
|
||||
|
||||
|
||||
===== Misc =====
|
||||
|
||||
^ Feature or change description ^ Appeared in Bash version ^ See also/remarks ^
|
||||
| ''DEBUG'' trap | 2.0 | |
|
||||
| ''ERR'' trap | 2.05a | KSH93 |
|
||||
| Support for multibyte characters: Unicode / UTF8 | 2.05b | |
|
||||
| ''RETURN'' trap | 3.0 | ksh93 ''EXIT'' trap evaluates in caller scope (for ''function name {''). Bash ''RETURN'' in same scope. |
|
||||
| ''command_not_found_handle'' handler function | 4.0-alpha | |
|
||||
| official introduction of switchable "compatiblity levels" | 4.0-alpha | ''compat31'' was introduced in a 3.2 version, mainly because of the incompatibilities that were introduced by the changed ''=~'' operator |
|
||||
| ''%%[[...]]%%'' and ''%%((...))%%'' conditional commands are subject to the ''ERR'' trap and ''set -e'' feature | 4.1-alpha | |
|
||||
| ACL support for file status checks | 4.1-alpha | |
|
||||
| Assignment to negative array indices | 4.3-alpha | ksh93, zsh |
|
||||
| ''declare''/''typeset -n'' | 4.3-alpha | Support for nameref variable type, a variable referencing another one by name |
|
||||
| shells started to run process substitutions now run any trap set on ''EXIT'' | 4.3-beta | |
|
||||
| process substitution does not inherit the ''v'' flag | 5.0-alpha | |
|
||||
| ''ERR'' trap | 5.0-alpha | Reports more reliable line numbers |
|
||||
| Variable assignment | 5.0-beta | Assignments preceeding a special builtin that chages variable attributes are not propagated back unless compatiblity mode is 44 or lower |
|
250
original_source/scripting/basics.txt
Normal file
250
original_source/scripting/basics.txt
Normal file
@ -0,0 +1,250 @@
|
||||
====== The basics of shell scripting ======
|
||||
|
||||
{{keywords>bash shell scripting basics learning tutorial}}
|
||||
|
||||
===== Script files =====
|
||||
A shell script usually resides inside a file. The file can be executable, but you can call a Bash script with that filename as a parameter:
|
||||
<code>
|
||||
bash ./myfile
|
||||
</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.
|
||||
<code>
|
||||
chmod +x ./myfile
|
||||
</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.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
===== The Shebang =====
|
||||
The in-file specification of the interpreter of that file, for example:
|
||||
<code bash>
|
||||
#!/bin/bash
|
||||
echo "Hello world..."
|
||||
</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!
|
||||
|
||||
You can follow the process by using ''echo'' as a fake interpreter:
|
||||
<code>
|
||||
#!/bin/echo
|
||||
</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.
|
||||
<code>
|
||||
$ /home/bash/bin/test testword hello
|
||||
/home/bash/bin/test testword hello
|
||||
</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''"
|
||||
|
||||
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.
|
||||
|
||||
<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.
|
||||
</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:
|
||||
* [[scripting:bashbehaviour | Bash behaviour]].
|
||||
|
||||
A common method is to specify a shebang like
|
||||
<code>
|
||||
#!/usr/bin/env bash
|
||||
</code>
|
||||
...which just moves the location of the potential problem to
|
||||
* the ''env'' utility must be located in /usr/bin/
|
||||
* the needed ''bash'' binary must be located in ''PATH''
|
||||
|
||||
Which one you need, or whether you think which one is good, or bad, is up to you. There is no bulletproof portable way to specify an interpreter. **It's a common misconception that it solves all problems. Period.**
|
||||
|
||||
===== The standard filedescriptors =====
|
||||
Once Initialized, every normal UNIX(r)-program has //at least 3 open files//:
|
||||
|
||||
* **stdin**: standard input
|
||||
* **stdout**: standard output
|
||||
* **stderr**: standard error output
|
||||
|
||||
Usually, they're all connected to your terminal, stdin as input file (keyboard), stdout and stderr as output files (screen). When calling such a program, the invoking shell can change these filedescriptor connections away from the terminal to any other file (see redirection).
|
||||
Why two different output filedescriptors? It's convention to send error messages and warnings to stderr and only program output to stdout. This enables the user to decide if they want to see nothing, only the data, only the errors, or both - and where they want to see them.
|
||||
|
||||
When you write a script:
|
||||
* always read user-input from ''stdin''
|
||||
* always write diagnostic/error/warning messages to ''stderr''
|
||||
|
||||
To learn more about the standard filedescriptors, especially about redirection and piping, see:
|
||||
* [[howto:redirection_tutorial | An illustrated redirection tutorial]]
|
||||
|
||||
===== Variable names =====
|
||||
It's good practice to use lowercase names for your variables, as shell and system-variable names are usually all in UPPERCASE. However, you should avoid naming your variables any of the following (incomplete list!):
|
||||
|''BASH''|''BASH_ARGC''|''BASH_ARGV''|''BASH_LINENO''|''BASH_SOURCE''|''BASH_VERSINFO''|
|
||||
|''BASH_VERSION''|''COLUMNS''|''DIRSTACK''|''DISPLAY''|''EDITOR''|''EUID''|
|
||||
|''GROUPS''|''HISTFILE''|''HISTFILESIZE''|''HISTSIZE''|''HOME''|''HOSTNAME''|
|
||||
|''IFS''|''LANG''|''LANGUAGE''|''LC_ALL''|''LINES''|''LOGNAME''|
|
||||
|''LS_COLORS''|''MACHTYPE''|''MAILCHECK''|''OLDPWD''|''OPTERR''|''OPTIND''|
|
||||
|''OSTYPE''|''PATH''|''PIPESTATUS''|''PPID''|''PROMPT_COMMAND''|''PS1''|
|
||||
|''PS2''|''PS4''|''PS3''|''PWD''|''SHELL''|''SHELLOPTS''|
|
||||
|''SHLVL''|''TERM''|''UID''|''USER''|''USERNAME''|''XAUTHORITY''|
|
||||
This list is incomplete. **The safest way is to use all-lowercase variable names.**
|
||||
|
||||
|
||||
|
||||
|
||||
===== Exit codes =====
|
||||
Every program you start terminates with an exit code and reports it to the operating system. This exit code can be utilized by Bash. You can show it, you can act on it, you can control script flow with it.
|
||||
The code is a number between 0 and 255. Values from 126 to 255 are reserved for use by the shell directly, or for special purposes, like reporting a termination by a signal:
|
||||
* **126**: the requested command (file) was found, but can't be executed
|
||||
* **127**: command (file) not found
|
||||
* **128**: according to ABS it's used to report an invalid argument to the exit builtin, but I wasn't able to verify that in the source code of Bash (see code 255)
|
||||
* **128 + N**: the shell was terminated by the signal N
|
||||
* **255**: wrong argument to the exit builtin (see code 128)
|
||||
|
||||
The lower codes 0 to 125 are not reserved and may be used for whatever the program likes to report.
|
||||
A value of 0 means **successful** termination, a value not 0 means **unsuccessful** termination. This behavior (== 0, != 0) is also what Bash reacts to in some flow control statements.
|
||||
|
||||
An example of using the exit code of the program ''grep'' to check if a specific user is present in /etc/passwd:
|
||||
<code bash>
|
||||
if grep ^root /etc/passwd; then
|
||||
echo "The user root was found"
|
||||
else
|
||||
echo "The user root was not found"
|
||||
fi
|
||||
</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!
|
||||
|
||||
<code bash>
|
||||
if [ "$mystring" = "Hello world" ]; then
|
||||
echo "Yeah dude, you entered the right words..."
|
||||
else
|
||||
echo "Eeeek - go away..."
|
||||
fi
|
||||
</code>
|
||||
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:
|
||||
<code bash>
|
||||
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."
|
||||
</code>
|
||||
|
||||
Please, when your script exits on errors, provide a "FALSE" exit code, so others can check the script execution.
|
||||
|
||||
===== Comments =====
|
||||
In a larger, or complex script, it's wise to comment the code. Comments can help with debugging or tests. Comments start with the # character (hashmark) and continue to the end of the line:
|
||||
<code bash>
|
||||
#!/bin/bash
|
||||
# This is a small script to say something.
|
||||
echo "Be liberal in what you accept, and conservative in what you send" # say something
|
||||
</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.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
==== 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:
|
||||
<code bash>
|
||||
#!/bin/bash
|
||||
# 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
|
||||
logger -t SYSHALT "System halt requested"
|
||||
|
||||
##### The following "code block" is effectively ignored
|
||||
: <<"SOMEWORD"
|
||||
/etc/init.d/mydatabase clean_stop
|
||||
mydatabase_dump /var/db/db1 /mnt/fsrv0/backups/db1
|
||||
logger -t SYSHALT "System halt: pre-shutdown actions done, now shutting down the system"
|
||||
shutdown -h NOW
|
||||
SOMEWORD
|
||||
##### The ignored codeblock ends here
|
||||
</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.
|
||||
|
||||
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
|
||||
|
||||
|
||||
===== Variable scope =====
|
||||
|
||||
|
||||
In Bash, the scope of user variables is generally //global//. That means, it does **not** matter whether a variable is set in the "main program" or in a "function", the variable is defined everywhere.
|
||||
|
||||
Compare the following //equivalent// code snippets:
|
||||
<code bash>
|
||||
myvariable=test
|
||||
echo $myvariable
|
||||
</code>
|
||||
|
||||
<code bash>
|
||||
myfunction() {
|
||||
myvariable=test
|
||||
}
|
||||
|
||||
myfunction
|
||||
echo $myvariable
|
||||
</code>
|
||||
|
||||
In both cases, the variable ''myvariable'' is set and accessible from everywhere in that script, both in functions and in the "main program".
|
||||
|
||||
**__Attention:__** When you set variables in a child process, for example a //subshell//, they will be set there, but you will **never** have access to them outside of that subshell. One way to create a subshell is the pipe. It's all mentioned in a small article about [[scripting:processtree | Bash in the processtree]]!
|
||||
|
||||
==== Local variables ====
|
||||
|
||||
Bash provides ways to make a variable's scope //local// to a function:
|
||||
* Using the ''local'' keyword, or
|
||||
* Using ''declare'' (which will //detect// when it was called from within a function and make the variable(s) local).
|
||||
<code bash>
|
||||
myfunc() {
|
||||
local var=VALUE
|
||||
|
||||
# alternative, only when used INSIDE a function
|
||||
declare var=VALUE
|
||||
|
||||
...
|
||||
}
|
||||
</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:
|
||||
|
||||
<code bash>
|
||||
foo=external
|
||||
|
||||
printvalue() {
|
||||
local foo=internal
|
||||
|
||||
echo $foo
|
||||
}
|
||||
|
||||
|
||||
# this will print "external"
|
||||
echo $foo
|
||||
|
||||
# this will print "internal"
|
||||
printvalue
|
||||
|
||||
# this will print - again - "external"
|
||||
echo $foo
|
||||
</code>
|
||||
|
||||
==== Environment variables ====
|
||||
|
||||
The environment space is not directly related to the topic about scope, but it's worth mentioning.
|
||||
|
||||
Every UNIX(r) process has a so-called //environment//. Other items, in addition to variables, are saved there, the so-called //environment variables//. When a child process is created (in Bash e.g. by simply executing another program, say ''ls'' to list files), the whole environment //including the environment variables// is copied to the new process. Reading that from the other side means: **Only variables that are part of the environment are available in the child process.**
|
||||
|
||||
A variable can be tagged to be part of the environment using the ''export'' command:
|
||||
<code bash>
|
||||
# create a new variable and set it:
|
||||
# -> This is a normal shell variable, not an environment variable!
|
||||
myvariable="Hello world."
|
||||
|
||||
# make the variable visible to all child processes:
|
||||
# -> Make it an environment variable: "export" it
|
||||
export myvariable
|
||||
</code>
|
||||
|
||||
Remember that the //exported// variable is a **copy**. There is no provision to "copy it back to the parent." See the article about [[scripting:processtree | Bash in the process tree]]!
|
327
original_source/scripting/cc.txt
Normal file
327
original_source/scripting/cc.txt
Normal file
File diff suppressed because one or more lines are too long
329
original_source/scripting/debuggingtips.txt
Normal file
329
original_source/scripting/debuggingtips.txt
Normal file
@ -0,0 +1,329 @@
|
||||
====== Debugging a script ======
|
||||
|
||||
{{keywords>bash shell scripting bug debug debugging}}
|
||||
|
||||
These few lines are not intended as a full-fledged debugging tutorial, but as hints and comments about debugging a Bash script.
|
||||
|
||||
|
||||
|
||||
===== Use a unique name for your script =====
|
||||
|
||||
Do **not** name your script ''test'', for example! //Why?// ''test'' is the name of a UNIX(r)-command, and __most likely built into your shell__ (it's a built-in in Bash) - so you won't be able to run a script with the name ''test'' in a normal way.
|
||||
|
||||
**Don't laugh!** This is a classic mistake :-)
|
||||
|
||||
===== Read the error messages =====
|
||||
|
||||
Many people come into IRC and ask something like //"Why does my script fail? I get an error!"//. And when you ask them what the error message is, they don't even know. Beautiful.
|
||||
|
||||
Reading and interpreting error messages is 50% of your job as debugger! Error messages actually **mean** something. At the very least, they can give you hints as to where to start debugging. **READ YOUR ERROR MESSAGES!**
|
||||
|
||||
You may ask yourself why is this mentioned as debugging tip? Well, __you would be surprised how many shell users ignore the text of error messages!__ When I find some time, I'll paste 2 or 3 IRC log-snips here, just to show you that annoying fact.
|
||||
|
||||
|
||||
|
||||
===== Use a good editor =====
|
||||
|
||||
Your choice of editor is a matter of personal preference, but one with **Bash syntax highlighting** is highly recommended! Syntax highlighting helps you see (you guessed it) syntax errors, such as unclosed quotes and braces, typos, etc.
|
||||
|
||||
From my personal experience, I can suggest ''vim'' or ''GNU emacs''.
|
||||
|
||||
===== Write logfiles =====
|
||||
|
||||
For more complex scripts, it's useful to write to a log file, or to the system log. Nobody can debug your script without knowing what actually happened and what went wrong.
|
||||
|
||||
An available syslog interface is ''logger'' ([[http://unixhelp.ed.ac.uk/CGI/man-cgi?logger+1 | online manpage]]).
|
||||
|
||||
|
||||
|
||||
|
||||
===== Inject debugging code =====
|
||||
|
||||
Insert **echos** everywhere you can, and print to ''stderr'':
|
||||
<code>
|
||||
echo "DEBUG: current i=$i" >&2
|
||||
</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!
|
||||
|
||||
<code>
|
||||
pid=$(< fooservice.pid)
|
||||
echo "DEBUG: read from file: pid=\"$pid\"" >&2
|
||||
</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.
|
||||
<code>
|
||||
foo=$(< inputfile)
|
||||
printf "DEBUG: foo is |%q|\n" "$foo" >&2
|
||||
# exposes whitespace (such as CRs, see below) and non-printing characters
|
||||
</code>
|
||||
|
||||
===== Use shell debug output =====
|
||||
|
||||
There are two useful debug outputs for that task (both are written to ''stderr''):
|
||||
|
||||
* ''set -v'' mode (''set -o verbose'')
|
||||
* print commands to be executed to ''stderr'' as if they were read from input (script file or keyboard)
|
||||
* print everything **before** any ([[syntax:expansion:intro | substitution and expansion]], ...) is applied
|
||||
* ''set -x'' mode (''set -o xtrace'')
|
||||
* 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 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.
|
||||
|
||||
**__Hint:__** These modes can be entered when calling Bash:
|
||||
* from commandline: ''bash -vx ./myscript''
|
||||
* from shebang (OS dependant): ''#!/bin/bash -vx''
|
||||
|
||||
|
||||
==== Simple example of how to interpret xtrace output ====
|
||||
|
||||
Here's a simple command (a string comparison using the [[commands:classictest | classic test command]]) executed while in ''set -x'' mode:
|
||||
<code>
|
||||
set -x
|
||||
foo="bar baz"
|
||||
[ $foo = test ]
|
||||
</code>
|
||||
|
||||
That fails. Why? Let's see the ''xtrace'' output:
|
||||
|
||||
<code>
|
||||
+ '[' bar baz = test ']'
|
||||
</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...
|
||||
<code>
|
||||
# next try
|
||||
[ "$foo" = test ]
|
||||
</code>
|
||||
|
||||
''xtrace'' now gives
|
||||
<code>
|
||||
+ '[' 'bar baz' = test ']'
|
||||
^ ^
|
||||
word markers!
|
||||
</code>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
==== Making xtrace more useful ====
|
||||
|
||||
(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:
|
||||
<code>
|
||||
export PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'
|
||||
</code>
|
||||
**Be sure to use single quotes here!**
|
||||
|
||||
The output would look like this when you trace code //outside a function//:
|
||||
<code>
|
||||
+(somefile.bash:412): echo 'Hello world'
|
||||
</code>
|
||||
...and like this when you trace code //inside a function//:
|
||||
<code>
|
||||
+(somefile.bash:412): myfunc(): echo 'Hello world'
|
||||
</code>
|
||||
|
||||
That helps a lot when the script is long, or when the main script sources many other files.
|
||||
|
||||
=== Set flag variables with descriptive words ===
|
||||
If you test variables that flag the state of options, such as with ''%%if [[ -n $option ]];%%'', consider using descriptive words rather than short codes, such as 0, 1, Y, N, because xtrace will show ''%%[[ -n word ]]%%'' rather than ''%%[[ -n 1 ]]%%'' when the option is set.
|
||||
===== Debugging commands depending on a set variable =====
|
||||
|
||||
For general debugging purposes you can also define a function and a variable to use:
|
||||
<code>
|
||||
debugme() {
|
||||
[[ $script_debug = 1 ]] && "$@" || :
|
||||
# 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
|
||||
# "false" return code (for example the script's exit code if this function is used
|
||||
# as the very last command in the script)
|
||||
}
|
||||
</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:
|
||||
<code>
|
||||
script_debug=1
|
||||
# to turn it off, set script_debug=0
|
||||
|
||||
debugme logger "Sorting the database"
|
||||
database_sort
|
||||
debugme logger "Finished sorting the database, exit code $?"
|
||||
</code>
|
||||
|
||||
Of course this can be used to execute something other than echo during debugging:
|
||||
<code>
|
||||
debugme set -x
|
||||
# ... some code ...
|
||||
debugme set +x
|
||||
</code>
|
||||
|
||||
===== Dry-run STDIN driven commands =====
|
||||
|
||||
Imagine you have a script that runs FTP commands using the standard FTP client:
|
||||
|
||||
<code bash>
|
||||
ftp user@host <<FTP
|
||||
cd /data
|
||||
get current.log
|
||||
dele current.log
|
||||
FTP
|
||||
</code>
|
||||
|
||||
A method to dry-run this with debug output is:
|
||||
<code bash>
|
||||
if [[ $DRY_RUN = yes ]]; then
|
||||
sed 's/^/DRY_RUN FTP: /'
|
||||
else
|
||||
ftp user@host
|
||||
fi <<FTP
|
||||
cd /data
|
||||
get current.log
|
||||
dele current.log
|
||||
FTP
|
||||
</code>
|
||||
|
||||
This can be wrapped in a shell function for more readable code.
|
||||
|
||||
===== Common error messages =====
|
||||
|
||||
|
||||
|
||||
==== Unexpected end of file ====
|
||||
<code>
|
||||
script.sh: line 100: syntax error: unexpected end of file
|
||||
</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]]:
|
||||
* did you close your ''do'' with a ''done''?
|
||||
* did you close your ''if'' with a ''fi''?
|
||||
* did you close your ''case'' with a ''esac''?
|
||||
* did you close your ''{'' with a ''}''?
|
||||
* did you close your ''('' with a '')''?
|
||||
|
||||
|
||||
**__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>')''
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
==== Unexpected end of file while looking for matching ... ====
|
||||
|
||||
<code>
|
||||
script.sh: line 50: unexpected EOF while looking for matching `"'
|
||||
script.sh: line 100: syntax error: unexpected end of file
|
||||
</code>
|
||||
|
||||
This one indicates the double-quote opened in line 50 does not have a matching closing quote.
|
||||
|
||||
These //unmatched errors// occur with:
|
||||
* double-quote pairs
|
||||
* single-quote pairs (also ''<nowiki>$'string'</nowiki>''!)
|
||||
* missing a closing ''}'' with [[syntax:pe | parameter expansion syntax]]
|
||||
|
||||
|
||||
|
||||
==== Too many arguments ====
|
||||
|
||||
<code>
|
||||
bash: test: too many arguments
|
||||
</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.
|
||||
|
||||
|
||||
==== !": event not found ====
|
||||
|
||||
<code>
|
||||
$ echo "Hello world!"
|
||||
bash: !": event not found
|
||||
</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:
|
||||
<code>
|
||||
set +H
|
||||
# or
|
||||
set +o histexpand
|
||||
</code>
|
||||
|
||||
==== syntax error near unexpected token `(' ====
|
||||
|
||||
When this happens during a script **function definition** or on the commandline, e.g.
|
||||
|
||||
<code>
|
||||
$ foo () { echo "Hello world"; }
|
||||
bash: syntax error near unexpected token `('
|
||||
</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.
|
||||
|
||||
===== The CRLF issue =====
|
||||
|
||||
==== What is the CRLF issue? ====
|
||||
|
||||
There's a big difference in the way that UNIX(r) and Microsoft(r) (and possibly others) handle the **line endings** of plain text files. The difference lies in the use of the CR (Carriage Return) and LF (Line Feed) characters.
|
||||
|
||||
* MSDOS uses: ''\r\n'' (ASCII ''CR'' #13 ''^M'', ASCII LF #10)
|
||||
* UNIX(r) uses: ''\n'' (ASCII ''LF'' #10)
|
||||
|
||||
Keep in mind your script is a **plain text file**, and the ''CR'' character means nothing special to UNIX(r) - it is treated like any other character. If it's printed to your terminal, a carriage return will effectively place the cursor at the beginning of the //current// line. This can cause much confusion and many headaches, since lines containing CRs are not what they appear to be when printed. In summary, CRs are a pain.
|
||||
|
||||
==== How did a CR end up in my file? ====
|
||||
|
||||
Some possible sources of CRs:
|
||||
* a DOS/Windows text editor
|
||||
* a UNIX(r) text editor that is "too smart" when determining the file content type (and thinks "//it's a DOS text file//")
|
||||
* a direct copy and paste from certain webpages (some pastebins are known for this)
|
||||
|
||||
|
||||
==== Why do CRs hurt? ====
|
||||
|
||||
CRs can be a nuisance in various ways. They are especially bad when present in the shebang/interpreter specified with ''#!'' in the very first line of a script. Consider the following script, written with a Windows(r) text editor (''^M'' is a symbolic representation of the ''CR'' carriage return character!):
|
||||
<code>
|
||||
#!/bin/bash^M
|
||||
^M
|
||||
echo "Hello world"^M
|
||||
...
|
||||
</code>
|
||||
|
||||
Here's what happens because of the ''#!/bin/bash^M'' in our shebang:
|
||||
* the file ''/bin/bash^M'' doesn't exist (hopefully)
|
||||
* So Bash prints an error message which (depending on the terminal, the Bash version, or custom patches!) may or may not expose the problem.
|
||||
* the script can't be executed
|
||||
|
||||
The error message can vary. If you're lucky, you'll get:
|
||||
<code>
|
||||
bash: ./testing.sh: /bin/bash^M: bad interpreter: No such file or directory
|
||||
</code>
|
||||
which alerts you to the CR. But you may also get the following:
|
||||
<code>
|
||||
: bad interpreter: No such file or directory
|
||||
</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!
|
||||
|
||||
<note warning>
|
||||
It's easy to imagine the ''^M'' is bad in other places too. If you get weird and illogical messages from your script, rule out the possibility that''^M'' is involved. Find and eliminate it!
|
||||
</note>
|
||||
|
||||
==== How can I find and eliminate them? ====
|
||||
|
||||
**To display** CRs (these are only a few examples)
|
||||
* in VI/VIM: '':set list''
|
||||
* with ''cat(1)'': ''cat -v FILE''
|
||||
|
||||
**To eliminate** them (only a few examples)
|
||||
* blindly with ''tr(1)'': ''tr -d <nowiki>'\r'</nowiki> <FILE >FILE.new''
|
||||
* controlled with ''recode(1)'': ''recode MSDOS..latin1 FILE''
|
||||
* controlled with ''dos2unix(1)'': ''dos2unix FILE''
|
||||
|
||||
===== See also =====
|
||||
* [[commands:builtin:set | the set builtin command]] (for ''-v'' and ''-x'')
|
||||
|
||||
|
||||
FIXME
|
||||
* DEBUG trap
|
||||
* BASH Debugger http://bashdb.sourceforge.net/
|
327
original_source/scripting/f_color_def.txt
Normal file
327
original_source/scripting/f_color_def.txt
Normal file
File diff suppressed because one or more lines are too long
327
original_source/scripting/fc.txt
Normal file
327
original_source/scripting/fc.txt
Normal file
File diff suppressed because one or more lines are too long
327
original_source/scripting/lc.txt
Normal file
327
original_source/scripting/lc.txt
Normal file
File diff suppressed because one or more lines are too long
327
original_source/scripting/lnc.txt
Normal file
327
original_source/scripting/lnc.txt
Normal file
File diff suppressed because one or more lines are too long
242
original_source/scripting/newbie_traps.txt
Normal file
242
original_source/scripting/newbie_traps.txt
Normal file
@ -0,0 +1,242 @@
|
||||
====== Beginner Mistakes ======
|
||||
|
||||
{{keywords>bash shell scripting pitfalls traps beginners}}
|
||||
|
||||
Here are some typical traps:
|
||||
|
||||
===== Script execution =====
|
||||
|
||||
==== Your perfect Bash script executes with syntax errors ====
|
||||
|
||||
If you write Bash scripts with Bash specific syntax and features, run them with __Bash__, and run them with Bash in __native mode__.
|
||||
|
||||
**Wrong**:
|
||||
* no shebang
|
||||
* the interpreter used depends on the OS implementation and current shell
|
||||
* **can** be run by calling bash with the script name as an argument, e.g. ''bash myscript''
|
||||
* ''#!/bin/sh'' shebang
|
||||
* depends on what ''/bin/sh'' actually is, for a Bash it means compatiblity mode, **not** native mode
|
||||
|
||||
See also:
|
||||
* [[scripting:bashbehaviour#sh_mode | Bash startup mode: SH mode]]
|
||||
* [[scripting:bashbehaviour#posix_run_mode | Bash run mode: POSIX mode]]
|
||||
|
||||
==== Your script named "test" doesn't execute ====
|
||||
|
||||
Give it another name. The executable ''test'' already exists.
|
||||
|
||||
In Bash it's a builtin. With other shells, it might be an executable file. Either way, it's bad name choice!
|
||||
|
||||
Workaround: You can call it using the pathname:
|
||||
<code>
|
||||
/home/user/bin/test
|
||||
</code>
|
||||
|
||||
===== Globbing =====
|
||||
|
||||
==== Brace expansion is not globbing ====
|
||||
|
||||
The following command line is not related to globbing (filename expansion):
|
||||
<code>
|
||||
# YOU EXPECT
|
||||
# -i1.vob -i2.vob -i3.vob ....
|
||||
|
||||
echo -i{*.vob,}
|
||||
|
||||
# YOU GET
|
||||
# -i*.vob -i
|
||||
</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.
|
||||
|
||||
Please see:
|
||||
* [[syntax:expansion:brace]]
|
||||
|
||||
|
||||
===== Test-command =====
|
||||
|
||||
* ''if [ $foo ] ...''
|
||||
* ''if [-d $dir] ...''
|
||||
* ...
|
||||
Please see:
|
||||
* [[commands:classictest#pitfalls_summarized|The classic test command - pitfalls]]
|
||||
|
||||
===== Variables =====
|
||||
|
||||
==== Setting variables ====
|
||||
|
||||
=== The Dollar-Sign ===
|
||||
|
||||
There is no ''$'' (dollar-sign) when you reference the **name** of a variable! Bash is not PHP!
|
||||
<code>
|
||||
# THIS IS WRONG!
|
||||
$myvar="Hello world!"
|
||||
</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...
|
||||
<code>
|
||||
="Hello world!"
|
||||
</code>
|
||||
...which **definitely is wrong**!
|
||||
|
||||
When you need the **name** of a variable, you write **only the name**, for example
|
||||
* (as shown above) to set variables: ''picture=/usr/share/images/foo.png''
|
||||
* to name variables to be used by the ''read'' builtin command: ''read picture''
|
||||
* to name variables to be unset: ''unset picture''
|
||||
|
||||
When you need the **content** of a variable, you prefix its name with **a dollar-sign**, like
|
||||
* echo "The used picture is: $picture"
|
||||
|
||||
=== Whitespace ===
|
||||
|
||||
Putting spaces on either or both sides of the equal-sign (''='') when assigning a value to a variable **will** fail.
|
||||
|
||||
<code>
|
||||
# INCORRECT 1
|
||||
example = Hello
|
||||
|
||||
# INCORRECT 2
|
||||
example= Hello
|
||||
|
||||
# INCORRECT 3
|
||||
example =Hello
|
||||
</code>
|
||||
|
||||
The only valid form is **no spaces between the variable name and assigned value**:
|
||||
<code>
|
||||
# CORRECT 1
|
||||
example=Hello
|
||||
|
||||
# CORRECT 2
|
||||
example=" Hello"
|
||||
</code>
|
||||
|
||||
==== Expanding (using) variables ====
|
||||
|
||||
A typical beginner's trap is quoting.
|
||||
|
||||
As noted above, when you want to **expand** a variable i.e. "get the content", the variable name needs to be prefixed with a dollar-sign. But, since Bash knows various ways to quote and does word-splitting, the result isn't always the same.
|
||||
|
||||
Let's define an example variable containing text with spaces:
|
||||
<code>
|
||||
example="Hello world"
|
||||
</code>
|
||||
|
||||
^Used form^result^number of words^
|
||||
|''$example'' |''Hello world''|2|
|
||||
|''"$example"'' |''Hello world''|1|
|
||||
|''\$example'' |''$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''):
|
||||
<code>
|
||||
# WRONG!
|
||||
echo "The first character of PATH is ${$PATH:0:1}"
|
||||
|
||||
# CORRECT
|
||||
echo "The first character of PATH is ${PATH:0:1}"
|
||||
</code>
|
||||
|
||||
Note that if you are using variables in [[syntax:arith_expr | arithmetic expressions]], then the bare **name** is allowed:
|
||||
<code>
|
||||
((a=$a+7)) # Add 7 to a
|
||||
((a = a + 7)) # Add 7 to a. Identical to the previous command.
|
||||
((a += 7)) # Add 7 to a. Identical to the previous command.
|
||||
|
||||
a=$((a+7)) # POSIX-compatible version of previous code.
|
||||
</code>
|
||||
|
||||
|
||||
Please see:
|
||||
* [[syntax:words]]
|
||||
* [[syntax:quoting]]
|
||||
* [[syntax:expansion:wordsplit]]
|
||||
* [[syntax:pe]]
|
||||
|
||||
==== Exporting ====
|
||||
|
||||
Exporting a variable means giving **newly created** (child-)processes a copy of that variable. It does **not** copy a variable created in a child process back to the parent process. The following example does **not** work, since the variable ''hello'' is set in a child process (the process you execute to start that script ''./script.sh''):
|
||||
|
||||
<code>
|
||||
$ cat script.sh
|
||||
export hello=world
|
||||
|
||||
$ ./script.sh
|
||||
$ echo $hello
|
||||
$
|
||||
</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:
|
||||
<code>
|
||||
$ source ./script.sh
|
||||
$ echo $hello
|
||||
world
|
||||
$
|
||||
</code>
|
||||
In this case, the export command is of no use.
|
||||
|
||||
Please see:
|
||||
* [[scripting:processtree]]
|
||||
|
||||
===== Exit codes =====
|
||||
==== Reacting to exit codes ====
|
||||
|
||||
If you just want to react to an exit code, regardless of its specific value, you **don't need** to use ''$?'' in a test command like this:
|
||||
|
||||
<code bash>
|
||||
grep ^root: /etc/passwd >/dev/null 2>&1
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "root was not found - check the pub at the corner"
|
||||
fi
|
||||
</code>
|
||||
|
||||
This can be simplified to:
|
||||
<code bash>
|
||||
if ! grep ^root: /etc/passwd >/dev/null 2>&1; then
|
||||
echo "root was not found - check the pub at the corner"
|
||||
fi
|
||||
</code>
|
||||
|
||||
Or, simpler yet:
|
||||
<code bash>
|
||||
grep ^root: /etc/passwd >/dev/null 2>&1 || echo "root was not found - check the pub at the corner"
|
||||
</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 ''$?''.
|
||||
|
||||
See also:
|
||||
* [[scripting:basics#exit_codes | Exit codes]]
|
||||
|
||||
==== Output vs. Return Value ====
|
||||
|
||||
It's important to remember the different ways to run a child command, and whether you want the output, the return value, or neither.
|
||||
|
||||
When you want to run a command (or a pipeline) and save (or print) the **output**, whether as a string or an array, you use Bash's ''$(command)'' syntax:
|
||||
<code>
|
||||
$(ls -l /tmp)
|
||||
newvariable=$(printf "foo")
|
||||
</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:
|
||||
<code>
|
||||
if grep someuser /etc/passwd ; then
|
||||
# do something
|
||||
fi
|
||||
|
||||
if ( w | grep someuser | grep sqlplus ) ; then
|
||||
# someuser is logged in and running sqlplus
|
||||
fi
|
||||
</code>
|
||||
|
||||
Make sure you're using the form you intended:
|
||||
<code>
|
||||
# WRONG!
|
||||
if $(grep ERROR /var/log/messages) ; then
|
||||
# send alerts
|
||||
fi
|
||||
</code>
|
||||
|
||||
Please see:
|
||||
* [[syntax:ccmd:intro]]
|
||||
* [[syntax:expansion:cmdsubst]]
|
||||
* [[syntax:ccmd:grouping_subshell]]
|
145
original_source/scripting/nonportable.txt
Normal file
145
original_source/scripting/nonportable.txt
Normal file
@ -0,0 +1,145 @@
|
||||
====== Portability talk ======
|
||||
|
||||
{{keywords>bash shell scripting portability POSIX portable}}
|
||||
|
||||
The script programming language of BASH is based on the Bourne Shell syntax, with some extensions and derivations.
|
||||
|
||||
If scripts need to be portable, some of the BASH-specific syntax elements should be avoided. Others should be avoided for all scripts, e.g. if there is a corresponding POSIX(r)-compatible syntax (see [[scripting:obsolete]]).
|
||||
|
||||
Some syntax elements have a BASH-specific, and a portable(("portable" doesn't necessarily mean it's POSIX, it can also mean it's "widely used and accepted", and thus maybe more portable than POSIX(r))) pendant. In these cases the portable syntax should be preferred.
|
||||
|
||||
^ construct ^ portable equivalent ^ Description ^ Portability ^
|
||||
|''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!**|
|
||||
|''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, ...|
|
||||
|''<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|
|
||||
|''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 |
|
||||
|
||||
===== Portability rationale =====
|
||||
|
||||
Here is some assorted portability information. Take it as a small guide to make your scripts a bit more portable. It's not complete (it never will be!) and it's not very detailed (e.g. you won't find information about how which shell technically forks off which subshell). It's just an assorted small set of portability guidelines. //-Thebonsai//
|
||||
|
||||
FIXME UNIX shell gurus out there, please be patient with a newbie like me and give comments and hints instead of flames.
|
||||
|
||||
==== Environment (exported) variables ====
|
||||
|
||||
When a new value is assigned to an **existing environment variable**, there are two possibilities:
|
||||
|
||||
The //new value// is seen by subsequent programs
|
||||
* without any special action (e.g. Bash)
|
||||
* only after an explicit export with ''export VARIABLE'' (e.g. Sun's ''/bin/sh'')
|
||||
|
||||
Since an extra ''export'' doesn't hurt, the safest and most portable way is to always (re-)export a changed variable if you want it to be seen by subsequent processes.
|
||||
|
||||
==== Arithmetics ====
|
||||
|
||||
Bash has a special compound command to do arithmetic without expansion. However, POSIX has no such command. In the table at the top, there's the ''<nowiki>: $((MATH))</nowiki>'' construct mentioned as possible alternative. Regarding the exit code, a 100% equivalent construct would be:
|
||||
<code>
|
||||
# Bash (or others) compound command
|
||||
if ((MATH)); then
|
||||
...
|
||||
|
||||
# portable equivalent command
|
||||
if [ "$((MATH))" -ne 0 ]; then
|
||||
...
|
||||
</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//.
|
||||
|
||||
==== echo command ====
|
||||
|
||||
The overall problem with ''echo'' is, that there are 2 (maybe more) mainstream flavours around. The only case where you may safely use an ''echo'' on all systems is: Echoing non-variable arguments that don't start with a ''-'' (dash) and don't contain a ''\'' (backslash).
|
||||
|
||||
Why? (list of known behaviours)
|
||||
* may or may not automatically interpret backslash escpape codes in the strings
|
||||
* may or may not automatically interpret switches (like ''-n'')
|
||||
* may or may not ignore "end of options" tag (''<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)
|
||||
|
||||
For these, and possibly other, reasons, POSIX (SUS) standardized the existance of [[commands:builtin:printf | the ''printf'' command]].
|
||||
|
||||
|
||||
==== Parameter expansions ====
|
||||
|
||||
* ''${var:x:x}'' is 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)
|
||||
|
||||
==== Special variables ====
|
||||
|
||||
=== 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:
|
||||
<code>
|
||||
pwd -P > dev/null
|
||||
</code>
|
||||
|
||||
=== 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.
|
||||
|
||||
<code>
|
||||
# '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)} }')
|
||||
|
||||
# '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)} }')
|
||||
</code>
|
||||
//Yes, I'm not an ''awk'' expert, so please correct it, rather than complaining about possible stupid code!//
|
||||
|
||||
<code>
|
||||
# Well, seeing how this //is// BASH-hackers.org I kinda missed the bash way of doing the above ;-)
|
||||
# print a number between 0 and 500 :-)
|
||||
printf $(( 500 * RANDOM / 32767 ))
|
||||
|
||||
# Or print 30 random numbers between 0 and 10 ;)
|
||||
X=0; while (( X++ < 30 )); do echo $(( 10 * RANDOM / 32767 )); done
|
||||
</code>
|
||||
|
||||
|
||||
=== SECONDS ===
|
||||
|
||||
[[syntax:shellvars#SECONDS|SECONDS]] is KSH/ZSH/Bash specific. Avoid it. Find another method.
|
||||
|
||||
|
||||
|
||||
==== Check for a command in PATH ====
|
||||
|
||||
The [[syntax:shellvars#PATH|PATH]] variable is a colon-delimited list of directory names, so it's basically possible to run a loop and check every ''PATH'' component for the command you're looking for and for executability.
|
||||
|
||||
However, this method doesn't look nice. There are other ways of doing this, using commands that are //not directly// related to this task.
|
||||
|
||||
=== hash ===
|
||||
The ''hash'' command is used to make the shell store the full pathname of a command in a lookup-table (to avoid re-scanning the ''PATH'' on every command execution attempt). Since it has to do a ''PATH'' search, it can be used for this check.
|
||||
|
||||
For example, to check if the command ''ls'' is available in a location accessible by ''PATH'':
|
||||
<code>
|
||||
if hash ls >/dev/null 2>&1; then
|
||||
echo "ls is available"
|
||||
fi
|
||||
</code>
|
||||
|
||||
Somewhat of a mass-check:
|
||||
<code>
|
||||
for name in ls grep sed awk; do
|
||||
if ! hash "$name" >/dev/null 2>&1; then
|
||||
echo "FAIL: Missing command '$name'"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
</code>
|
||||
|
||||
Here (bash 3), ''hash'' also respects builtin commands. I don't know if this works everywhere, but it seems logical.
|
||||
|
||||
=== command ===
|
||||
The ''command'' command is used to explicitly call an external command, rather than a builtin with the same name. For exactly this reason, it has to do a ''PATH'' search, and can be used for this check.
|
||||
|
||||
For example, to check if the command ''sed'' is available in a location accessible by ''PATH'':
|
||||
<code>
|
||||
if command -v sed >/dev/null 2>&1; then
|
||||
echo "sed is available"
|
||||
fi
|
||||
</code>
|
42
original_source/scripting/obsolete.txt
Normal file
42
original_source/scripting/obsolete.txt
Normal file
@ -0,0 +1,42 @@
|
||||
====== Obsolete and deprecated syntax ======
|
||||
|
||||
{{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 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 ^
|
||||
|''&>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
|
||||
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]].|
|
||||
|''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.|
|
||||
|''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.
|
||||
|
||||
^ 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!]]// |
|
||||
|''`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 [[ ?]]// |
|
||||
|''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''. |
|
||||
|''${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> |
|
||||
|
||||
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 ^
|
||||
|''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. |
|
||||
|''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]]. |
|
||||
|
||||
===== See also =====
|
||||
* [[scripting:nonportable | Non-portable syntax and command uses]]
|
||||
* [[scripting:bashchanges]]
|
||||
* [[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/BashPitfalls]]
|
405
original_source/scripting/posparams.txt
Normal file
405
original_source/scripting/posparams.txt
Normal file
@ -0,0 +1,405 @@
|
||||
====== Handling positional parameters ======
|
||||
|
||||
{{keywords>bash shell scripting arguments positional parameters options}}
|
||||
|
||||
|
||||
===== Intro =====
|
||||
|
||||
The day will come when you want to give arguments to your scripts. These arguments are known as **positional parameters**. Some relevant special parameters are described below:
|
||||
^Parameter(s)^Description^
|
||||
|''$0''|the first positional parameter, equivalent to ''argv[0]'' in C, see [[scripting:posparams#the_first_argument | the first argument]]|
|
||||
|''$FUNCNAME''| the function name (__**attention**__: inside a function, ''$0'' is still the ''$0'' of the shell, **not** the function name)|
|
||||
|''$1 ... $9''|the argument list elements from 1 to 9|
|
||||
|''${10} ... ${N}''|the argument list elements beyond 9 (note the [[syntax:pe | parameter expansion]] syntax!)|
|
||||
|''$*''|all positional parameters except ''$0'', see [[scripting:posparams#mass_usage | mass usage]]|
|
||||
|''$@''|all positional parameters except ''$0'', see [[scripting:posparams#mass_usage | mass usage]]|
|
||||
|''$#''|the number of arguments, not counting ''$0''|
|
||||
|
||||
|
||||
These positional parameters reflect exactly what was given to the
|
||||
script when it was called.
|
||||
|
||||
Option-switch parsing (e.g. ''-h'' for displaying help) is not performed at
|
||||
this point.
|
||||
|
||||
See also [[dict:terms:parameter | the dictionary entry for "parameter"]].
|
||||
|
||||
|
||||
===== The first argument =====
|
||||
|
||||
The very first argument you can access is referenced as ''$0''. It
|
||||
is usually set to the script's name exactly as called, and it's
|
||||
set on shell initialization:
|
||||
|
||||
__Testscript__ - it just echos ''$0'':
|
||||
<code>
|
||||
#!/bin/bash
|
||||
echo "$0"
|
||||
</code>
|
||||
You see, ''$0'' is always set to the name the script is called with (''>'' is the prompt...):
|
||||
<code>
|
||||
> ./testscript
|
||||
./testscript
|
||||
</code>
|
||||
<code>
|
||||
> /usr/bin/testscript
|
||||
/usr/bin/testscript
|
||||
</code>
|
||||
|
||||
However, this isn't true for login shells:
|
||||
<code>
|
||||
> echo "$0"
|
||||
-bash
|
||||
</code>
|
||||
|
||||
In other terms, ''$0'' is not a positional parameter, it's a
|
||||
special parameter independent from the positional parameter list. It
|
||||
can be set to anything. In the **ideal** case it's the pathname
|
||||
of the script, but since this gets set on invocation, the invoking
|
||||
program can easily influence it (the ''login'' program does that for
|
||||
login shells, by prefixing a dash, for example).
|
||||
|
||||
Inside a function, ''$0'' still behaves as described above. To
|
||||
get the function name, use ''$FUNCNAME''.
|
||||
|
||||
===== Shifting =====
|
||||
|
||||
The builtin command ''shift'' is used to change the positional parameter values:
|
||||
* ''$1'' will be discarded
|
||||
* ''$2'' will become ''$1''
|
||||
* ''$3'' will become ''$2''
|
||||
* ...
|
||||
* in general: ''$N'' will become ''$N-1''
|
||||
|
||||
The command can take a number as argument: Number of positions to shift.
|
||||
e.g. ''shift 4'' shifts ''$5'' to ''$1''.
|
||||
|
||||
===== Using them =====
|
||||
|
||||
Enough theory, you want to access your script-arguments. Well, here we go.
|
||||
|
||||
==== One by one ====
|
||||
|
||||
One way is to access specific parameters:
|
||||
<code>
|
||||
#!/bin/bash
|
||||
echo "Total number of arguments: $#"
|
||||
echo "Argument 1: $1"
|
||||
echo "Argument 2: $2"
|
||||
echo "Argument 3: $3"
|
||||
echo "Argument 4: $4"
|
||||
echo "Argument 5: $5"
|
||||
</code>
|
||||
|
||||
While useful in another situation, this way is lacks flexibility.
|
||||
The maximum number of arguments is a fixedvalue
|
||||
- which is a bad idea if you write a script that takes many filenames
|
||||
as arguments.
|
||||
|
||||
=> forget that one
|
||||
|
||||
==== Loops ====
|
||||
|
||||
There are several ways to loop through the positional parameters.
|
||||
|
||||
----
|
||||
|
||||
You can code a [[syntax:ccmd:c_for | C-style for-loop]] using ''$#''
|
||||
as the end value. On every iteration, the ''shift''-command is used to
|
||||
shift the argument list:
|
||||
|
||||
<code>
|
||||
numargs=$#
|
||||
for ((i=1 ; i <= numargs ; i++))
|
||||
do
|
||||
echo "$1"
|
||||
shift
|
||||
done
|
||||
</code>
|
||||
|
||||
Not very stylish, but usable. The ''numargs'' variable is used
|
||||
to store the initial value of ''$#'' because the shift command
|
||||
will change it as the script runs.
|
||||
|
||||
----
|
||||
|
||||
Another way to iterate one argument at a time is the ''for'' loop
|
||||
without a given wordlist. The loop uses the positional parameters as a wordlist:
|
||||
|
||||
<code>
|
||||
for arg
|
||||
do
|
||||
echo "$arg"
|
||||
done
|
||||
</code>
|
||||
__Advantage:__ The positional parameters will be preserved
|
||||
|
||||
----
|
||||
|
||||
The next method is similar to the first example (the ''for'' loop), but
|
||||
it doesn't test for reaching ''$#''. It shifts and checks if ''$1''
|
||||
still expands to something, using the [[commands:classictest | test command]]:
|
||||
|
||||
<code>
|
||||
while [ "$1" ]
|
||||
do
|
||||
echo "$1"
|
||||
shift
|
||||
done
|
||||
</code>
|
||||
|
||||
Looks nice, but has the disadvantage of stopping when ''$1'' is empty
|
||||
(null-string). Let's modify it to run as long as ''$1'' is defined
|
||||
(but may be null), using [[syntax:pe#use_an_alternate_value | parameter expansion for an alternate value]]:
|
||||
|
||||
<code>
|
||||
while [ "${1+defined}" ]; do
|
||||
echo "$1"
|
||||
shift
|
||||
done
|
||||
</code>
|
||||
|
||||
==== Getopts ====
|
||||
|
||||
There is a [[howto:getopts_tutorial|small tutorial dedicated to ''getopts'']] (//under construction//).
|
||||
|
||||
===== Mass usage =====
|
||||
|
||||
==== All Positional Parameters ====
|
||||
|
||||
Sometimes it's necessary to just "relay" or "pass" given
|
||||
arguments to another program. It's very inefficient to do that in one
|
||||
of these loops, as you will destroy integrity, most likely
|
||||
(spaces!).
|
||||
|
||||
The shell developers created ''$*'' and ''$@'' for this purpose.
|
||||
|
||||
As overview:
|
||||
|
||||
^Syntax ^Effective result ^
|
||||
| ''$*'' | ''$1 $2 $3 ... ${N}'' |
|
||||
| ''$@'' | ''$1 $2 $3 ... ${N}'' |
|
||||
| ''"$*"'' | ''"$1c$2c$3c...c${N}"'' |
|
||||
| ''"$@"'' | ''"$1" "$2" "$3" ... "${N}"'' |
|
||||
|
||||
Without being quoted (double quotes), both have the same
|
||||
effect: All positional parameters from ''$1'' to the last one used are
|
||||
expanded without any special handling.
|
||||
|
||||
When the ''$*'' special parameter is double quoted, it expands to the
|
||||
equivalent of: ''"$1c$2c$3c$4c........$N"'', where 'c' is the first
|
||||
character of ''IFS''.
|
||||
|
||||
But when the ''$@'' special parameter is used inside double quotes, it
|
||||
expands to the equivanent of...
|
||||
|
||||
''"$1" "$2" "$3" "$4" ..... "$N"''
|
||||
|
||||
...which **reflects all positional parameters as they were
|
||||
set initially** and passed to the script or function. If you want
|
||||
to re-use your positional parameters to **call another program** (for
|
||||
example in a wrapper-script), then this is the choice for you, use
|
||||
double quoted ''"$@"''.
|
||||
|
||||
Well, let's just say: **You almost always want a quoted ''"$@"''!**
|
||||
|
||||
==== Range Of Positional Parameters ====
|
||||
|
||||
Another way to mass expand the positional parameters is similar to
|
||||
what is possible for a range of characters using
|
||||
[[syntax:pe#substring_expansion | substring expansion]] on normal
|
||||
parameters and the mass expansion range of [[syntax:arrays | arrays]].
|
||||
|
||||
''${@:START:COUNT}''
|
||||
|
||||
''${*:START:COUNT}''
|
||||
|
||||
''"${@:START:COUNT}"''
|
||||
|
||||
''"${*:START:COUNT}"''
|
||||
|
||||
The rules for using ''@'' or ''*'' and quoting are the same as
|
||||
above. This will expand ''COUNT'' number of positional parameters
|
||||
beginning at ''START''. ''COUNT'' can be omitted (''${@:START}''), in
|
||||
which case, all positional parameters beginning at ''START'' are
|
||||
expanded.
|
||||
|
||||
If ''START'' is negative, the positional parameters are numbered in reverse
|
||||
starting with the last one.
|
||||
|
||||
''COUNT'' may not be negative, i.e. the element count may not be decremented.
|
||||
|
||||
__**Example:**__
|
||||
START at the last positional parameter:
|
||||
<code>
|
||||
echo "${@: -1}"
|
||||
</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''.
|
||||
|
||||
===== Setting Positional Parameters =====
|
||||
|
||||
Setting positional parameters with command line arguments,
|
||||
is not the only way to set them.
|
||||
The [[ commands:builtin:set | builtin command, set ]]
|
||||
may be used to "artificially" change the positional parameters from
|
||||
inside the script or function:
|
||||
|
||||
<code>
|
||||
set "This is" my new "set of" positional parameters
|
||||
|
||||
# RESULTS IN
|
||||
# $1: This is
|
||||
# $2: my
|
||||
# $3: new
|
||||
# $4: set of
|
||||
# $5: positional
|
||||
# $6: parameters
|
||||
</code>
|
||||
|
||||
It's wise to signal "end of options" when setting positional
|
||||
parameters this way. If not, the dashes might be interpreted as an option switch
|
||||
by ''set'' itself:
|
||||
|
||||
<code>
|
||||
# both ways work, but behave differently. See the article about the set command!
|
||||
set -- ...
|
||||
set - ...
|
||||
</code>
|
||||
|
||||
Alternately this will also preserve any verbose (-v) or tracing (-x) flags, which may otherwise be reset by ''set''
|
||||
<code>
|
||||
set -$- ...
|
||||
</code>
|
||||
|
||||
FIXME continue
|
||||
|
||||
===== Production examples =====
|
||||
|
||||
==== Using a while loop ====
|
||||
|
||||
To make your program accept options as standard command syntax:
|
||||
|
||||
''COMMAND [options] <params>'' # Like 'cat -A file.txt'
|
||||
|
||||
See simple option parsing code below. It's not that flexible. It
|
||||
doesn't auto-interpret combined options (-fu USER) but it works and is
|
||||
a good rudimentary way to parse your arguments.
|
||||
|
||||
<code>
|
||||
#!/bin/sh
|
||||
# Keeping options in alphabetical order makes it easy to add more.
|
||||
|
||||
while :
|
||||
do
|
||||
case "$1" in
|
||||
-f | --file)
|
||||
file="$2" # You may want to check validity of $2
|
||||
shift 2
|
||||
;;
|
||||
-h | --help)
|
||||
display_help # Call your function
|
||||
# no shifting needed here, we're done.
|
||||
exit 0
|
||||
;;
|
||||
-u | --user)
|
||||
username="$2" # You may want to check validity of $2
|
||||
shift 2
|
||||
;;
|
||||
-v | --verbose)
|
||||
# It's better to assign a string, than a number like "verbose=1"
|
||||
# because if you're debugging the script with "bash -x" code like this:
|
||||
#
|
||||
# if [ "$verbose" ] ...
|
||||
#
|
||||
# You will see:
|
||||
#
|
||||
# if [ "verbose" ] ...
|
||||
#
|
||||
# Instead of cryptic
|
||||
#
|
||||
# if [ "1" ] ...
|
||||
#
|
||||
verbose="verbose"
|
||||
shift
|
||||
;;
|
||||
--) # End of all options
|
||||
shift
|
||||
break;
|
||||
-*)
|
||||
echo "Error: Unknown option: $1" >&2
|
||||
exit 1
|
||||
;;
|
||||
*) # No more options
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# End of file
|
||||
</code>
|
||||
|
||||
==== Filter unwanted options with a wrapper script ====
|
||||
|
||||
This simple wrapper enables filtering unwanted options (here: ''-a''
|
||||
and ''--all'' for ''ls'') out of the command line. It reads the
|
||||
positional parameters and builds a filtered array consisting of them, then
|
||||
calls ''ls'' with the new option set. It also respects the ''--''
|
||||
as "end of options" for ''ls'' and doesn't change anything after it:
|
||||
|
||||
<code>
|
||||
#!/bin/bash
|
||||
|
||||
# simple ls(1) wrapper that doesn't allow the -a option
|
||||
|
||||
options=() # the buffer array for the parameters
|
||||
eoo=0 # end of options reached
|
||||
|
||||
while [[ $1 ]]
|
||||
do
|
||||
if ! ((eoo)); then
|
||||
case "$1" in
|
||||
-a)
|
||||
shift
|
||||
;;
|
||||
--all)
|
||||
shift
|
||||
;;
|
||||
-[^-]*a*|-a?*)
|
||||
options+=("${1//a}")
|
||||
shift
|
||||
;;
|
||||
--)
|
||||
eoo=1
|
||||
options+=("$1")
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
options+=("$1")
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
else
|
||||
options+=("$1")
|
||||
|
||||
# Another (worse) way of doing the same thing:
|
||||
# options=("${options[@]}" "$1")
|
||||
shift
|
||||
fi
|
||||
done
|
||||
|
||||
/bin/ls "${options[@]}"
|
||||
</code>
|
||||
|
||||
==== Using getopts ====
|
||||
|
||||
There is a [[howto:getopts_tutorial|small tutorial dedicated to ''getopts'']] (//under construction//).
|
||||
|
||||
===== See also =====
|
||||
|
||||
* Internal: [[howto:getopts_tutorial]]
|
||||
* Internal: [[syntax:ccmd:while_loop]]
|
||||
* Internal: [[syntax:ccmd:c_for]]
|
||||
* Internal: [[syntax:arrays]] (for equivalent syntax for mass-expansion)
|
||||
* Internal: [[syntax:pe#substring_expansion | Substring expansion on a parameter]] (for equivalent syntax for mass-expansion)
|
||||
* Dictionary, internal: [[dict:terms:parameter]]
|
129
original_source/scripting/processtree.txt
Normal file
129
original_source/scripting/processtree.txt
Normal file
@ -0,0 +1,129 @@
|
||||
====== Bash and the process tree ======
|
||||
|
||||
{{keywords>bash shell scripting processes pipes variables environment}}
|
||||
|
||||
===== The process tree =====
|
||||
|
||||
The processes in UNIX(r) are - unlike other systems - **organized as a tree**. Every process has a parent process that started, or is responsible, for it. Every process has its own **context memory** (Not the memory where the process stores its data, rather, the memory where data is stored that doesn't directly belong to the process, but is needed to run the process) i.e. __**The environment**__.
|
||||
|
||||
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.
|
||||
|
||||
**__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>
|
||||
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 =====
|
||||
|
||||
All the diagrams of the process tree use names like "''xterm''" or "''bash''", but that's just to make it easier to understand what's going on, it doesn't mean those processes are actually executed.
|
||||
|
||||
Let's take a short look at what happens when you "execute a program" from the Bash prompt, a program like "ls":
|
||||
|
||||
<code>
|
||||
$ ls
|
||||
</code>
|
||||
|
||||
Bash will now perform **two steps**:
|
||||
* It will make a copy of itself
|
||||
* The copy will replace itself with the "ls" program
|
||||
The copy of Bash will inherit the environment from the "main Bash" process: All environment variables will also be copied to the new process. This step is called **forking**.
|
||||
|
||||
For a short moment, you have a process tree that might look like this...
|
||||
<code>
|
||||
xterm ----- bash ----- bash(copy)
|
||||
</code>
|
||||
...and after the "second Bash" (the copy) replaces itself with the ''ls'' program (the copy execs it), it might look like
|
||||
<code>
|
||||
xterm ----- bash ----- ls
|
||||
</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'').
|
||||
|
||||
__**What is so important about it?**__ In our example, what the program ''ls'' does inside its own environment, it can't affect the environment of its parent process (in this case, ''bash''). The environment was copied when ls was executed. Nothing is "copied back" to the parent environment when ''ls'' terminates.
|
||||
|
||||
===== Bash playing with pipes =====
|
||||
|
||||
Pipes are a very powerful tool. You can connect the output of one process to the input of another process. We won't delve into piping at this point, we just want to see how it looks in the process tree. Again, we execute some commands, this time, we'll run ''ls'' and ''grep'':
|
||||
|
||||
<code>
|
||||
$ ls | grep myfile
|
||||
</code>
|
||||
|
||||
It results in a tree like this:
|
||||
<code>
|
||||
+-- ls
|
||||
xterm ----- bash --|
|
||||
+-- grep
|
||||
</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.
|
||||
|
||||
__**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:
|
||||
|
||||
<code>
|
||||
counter=0
|
||||
|
||||
cat /etc/passwd | while read; do ((counter++)); done
|
||||
echo "Lines: $counter"
|
||||
</code>
|
||||
|
||||
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:
|
||||
<code>
|
||||
+-- cat /etc/passwd
|
||||
xterm ----- bash --|
|
||||
+-- bash (while read; do ((counter++)); done)
|
||||
</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!
|
||||
|
||||
__**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:
|
||||
|
||||
<code>
|
||||
counter=0
|
||||
|
||||
while read; do ((counter++)); done </etc/passwd
|
||||
echo "Lines: $counter"
|
||||
</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.
|
||||
===== Actions that create a subshell =====
|
||||
|
||||
Bash creates **subshells** or **subprocesses** on various actions it performs:
|
||||
|
||||
==== Executing commands ====
|
||||
|
||||
As shown above, Bash will create subprocesses everytime it executes commands. That's nothing new.
|
||||
|
||||
But if your command is a subprocess that sets variables you want to use in your main script, that won't work.
|
||||
|
||||
For exactly this purpose, there's the ''source'' command (also: the //dot// ''.'' command). Source doesn't execute the script, it imports the other script's code into the current shell:
|
||||
<code>
|
||||
source ./myvariables.sh
|
||||
# equivalent to:
|
||||
. ./myvariables.sh
|
||||
</code>
|
||||
|
||||
==== Pipes ====
|
||||
|
||||
The last big section was about pipes, so no example here.
|
||||
==== Explicit subshell ====
|
||||
|
||||
If you group commands by enclosing them in parentheses, these commands are run inside a subshell:
|
||||
<code>(echo PASSWD follows; cat /etc/passwd; echo GROUP follows; cat /etc/group) >output.txt</code>
|
||||
==== 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:
|
||||
<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:
|
||||
<code>
|
||||
+-- cat /etc/passwd
|
||||
xterm ----- bash ----- bash (cmd. subst.) --|
|
||||
+-- wc -l
|
||||
</code>
|
||||
|
||||
FIXME to be continued
|
327
original_source/scripting/sc.txt
Normal file
327
original_source/scripting/sc.txt
Normal file
File diff suppressed because one or more lines are too long
327
original_source/scripting/start.txt
Normal file
327
original_source/scripting/start.txt
Normal file
File diff suppressed because one or more lines are too long
315
original_source/scripting/style.txt
Normal file
315
original_source/scripting/style.txt
Normal file
@ -0,0 +1,315 @@
|
||||
====== Scripting with style ======
|
||||
|
||||
FIXME continue
|
||||
|
||||
These are some coding guidelines that helped me to read and understand my own code over the years. They also will help to produce code that will be a bit more robust than "if something breaks, I know how to fix it".
|
||||
|
||||
This is not a bible, of course. But I have seen so much ugly and terrible code (not only in shell) during all the years, that I'm 100% convinced there needs to be //some// code layout and style. No matter which one you use, use it throughout your code (at least don't change it within the same shellscript file); don't change your code layout with your mood.
|
||||
|
||||
Some good code layout helps you to read your own code after a while. And of course it helps others to read the code.
|
||||
|
||||
===== Indentation guidelines =====
|
||||
|
||||
Indentation is nothing that technically influences a script, it's only for us humans.
|
||||
|
||||
I'm used to seeing/using indentation of //two space characters// (though many may prefer 4 spaces, see below in the discussion section):
|
||||
* it's easy and fast to type
|
||||
* it's not a hard-tab that's displayed differently in different environments
|
||||
* it's wide enough to give a visual break and small enough to not waste too much space on the line
|
||||
|
||||
Speaking of hard-tabs: Avoid them if possible. They only make trouble. I can imagine one case where they're useful: Indenting [[syntax:redirection#here_documents | here-documents]].
|
||||
|
||||
==== Breaking up lines ====
|
||||
|
||||
Whenever you need to break lines of long code, you should follow one of these two rules:
|
||||
|
||||
__**Indention using command width:**__
|
||||
<code>
|
||||
activate some_very_long_option \
|
||||
some_other_option
|
||||
</code>
|
||||
|
||||
__**Indention using two spaces:**__
|
||||
<code>
|
||||
activate some_very_long_option \
|
||||
some_other_option
|
||||
</code>
|
||||
|
||||
Personally, with some exceptions, I prefer the first form because it supports the visual impression of "these belong together".
|
||||
|
||||
==== Breaking compound commands ====
|
||||
|
||||
[[syntax:ccmd:intro | Compound commands]] form the structures that make a shell script different from a stupid enumeration of commands. Usually they contain a kind of "head" and a "body" that contains command lists. This type of compound command is relatively easy to indent.
|
||||
|
||||
I'm used to (not all points apply to all compound commands, just pick the basic idea):
|
||||
* put the introducing keyword and the initial command list or parameters on one line ("head")
|
||||
* put the "body-introducing" keyword on the same line
|
||||
* the command list of the "body" on separate lines, indented by two spaces
|
||||
* put the closing keyword on a separated line, indented like the initial introducing keyword
|
||||
|
||||
What?! Well, here again:
|
||||
== Symbolic ==
|
||||
<code>
|
||||
HEAD_KEYWORD parameters; BODY_BEGIN
|
||||
BODY_COMMANDS
|
||||
BODY_END
|
||||
</code>
|
||||
|
||||
== 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:
|
||||
|
||||
<code>
|
||||
if ...; then
|
||||
...
|
||||
elif ...; then
|
||||
...
|
||||
else
|
||||
...
|
||||
fi
|
||||
</code>
|
||||
|
||||
== for ==
|
||||
<code>
|
||||
for f in /etc/*; do
|
||||
...
|
||||
done
|
||||
</code>
|
||||
|
||||
== while/until ==
|
||||
<code>
|
||||
while [[ $answer != [YyNn] ]]; do
|
||||
...
|
||||
done
|
||||
</code>
|
||||
|
||||
== The case construct ==
|
||||
|
||||
The ''case'' construct might need a bit more discussion here, since its structure is a bit more complex.
|
||||
|
||||
In general, every new "layer" gets a new indentation level:
|
||||
<code>
|
||||
case $input in
|
||||
hello)
|
||||
echo "You said hello"
|
||||
;;
|
||||
bye)
|
||||
echo "You said bye"
|
||||
if foo; then
|
||||
bar
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo "You said something weird..."
|
||||
;;
|
||||
esac
|
||||
</code>
|
||||
|
||||
Some notes:
|
||||
* if not 100% needed, the optional left parenthesis on the pattern is not used
|
||||
* the patterns (''hello)'') and the corresponding action terminator ('';;'') are indented at the same level
|
||||
* the action command lists are indented one more level (and continue to have their own indentation, if needed)
|
||||
* though optional, the very last action terminator is given
|
||||
|
||||
===== Syntax and coding guidelines =====
|
||||
|
||||
==== Cryptic constructs ====
|
||||
|
||||
Cryptic constructs, we all know them, we all love them. If they are not 100% needed, avoid them, since nobody except you may be able to decipher them.
|
||||
|
||||
It's - just like in C - the middle ground between smart, efficient and readable.
|
||||
|
||||
If you need to use a cryptic construct, include a comment that explains what your "monster" does.
|
||||
|
||||
==== Variable names ====
|
||||
|
||||
Since all reserved variables are ''UPPERCASE'', the safest way is to only use ''lowercase'' variable names. This is true for reading user input, loop counting variables, etc., ... (in the example: ''file'')
|
||||
|
||||
|
||||
* prefer ''lowercase'' variables
|
||||
* if you use ''UPPERCASE'' names, **do not use reserved variable names** (see [[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)
|
||||
|
||||
<code>
|
||||
#!/bin/bash
|
||||
|
||||
# the prefix 'MY_'
|
||||
MY_LOG_DIRECTORY=/var/adm/
|
||||
|
||||
for file in "$MY_LOG_DIRECTORY"/*; do
|
||||
echo "Found Logfile: $file"
|
||||
done
|
||||
</code>
|
||||
==== Variable initialization ====
|
||||
|
||||
As in C, it's always a good idea to initialize your variables, though, the shell will initialize fresh variables itself (better: Unset variables will generally behave like variables containing a null string).
|
||||
|
||||
It's no problem to pass an **environment variable** to the script. If you blindly assume that all variables you use for the first time are **empty**, anybody can **inject** content into a variable by passing it via the environment.
|
||||
|
||||
The solution is simple and effective: **Initialize them**
|
||||
<code>
|
||||
my_input=""
|
||||
my_array=()
|
||||
my_number=0
|
||||
</code>
|
||||
|
||||
If you do that for every variable you use, then you also have some in-code documentation for them.
|
||||
|
||||
==== Parameter expansion ====
|
||||
|
||||
Unless you are really sure what you're doing, **quote every parameter expansion**.
|
||||
|
||||
There are some cases where this isn't needed from a technical point of view, e.g.
|
||||
* inside ''<nowiki>[[ ... ]]</nowiki>'' (other than the RHS of the ''=='', ''!='', and ''=~'' operators)
|
||||
* the parameter (''WORD'') in ''case $WORD in ....''
|
||||
* variable assignment: ''VAR=$WORD''
|
||||
|
||||
But quoting these is never a mistake. If you quote every parameter expansion, you'll be safe.
|
||||
|
||||
If you need to parse a parameter as a list of words, you can't quote, of course, e.g.
|
||||
<code>
|
||||
list="one two three"
|
||||
|
||||
# you MUST NOT quote $list here
|
||||
for word in $list; do
|
||||
...
|
||||
done
|
||||
</code>
|
||||
|
||||
==== Function names ====
|
||||
|
||||
Function names should be all ''lowercase'' and meaningful. The function names should be human readable. A function named ''f1'' may be easy and quick to write down, but for debugging and especially for other people, it reveals nothing. Good names help document your code without using extra comments.
|
||||
|
||||
**do not use command names for your functions**. e.g. naming a script or function ''test'', will collide with the UNIX ''test'' command.
|
||||
|
||||
Unless absolutely necessary, only use alphanumeric characters and the underscore for function names. ''/bin/ls'' is a valid function name in Bash, but is not a good idea.
|
||||
|
||||
|
||||
==== Command substitution ====
|
||||
|
||||
As noted in [[syntax:expansion:cmdsubst | the article about command substitution]], you should use the ''$( ... )'' form.
|
||||
|
||||
If portability is a concern, use the backquoted form ''` ... `''.
|
||||
|
||||
In any case, if other expansions and word splitting are not wanted, you should quote the command substitution!
|
||||
|
||||
==== Eval ====
|
||||
|
||||
Well, like Greg says: **"If eval is the answer, surely you are asking the wrong question."**
|
||||
|
||||
Avoid it, unless absolutely neccesary:
|
||||
* ''eval'' can be your neckshot
|
||||
* there are most likely other ways to achieve what you want
|
||||
* if possible, re-think the way your script works, if it seems you can't avoid ''eval'' with your current method
|
||||
* if you really, really, have to use it, then take care, and be sure about what you're doing
|
||||
|
||||
===== Basic structure =====
|
||||
|
||||
The basic structure of a script simply reads:
|
||||
|
||||
<code>
|
||||
#!SHEBANG
|
||||
|
||||
CONFIGURATION_VARIABLES
|
||||
|
||||
FUNCTION_DEFINITIONS
|
||||
|
||||
MAIN_CODE
|
||||
</code>
|
||||
|
||||
==== The shebang ====
|
||||
|
||||
If possible (I know it's not always possible!), use [[dict:terms:shebang | a shebang]].
|
||||
|
||||
Be careful with ''/bin/sh'': The argument that "on Linux ''/bin/sh'' is Bash" **is a lie** (and technically irrelevant)
|
||||
|
||||
The shebang serves two purposes for me:
|
||||
* it specifies the interpreter to be used when the script file is called directly: If you code for Bash, specify ''bash''!
|
||||
* it documents the desired interpreter (so: use ''bash'' when you write a Bash-script, use ''sh'' when you write a general Bourne/POSIX script, ...)
|
||||
|
||||
|
||||
==== Configuration variables ====
|
||||
|
||||
I call variables that are meant to be changed by the user "configuration variables" here.
|
||||
|
||||
Make them easy to find (directly at the top of the script), give them meaningful names and maybe a short comment. As noted above, use ''UPPERCASE'' for them only when you're sure about what you're doing. ''lowercase'' will be the safest.
|
||||
|
||||
|
||||
==== Function definitions ====
|
||||
|
||||
Unless there are reasons not to, all function definitions should be declared before the main script code runs. This gives a far better overview and ensures that all function names are known before they are used.
|
||||
|
||||
Since a function isn't parsed before it is executed, you usually don't have to ensure they're in a specific order.
|
||||
|
||||
The portable form of the function definition should be used, without the ''function'' keyword (here using the [[syntax:ccmd:grouping_plain | grouping compound command]]):
|
||||
<code>
|
||||
getargs() {
|
||||
...
|
||||
}
|
||||
</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.
|
||||
|
||||
===== Behaviour and robustness =====
|
||||
|
||||
==== Fail early ====
|
||||
|
||||
**Fail early**, this sounds bad, but usually is good. Failing early means to error out as early as possible when checks indicate an error or unmet condition. Failing early means to error out **before** your script begins its work in a potentially broken state.
|
||||
|
||||
==== Availability of commands ====
|
||||
|
||||
If you use external commands that may not be present on the path, or not installed, check for their availability, then tell the user they're missing.
|
||||
|
||||
Example:
|
||||
<code>
|
||||
my_needed_commands="sed awk lsof who"
|
||||
|
||||
missing_counter=0
|
||||
for needed_command in $my_needed_commands; do
|
||||
if ! hash "$needed_command" >/dev/null 2>&1; then
|
||||
printf "Command not found in PATH: %s\n" "$needed_command" >&2
|
||||
((missing_counter++))
|
||||
fi
|
||||
done
|
||||
|
||||
if ((missing_counter > 0)); then
|
||||
printf "Minimum %d commands are missing in PATH, aborting\n" "$missing_counter" >&2
|
||||
exit 1
|
||||
fi
|
||||
</code>
|
||||
|
||||
==== Exit meaningfully ====
|
||||
|
||||
The [[dict:terms:exit_status | exit code]] is your only way to directly communicate with the calling process without any special provisions.
|
||||
|
||||
If your script exits, provide a meaningful exit code. That minimally means:
|
||||
* ''exit 0'' (zero) if everything is okay
|
||||
* ''exit 1'' - in general non-zero - if there was an error
|
||||
|
||||
This, **and only this**, will enable the calling component to check the operation status of your script.
|
||||
|
||||
You know:
|
||||
**"One of the main causes of the fall of the Roman Empire was that, lacking zero, they had no way to indicate successful termination of their C programs."** //-- Robert Firth//
|
||||
|
||||
===== Misc =====
|
||||
|
||||
==== Output and appearance ====
|
||||
|
||||
* if the script is interactive, if it works for you and if you think this is a nice feature, you can try to [[snipplets:screen_saverestore | save the terminal content and restore it]] after execution
|
||||
* output clean and understandable screen messages
|
||||
* if applicable, you can use colors or specific prefixes to tag error and warning messages
|
||||
* make it easy for the user to identify those messages
|
||||
* write normal output to ''STDOUT''. write error, warning and diagnostic messages to ''STDERR''
|
||||
* enables message filtering
|
||||
* keeps the script from mixing output data with diagnostic, or error messages
|
||||
* if the script gives syntax help (''-?'' or ''-h'' or ''--help'' arguments), it should go to ''STDOUT''
|
||||
* if applicable, write error/diagnostic messages to a logfile
|
||||
* avoids screen clutter
|
||||
* messages are available for diagnostic use
|
||||
|
||||
==== Input ====
|
||||
|
||||
* never blindly assume anything. If you want the user to input a number, **check for numeric input, leading zeros**, etc. If you have specific format or content needs, **always validate the input!**
|
||||
|
||||
==== Tooling ====
|
||||
|
||||
* some of these guidelines, such as indentation, positioning of "body-introducing" keywords, and portable function declarations, can be enforced by [[https://github.com/mvdan/sh|shfmt]]
|
282
original_source/scripting/terminalcodes.txt
Normal file
282
original_source/scripting/terminalcodes.txt
Normal file
@ -0,0 +1,282 @@
|
||||
====== Terminal codes (ANSI/VT100) introduction ======
|
||||
|
||||
{{keywords>bash shell scripting colors cursor control vt100 ansi}}
|
||||
|
||||
Terminal (control) codes are used to issue specific commands to your terminal. This can be related to switching colors or positioning the cursor, i.e. anything that can't be done by the application itself.
|
||||
|
||||
|
||||
===== How it technically works =====
|
||||
|
||||
A terminal control code is a special sequence of characters that is printed (like any other text). If the terminal understands the code, it won't display the character-sequence, but will perform some action. You can print the codes with a simple ''echo'' command.
|
||||
|
||||
__**Note:**__ I see codes referenced as "Bash colors" sometimes (several "Bash tutorials" etc...): That's a completely incorrect definition.
|
||||
|
||||
===== The tput command =====
|
||||
|
||||
Because there's a large number of different terminal control languages, usually a system has an intermediate communication layer. The real codes are looked up in a database **for the currently detected terminal type** and you give standardized requests to an API or (from the shell) to a command.
|
||||
|
||||
One of these commands is ''tput''. Tput accepts a set of acronyms called //capability names// and any parameters, if appropriate, then looks up the correct escape sequences for the detected terminal in the ''terminfo'' database and prints the correct codes (the terminal hopefully understands).
|
||||
|
||||
===== The codes =====
|
||||
|
||||
In this list I'll focus on ANSI/VT100 control codes for the most common actions - take it as quick reference. The documentation of your terminal or the ''terminfo'' database is always the preferred source when something is unclear! Also the ''tput'' acronyms are usually the ones dedicated for ANSI escapes!
|
||||
|
||||
I listed only the most relevant codes, of course, any ANSI terminal understands many more! But let's keep the discussion centered on common shell scripting ;-)
|
||||
|
||||
If I couldn't find a matching ANSI escape, you'll see a :?: as the code. Feel free to mail me or fix it.
|
||||
|
||||
The ANSI codes always start with the ESC character. (ASCII 0x1B or octal 033) This isn't part of the list, but **you should avoid using the ANSI codes directly - use the ''tput'' command!**
|
||||
|
||||
All codes that can be used with ''tput'' can be found in terminfo(5). (on OpenBSD at least)
|
||||
See [[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 ====
|
||||
|
||||
The **Ctrl-Key** representation is simply associating the non-printable characters from ASCII code 1 with the printable (letter) characters from ASCII code 65 ("A"). ASCII code 1 would be ''^A'' (Ctrl-A), while ASCII code 7 (BEL) would be ''^G'' (Ctrl-G). This is a common representation (and input method) and historically comes from one of the VT series of terminals.
|
||||
|
||||
^Name^decimal^octal^hex^C-escape^Ctrl-Key^Description^
|
||||
|''BEL''|7|007|0x07|''\a''|''^G''|Terminal bell|
|
||||
|''BS''|8|010|0x08|''\b''|''^H''|Backspace|
|
||||
|''HT''|9|011|0x09|''\t''|''^I''|Horizontal TAB|
|
||||
|''LF''|10|012|0x0A|''\n''|''^J''|Linefeed (newline)|
|
||||
|''VT''|11|013|0x0B|''\v''|''^K''|Vertical TAB|
|
||||
|''FF''|12|014|0x0C|''\f''|''^L''|Formfeed (also: New page ''NP'')|
|
||||
|''CR''|13|015|0x0D|''\r''|''^M''|Carriage return|
|
||||
|''ESC''|27|033|0x1B|''<none>''|''^[''|Escape character|
|
||||
|''DEL''|127|177|0x7F|''<none>''|''<none>''|Delete character|
|
||||
|
||||
==== Cursor handling ====
|
||||
|
||||
^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|
|
||||
|''[ H''|''home''|Move cursor to home position (0-0)|
|
||||
|''7''|''sc''|Save current cursor position|
|
||||
|''8''|''rc''|Restore saved cursor position|
|
||||
|:?: most likely a normal code like ''\b''|''cub1''|move left one space (backspace)|
|
||||
|VT100 ''[ ? 25 l''|''civis''|make cursor invisible|
|
||||
|VT100 ''[ ? 25 h''|''cvvis''|make cursor visible|
|
||||
|
||||
==== Erasing text ====
|
||||
|
||||
^ ANSI ^ terminfo equivalent ^ Description ^
|
||||
| ''[ K''\\ ''[ 0 K'' | ''el'' | **Clear line** from current cursor position **to end** of line |
|
||||
| ''[ 1 K'' | ''el1'' | **Clear line from beginning** to current cursor position |
|
||||
| ''[ 2 K'' | ''el2'':?: | **Clear whole line** (cursor position unchanged) |
|
||||
|
||||
==== General text attributes ====
|
||||
|
||||
^ANSI^terminfo equivalent^Description^
|
||||
|''[ 0 m''|''sgr0''|Reset all attributes|
|
||||
|''[ 1 m''|''bold''|Set "bright" attribute|
|
||||
|''[ 2 m''|''dim''|Set "dim" attribute|
|
||||
|''[ 3 m''|''smso''|Set "standout" attribute|
|
||||
|''[ 4 m''|set ''smul'' unset ''rmul'' :?:|Set "underscore" (underlined text) attribute|
|
||||
|''[ 5 m''|''blink''|Set "blink" attribute|
|
||||
|''[ 7 m''|''rev''|Set "reverse" attribute|
|
||||
|''[ 8 m''|''invis''|Set "hidden" attribute|
|
||||
|
||||
==== Foreground coloring ====
|
||||
|
||||
^ ANSI ^ terminfo equivalent ^ Description ^
|
||||
| ''[ 3 0 m'' | ''setaf 0'' | Set **foreground** to color #0 - **black** |
|
||||
| ''[ 3 1 m'' | ''setaf 1'' | Set **foreground** to color #1 - **red** |
|
||||
| ''[ 3 2 m'' | ''setaf 2'' | Set **foreground** to color #2 - **green** |
|
||||
| ''[ 3 3 m'' | ''setaf 3'' | Set **foreground** to color #3 - **yellow** |
|
||||
| ''[ 3 4 m'' | ''setaf 4'' | Set **foreground** to color #4 - **blue** |
|
||||
| ''[ 3 5 m'' | ''setaf 5'' | Set **foreground** to color #5 - **magenta** |
|
||||
| ''[ 3 6 m'' | ''setaf 6'' | Set **foreground** to color #6 - **cyan** |
|
||||
| ''[ 3 7 m'' | ''setaf 7'' | Set **foreground** to color #7 - **white** |
|
||||
| ''[ 3 9 m'' | ''setaf 9'' | Set **default** color as foreground color |
|
||||
|
||||
==== Background coloring ====
|
||||
|
||||
^ ANSI ^ terminfo equivalent ^ Description ^
|
||||
| ''[ 4 0 m'' | ''setab 0'' | Set **background** to color #0 - **black** |
|
||||
| ''[ 4 1 m'' | ''setab 1'' | Set **background** to color #1 - **red** |
|
||||
| ''[ 4 2 m'' | ''setab 2'' | Set **background** to color #2 - **green** |
|
||||
| ''[ 4 3 m'' | ''setab 3'' | Set **background** to color #3 - **yellow** |
|
||||
| ''[ 4 4 m'' | ''setab 4'' | Set **background** to color #4 - **blue** |
|
||||
| ''[ 4 5 m'' | ''setab 5'' | Set **background** to color #5 - **magenta** |
|
||||
| ''[ 4 6 m'' | ''setab 6'' | Set **background** to color #6 - **cyan** |
|
||||
| ''[ 4 7 m'' | ''setab 7'' | Set **background** to color #7 - **white** |
|
||||
| ''[ 4 9 m'' | ''setab 9'' | Set **default** color as background color |
|
||||
|
||||
==== Misc codes ====
|
||||
|
||||
=== Save/restore screen ===
|
||||
|
||||
Used capabilities: ''smcup'', ''rmcup''
|
||||
|
||||
You've undoubtedly already encountered programs that restore the terminal contents after they do their work (like ''vim''). This can be done by the following commands:
|
||||
<code>
|
||||
# save, clear screen
|
||||
tput smcup
|
||||
clear
|
||||
|
||||
# example "application" follows...
|
||||
read -n1 -p "Press any key to continue..."
|
||||
# example "application" ends here
|
||||
|
||||
# restore
|
||||
tput rmcup
|
||||
</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:
|
||||
|
||||
<code>
|
||||
echo -e '\033[?47h' # save screen
|
||||
echo -e '\033[?47l' # restore screen
|
||||
</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:
|
||||
|
||||
<code>
|
||||
export LESS=X
|
||||
less /path/to/file
|
||||
</code>
|
||||
|
||||
Similarly, ''vim'' can be configured not to "restore" the screen by adding the following to your ''~/.vimrc'':
|
||||
|
||||
<code>
|
||||
set t_ti= t_te=
|
||||
</code>
|
||||
|
||||
|
||||
|
||||
=== Additional colors ===
|
||||
|
||||
Some terminal emulators support additional colors. The most common extension used by xterm-compatible terminals supports 256 colors. These can be generated by ''tput'' with ''seta{f,b} [0-255]'' when the ''TERM'' value has a ''-256color'' suffix.
|
||||
[[https://gist.github.com/XVilka/8346728#now-supporting-truecolour|Some terminals]] also support full 24-bit colors, and any X11 color code can be written directly into a special escape sequence. ([[https://gist.github.com/XVilka/8346728|More infos]]) Only a few programs make use of anything beyond 256 colors, and tput doesn't know about them. Colors beyond 16 usually only apply to modern terminal emulators running in graphical environments.
|
||||
|
||||
The Virtual Terminal implemented in the Linux kernel supports only 16 colors, and the usual default terminfo entry for ''TERM=linux'' defines only 8. There is sometimes an alternate "linux-16color" that you can switch to, to get the other 8 colors.
|
||||
|
||||
===== Bash examples =====
|
||||
|
||||
==== Hardcoded colors ====
|
||||
|
||||
<code>
|
||||
printf '%b\n' 'It is \033[31mnot\033[39m intelligent to use \033[32mhardcoded ANSI\033[39m codes!'
|
||||
</code>
|
||||
|
||||
==== Colors using tput ====
|
||||
|
||||
__Directly inside the echo:__
|
||||
<code>
|
||||
echo "TPUT is a $(tput setaf 2)nice$(tput setaf 9) and $(tput setaf 5)user friendly$(tput setaf 9) terminal capability database."
|
||||
</code>
|
||||
|
||||
__With preset variables:__
|
||||
<code>
|
||||
COL_NORM="$(tput setaf 9)"
|
||||
COL_RED="$(tput setaf 1)"
|
||||
COL_GREEN="$(tput setaf 2)"
|
||||
echo "It's ${COL_RED}red${COL_NORM} and ${COL_GREEN}green${COL_NORM} - have you seen?"
|
||||
</code>
|
||||
|
||||
==== Misc ====
|
||||
|
||||
__HOME function__
|
||||
<code>
|
||||
home() {
|
||||
# yes, actually not much shorter ;-)
|
||||
tput home
|
||||
}
|
||||
</code>
|
||||
|
||||
==== Silly but nice effect ====
|
||||
|
||||
<code>
|
||||
#!/bin/bash
|
||||
|
||||
DATA[0]=" _/ _/ _/ _/ "
|
||||
DATA[1]=" _/_/_/_/_/ _/_/_/ _/_/_/ _/_/_/ _/_/_/ "
|
||||
DATA[2]=" _/ _/ _/ _/ _/ _/ _/_/ _/ _/"
|
||||
DATA[3]="_/_/_/_/_/ _/ _/ _/ _/ _/_/ _/ _/ "
|
||||
DATA[4]=" _/ _/ _/_/_/ _/_/_/ _/_/_/ _/ _/ "
|
||||
|
||||
# virtual coordinate system is X*Y ${#DATA} * 5
|
||||
|
||||
REAL_OFFSET_X=0
|
||||
REAL_OFFSET_Y=0
|
||||
|
||||
draw_char() {
|
||||
V_COORD_X=$1
|
||||
V_COORD_Y=$2
|
||||
|
||||
tput cup $((REAL_OFFSET_Y + V_COORD_Y)) $((REAL_OFFSET_X + V_COORD_X))
|
||||
|
||||
printf %c ${DATA[V_COORD_Y]:V_COORD_X:1}
|
||||
}
|
||||
|
||||
|
||||
trap 'exit 1' INT TERM
|
||||
trap 'tput setaf 9; tput cvvis; clear' EXIT
|
||||
|
||||
tput civis
|
||||
clear
|
||||
|
||||
while :; do
|
||||
|
||||
for ((c=1; c <= 7; c++)); do
|
||||
tput setaf $c
|
||||
for ((x=0; x<${#DATA[0]}; x++)); do
|
||||
for ((y=0; y<=4; y++)); do
|
||||
draw_char $x $y
|
||||
done
|
||||
done
|
||||
done
|
||||
|
||||
done
|
||||
</code>
|
||||
|
||||
==== 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.
|
||||
|
||||
<code>
|
||||
#!/usr/bin/env ksh
|
||||
|
||||
# Charles Cooke's 16-color Mandelbrot
|
||||
# http://earth.gkhs.net/ccooke/shell.html
|
||||
# Combined Bash/ksh93 flavors by Dan Douglas (ormaaj)
|
||||
|
||||
function doBash {
|
||||
typeset P Q X Y a b c i v x y
|
||||
for ((P=10**8,Q=P/100,X=320*Q/cols,Y=210*Q/lines,y=-105*Q,v=-220*Q,x=v;y<105*Q;x=v,y+=Y)); do
|
||||
for ((;x<P;a=b=i=c=0,x+=X)); do
|
||||
for ((;a**2+b**2<4*P**2&&i++<99;a=((c=a)**2-b**2)/P+x,b=2*c*b/P+y)); do :
|
||||
done
|
||||
colorBox $((i<99?i%16:0))
|
||||
done
|
||||
echo
|
||||
done
|
||||
}
|
||||
|
||||
function doKsh {
|
||||
integer i
|
||||
float a b c x=2.2 y=-1.05 X=3.2/cols Y=2.1/lines
|
||||
while
|
||||
for ((a=b=i=0;(c=a)**2+b**2<=2&&i++<99&&(a=a**2-b**2+x,b=2*c*b+y);)); do :
|
||||
done
|
||||
. colorBox $((i<99?i%16:0))
|
||||
if ((x<1?!(x+=X):(y+=Y,x=-2.2))); then
|
||||
print
|
||||
((y<1.05))
|
||||
fi
|
||||
do :
|
||||
done
|
||||
}
|
||||
|
||||
function colorBox {
|
||||
(($1==lastclr)) || printf %s "${colrs[lastclr=$1]:=$(tput setaf "$1")}"
|
||||
printf '\u2588'
|
||||
}
|
||||
|
||||
unset -v lastclr
|
||||
((cols=$(tput cols)-1, lines=$(tput lines)))
|
||||
typeset -a colrs
|
||||
trap 'tput sgr0; echo' EXIT
|
||||
${KSH_VERSION+. doKsh} ${BASH_VERSION+doBash}
|
||||
</code>
|
||||
|
||||
A much more sophisticated version by Roland Mainz can be found [[http://svn.nrubsig.org/svn/people/gisburn/scripts/mandelbrotset1.sh | here]]
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user