Bash syntax and semantics

From Wikinerds

Jump to: navigation, search

The syntax of the Bash script is the set of rules that defines how the Bash shell will be interpreted. A script that does not conform to these rules abnormally exit or produce unexpected results.

Contents

[edit] Integer mathematics

Bash can perform integer calculations without spawning external processes, unlike the Bourne shell. Bash uses the ((...)) command and the $[...] variable syntax for this purpose.

VAR=55 # Assign integer 55 to variable VAR.
((VAR = VAR + 1)) # Add one to variable VAR. Note the absence of the '$' character.
((++VAR)) # Another way to add one to VAR. Performs C-style pre-increment.
((VAR++)) # Another way to add one to VAR. Performs C-style post-increment.
echo $[VAR * 22] # Multiply VAR by 22 and substitute the result into the command.
echo $((VAR * 22)) # Another way to do the above.
<pre>
The <tt>((...))</tt> command can also be used in conditional statements because its [[exit status]] is zero or nonzero (most times 1) depending on whether the condition is true or false.
<source lang="bash">
if ((VAR == Y * 3 + X * 2))
then
echo Yes
fi
((Z > 23)) && echo Yes
</source>
The <tt>((...))</tt> command supports the following [[relational operator]]s: '<tt>==</tt>', '<tt>!=</tt>', '<tt>></tt>', '<tt><</tt>', '<tt>>=</tt>', and '<tt><=</tt>'.
Bash cannot perform in process [[floating point]] calculations. The only Unix command shells currently capable of this are the [[Korn Shell]] and the [[Z shell]].
== Integer comparison ==
While not Bash specific it is still very relevant.
<pre>
-eq Equal to
-ne Not equal to
-gt Greater than
-ge Greater than or equal to
-lt Lesser than
-le Lesser than or equal to
# Within ((....)) expressions: 
<   Lesser than 
>   Greater than
<=  Lesser than or equal to
>=  Greater than or equal to

Examples:

<source lang="bash"> n1=34 n2=43

if [ "$n1" -eq "$n2" ]; then # Normal operation if (("$n1" < "$n2")); then # Special construct to use with <, >, <=, and >=. </source>

[edit] String comparison

While not Bash specific this is as relevant for Bash as any other shell.

Operators

== String1 is equal to String2 (same as single = on some systems)
!= String1 is not equal to String2
<  String1 is lesser than String2 in ASCII value (see example for correct quoting)
>  String1 is greater than String2 in ASCII value (see example for correct quoting)
-z String is empty / zero length
-n String is not null / empty

Examples:

<source lang="bash"> s1="Test" s2="String"

if [ $s1 == $s2 ]; then # This is the base form, but things may go wrong. if [ "$s1" == "$s2" ]; then # This takes care of spaces in the strings - but still room for improvement. if [ "x$s1" == "x$s2" ]; then # By prepending with "x" it will also handle empty strings and not fail because of that. </source>

Be aware of escaping < and > when shell scripting

<source lang="bash"> if [[ "x$s1" < "x$s2" ]]; then # Use double brackets to avoid escaping < and >. if [ "x$s1" \< "x$s2" ]; then # Or simply escape it to treat is as the special (redirect) character it is. </source>

[edit] File tests

While this is not Bash specific it is definitely relevant to this article.

Regular tests

-d Directory
-e Exists (same as -a on some systems)
-f Regular file
-h Symbolic link (same as -L on some systems)
-p Named pipe
-r Readable
-s Not zerosize
-S Socket
-w Writable
-N Modified since last read

Comparing tests

-nt File1 newer than File2
-ot File1 older than File2
-ef File1 is a hard link to File2

Examples

<source lang="bash"> if [ -e /tmp/file ]; then

  echo "File exists."

else

  echo "File does not exist!"

fi </source>

<source lang="bash"> if [ /tmp/file1 -nt /tmp/file2 ]; then

  echo "File1 is newer than file2."

else

  echo "File1 is older than file2."

fi </source>

[edit] I/O redirection

Bash has several I/O redirection syntaxes that the traditional Bourne shell lacks. Bash can redirect standard output and standard error at the same time using the following syntax.

command &> file

This is simpler to type than the Bourne shell equivalent 'command > file 2>&1'.

Example: Redirect standard output to a file, write data, close file, reset stdout

<source lang="bash">

  1. make Filedescriptor(FD) 6 a copy of stdout (FD 1)

exec 6>&1

  1. open file "test.data" for writing

exec 1>test.data

  1. produce some content

echo "data:data:data"

  1. close file "test.data"

exec 1>&-

  1. make stdout a copy of FD 6 (reset stdout)

exec 1>&6

  1. close FD6

exec 6>&- </source>

Open and close files

<source lang="bash">

  1. open file test.data for reading

exec 6<test.data

  1. read until end of file

while read -u 6 dta do

   echo "$dta"

done

  1. close file test.data

exec 6<&- </source>

Catch output of external commands

<source lang="bash">

  1. execute 'find' and store results in VAR
  2. search for filenames which end with the letter "h"

VAR=$(find . -name "*h") </source>

Bash supports here documents just as the Bourne shell always has. However, since version 2.05b Bash can redirect standard input from a "here string" using the following syntax.

command <<< "string to be read as standard input"

If the string contains whitespace it must, of course, be quoted or the white space escaped.

Example: Temporarily use here document as standard input, process that, restore original standard input, and continue processing

<source lang="bash">

  1. !/bin/sh
  2. Save stdin and replace with indended "here" doc:

exec 9<&0 <<-TMPSTDIN

   # Interface table ... for table driven configuration code:
   # IF       ADDR        NETMASK           BROADCAST       GATEWAY
   eth0    123.45.67.89    255.255.255.0     123.45.67.255   123.45.67.1
   eth1    10.10.17.5      255.255.0.0       10.10.255.255   
   eth2    172.16.0.100    255.224.0.0       172.31.255.255   

TMPSTDIN

  1. Process embedded "here" doc

while read interface addr nmask bcast gw; do

   [ -z "${interface%%#*}" ] && continue # skip commentes in here doc table
   ifconfig "$interface" "$addr" netmask "$nmask" broadcast "$bcast"
   [ -n "$gw" ] && { route add default gw "$gw"; }

done

  1. Restore stdin and close temporary file descriptor

exec 0<&9 9<&-

  1. Continue with restored standard input

echo -n "Interface table processed. Hit [Enter] key to continue:" read waiting # under bash could also use read -n 1 ... to read any key </source>

[edit] In-process regular expressions

Bash 3.0 supports in-process regular expression matching using the following syntax, reminiscent of Perl:

<source lang="bash"> string =~ regex </source>

The regular expression syntax is the same as that documented by the regex(7)[1] man page. The exit status of the above command is 0 if the regex matches the string, 1 if it does not match. Parenthesized subexpressions in the regular expression can be accessed using the shell variable BASH_REMATCH, as follows:

<source lang="bash"> if abcfoobarbletch =~ 'foo(bar)bl(.*)' then echo The regex matches\! echo $BASH_REMATCH -- outputs: foobarbletch echo ${BASH_REMATCH[1]} -- outputs: bar echo ${BASH_REMATCH[2]} -- outputs: etch fi </source>

As of Bash 3.2, the above code does not work - the regular expression begins with the first non-whitespace character after =~. So the above code would become:

<source lang="bash"> if abcfoobarbletch =~ foo(bar)bl(.*) then echo The regex matches\! echo $BASH_REMATCH -- outputs: foobarbletch echo ${BASH_REMATCH[1]} -- outputs: bar echo ${BASH_REMATCH[2]} -- outputs: etch fi </source>

This syntax gives performance superior to spawning a separate process to run a grep command, because the regular expression matching takes place within the Bash process. The advantage grep gives is that it allows the programmer to explicitly specify the style of regular expression that will be used (i.e. Perl, POSIX, etc) allowing programmers to write the regular expressions in the style they are most familiar with. However, the difficulty this imposes is that subexpressions in grep can only be used as back-references in the same expression - thus it is not possible to extract subexpressions to be used outside of the instance of grep that matched it.

If the regular expression or the string contain whitespace or shell metacharacters (such as '*' or '?'), they should be quoted (again, this is only for versions of Bash supporting regular expressions but below version 3.2).

[edit] case for Globbing

An alternative to many uses of regular expressions in Bourne compatible shell scripts as always been to use the built-in globbing as supported by the case statement. This is slightly verbose but often suffices, particularly when the need for portability is tantamount. So many calls to the external grep command can be portably eliminated using code like:

<source lang="bash"> case "$1" in

   -*) echo "Ignoring $1 switch";;
   esac

</source>

or:

<source lang="bash"> while read line; do

   case "$line" in
       \#*) continue;; # Ignoring comment line
   esac

done </source>

Of course glob patterns are far more limited than regular expressions, and normally only used filename wildcard matching. So grep is sometimes necessary and usually more familiar to programmers.

[edit] Backslash escapes

Words of the form $'string' are treated specially. The word expands to string, with backslash-escaped characters replaced as specified by the C programming language. Backslash escape sequences, if present, are decoded as follows:

Backslash Escapes
Backslash
escape
Expands to ...
\a An alert (bell) character
\b A backspace character
\e An escape character
\f A form feed character
\n A new line character
\r A carriage return character
\t A horizontal tab character
\v A vertical tab character
\\ A backslash character
\' A single quote character
\nnn The eight-bit character whose value is the octal value nnn (one to three digits)
\xHH The eight-bit character whose value is the hexadecimal value HH (one or two hex digits)
\cx A control-X character

The expanded result is single-quoted, as if the dollar sign had not been present.

A double-quoted string preceded by a dollar sign ($"...") will cause the string to be translated according to the current locale. If the current locale is C or POSIX, the dollar sign is ignored. If the string is translated and replaced, the replacement is double-quoted.

[edit] LICENCE

This article is a derivative work of the 03:10, 24 October 2007 version (time in UTC+03) of the English Wikipedia article Bash syntax and semantics, whose sole author is Wikipedia user Jeff Carr.

Note that the Wikipedia page is considered for deletion as of 2 November 2007, so the above URL may not work.

The text of this page, except any short direct quotations clearly delimited, is available under the GNU Free Documentation License version 1.2 as published by the Free Software Foundation.

[edit] HISTORY

As per the requirements of the GFDL 1.2, here is the complete history of modifications to this document at the English Wikipedia (the times are in UTC+03):

  1. 03:10, 24 October 2007 Jeff Carr (11,571 bytes) (add {{GNU}})
  2. 03:06, 24 October 2007 Jeff Carr (11,562 bytes) (→Backslash escapes - added see also links and)
  3. 03:00, 24 October 2007 Jeff Carr (11,201 bytes) (initial page creation as per Python syntax and semantics)

The most recent history of the derivative document, not including the above, can be found at the Wikinerds wiki.

Personal tools