bash — Standard Shell

A thorough understanding of bash programming is vital when working with ebuilds.

Bash Conditionals

Basic Selection

The basic conditional operator is the if statement:

if something ; then
	do_stuff
fi

Multiple Selection

Multiple pronged selection can be done using else and elif:

if something ; then
	do_stuff
elif something_else ; then
	do_other_stuff
elif full_moon ; then
	howl
else
	turn_into_a_newt
fi
if some_stuff ; then
	# A statement is required here. a blank or a comment
	# isn't enough!
else
	einfo "Not some stuff"
fi

If you really don't want to restructure the block, you can use a single colon (:) on its own as a null statement.

if some_stuff ; then
	# Do nothing
	:
else
	einfo "Not some stuff"
fi

Selection Tests

To do comparisons or file attribute tests, [[ ]] (preferred) or [ ] blocks are needed.

# is ${foo} zero length?
if [[ -z ${foo} ]] ; then
	die "Please set foo"
fi

# is ${foo} equal to "moo"?
if [[ ${foo} == "moo" ]] ; then
	einfo "Hello Larry"
fi

# does ${ROOT}/etc/deleteme exist?
if [[ -f ${ROOT}/etc/deleteme ]] ; then
	einfo "Please delete ${ROOT}/etc/deleteme manually!"
fi

Single versus Double Brackets in bash

POSIX compliance is not a concern for ebuilds, as their interpreter is guaranteed to be GNU Bash. POSIX style tests have different semantics and using the common forms of tests adheres to the principle of least surprise. Most developers will be used to Bash test semantics and behaviour and deviating from this in ebuilds may be confusing.

This is because [[ ]] is a bash syntax construct, whereas [ ] is a program which happens to be implemented as an internal — as such, cleaner syntax is possible with the former. For a simple illustration, consider:

bash$ [ -n ${foo} ] && [ -z ${foo} ] && echo "huh?"
huh?
bash$ [[ -n ${foo} ]] && [[ -z ${foo} ]] && echo "huh?"
bash$

String Comparison in bash

The general form of a string comparison is string1 operator string2. The following are available:

Operator Purpose
== (also =) String equality
!= String inequality
< String lexicographic comparison (before)
> String lexicographic comparison (after)
=~ String regular expression match

String Tests in bash

The general form of string tests is -operator "string". The following are available:

Operator Purpose
-z "string" String has zero length
-n "string" String has non-zero length

Integer Comparison in bash

The general form of integer comparisons is int1 -operator int2. The following are available:

Operator Purpose
-eq Integer equality
-ne Integer inequality
-lt Integer less than
-le Integer less than or equal to
-gt Integer greater than
-ge Integer greater than or equal to

File Tests in bash

The general form of a file test is -operator "filename". The following are available (lifted from man bash):

Operator Purpose
-a file Exists (use -e instead)
-b file Exists and is a block special file
-c file Exists and is a character special file
-d file Exists and is a directory
-e file Exists
-f file Exists and is a regular file
-g file Exists and is set-group-id
-h file Exists and is a symbolic link
-k file Exists and its sticky bit is set
-p file Exists and is a named pipe (FIFO)
-r file Exists and is readable
-s file Exists and has a size greater than zero
-t fd Descriptor fd is open and refers to a terminal
-u file Exists and its set-user-id bit is set
-w file Exists and is writable
-x file Exists and is executable
-O file Exists and is owned by the effective user id
-G file Exists and is owned by the effective group id
-L file Exists and is a symbolic link
-S file Exists and is a socket
-N file Exists and has been modified since it was last read

File Comparison in bash

The general form of a file comparison is "file1" -operator "file2". The following are available:

Operator Purpose
file1 -nt file2 file1 is newer (according to modification date) than file2, or file1 exists and file2 does not
file1 -ot file2 file1 is older than file2, or file2 exists and file1 does not
file1 -ef file2 file1 is a hard link to file2

Boolean Algebra in bash

There are constructs available for boolean algebra ('and', 'or' and 'not'). These are used outside of the [[ ]] blocks. For operator precedence, use ( ).

Construct Effect
first || second first or second (short circuit)
first && second first and second (short circuit)
! condition not condition

Inside [ ] blocks, several -test style boolean operators are available. These should be avoided in favour of [[ ]] and the above operators.

Bash Iterative Structures

There are a few simple iterative structures available from within bash. The most useful of these is a for loop. This can be used to perform the same task upon multiple items.

for myvar in "the first" "the second" "and the third" ; do
	einfo "This is ${myvar}"
done

There is a second form of the for loop which can be used for repeating an event a given number of times.

for (( i = 1 ; i <= 10 ; i++ )) ; do
	einfo "i is ${i}"
done

There is also a while loop, although this is usually not useful within ebuilds.

while hungry ; do
	eat_cookies
done

This is most commonly used to iterate over lines in a file:

while read myline ; do
	einfo "It says ${myline}"
done < some_file

See die and Subshells for an explanation of why while read < file should be used over cat file | while read.

Bash Variable Manipulation

There are a number of special ${} constructs in bash which either manipulate or return information based upon variables. These can be used instead of expensive (or illegal, if we're in global scope) external calls to sed and friends.

bash String Length

The ${#somevar} construct can be used to obtain the length of a string variable.

somevar="Hello World"
echo "${somevar} is ${#somevar} characters long"

bash Variable Default Values

There are a number of ways of using a default value if a variable is unset or zero length. The ${var:-value} construct expands to the value of ${var} if it is set and not null, or value otherwise. The ${var-value} construct is similar, but checks only that the variable is set.

The ${var:=value} and ${var=value} forms will also assign value to var if var is unset (and also set, but null for the := form).

The ${var:?message} form will display message to stderr and then exit if var is unset or null. This should generally not be used within ebuilds as it does not use the die mechanism. There is a ${var?message} form too.

The ${var:+value} form expands to value if var is set and not null, or a blank string otherwise. There is a ${var+value} form.

bash Substring Extraction

The ${var:offset} and ${var:offset:length} constructs can be used to obtain a substring. Strings are zero-indexed. Both offset and length are arithmetic expressions.

The first form with a positive offset returns a substring starting with the character at offset and continuing to the end of a string. If the offset is negative, the offset is taken relative to the end of the string.

The second form returns the first length characters of the value of ${var} starting at offset. If offset is negative, the offset is taken from the end of the string. The length parameter must not be less than zero. Again, negative offset values must be given as an expression.

bash Command Substitution

The $(command) construct can be used to run a command and capture the output (stdout) as a string.

myconf="$(use_enable acl) $(use_enable nls) --with-tlib=ncurses"

bash String Replacements

There are three basic string replacement forms available: ${var#pattern}, ${var%pattern} and ${var/pattern/replacement}. The first two are used for deleting content from the start and end of a string respectively. The third is used to replace a match with different content.

The ${var#pattern} form will return var with the shortest match of pattern at the start of the value of var deleted. If no match can be made, the value of var is given. To delete the longest match at the start, use ${var##pattern} instead.

The ${var%pattern} and ${var%%pattern} forms are similar, but delete the shortest and longest matches at the end of var respectively.

The ${var/pattern/replacement} construct expands to the value of var with the first match of pattern replaced with replacement. To replace all matches, ${var//pattern/replacement} can be used.

To match only if pattern occurs at the start of the value of var, the pattern should begin with a # character. To match only at the end, the pattern should begin with a %.

If replacement is null, matches are deleted and the / following pattern may be omitted.

The pattern may contain a number of special metacharacters for pattern matching.

Character Meaning
* Matches any string, including the null string
? Matches any single character
[...] Matches any one of the enclosed characters

Refer to the Bash Reference Manual for further details and caveats regarding these characters.

If the extglob shell option is enabled, a number of additional constructs are available. These can be extremely useful sometimes. In the following table, a pattern-list is a list of one or more patterns separated by |.

Construct Meaning
?(pattern-list) Matches zero or one occurrence of the given patterns
*(pattern-list) Matches zero or more occurrences of the given patterns
+(pattern-list) Matches one or more occurrences of the given patterns
@(pattern-list) Matches one of the given patterns
!(pattern-list) Matches anything except one of the given patterns

bash Arithmetic Expansion

The $(( expression )) construct can be used for integer arithmetic evaluation. expression is a C-like arithmetic expression. The following operators are supported (the table is in order of precedence, highest first):

Operators Effect
var++, var-- Variable post-increment, post-decrement
++var, --var Variable pre-increment, pre-decrement
-, + Unary minus and plus
!, ~ Logical negation, bitwise negation
** Exponentiation
*, /, % Multiplication, division, remainder
+, - Addition, subtraction
<<, >> Left, right bitwise shifts
<=, >=, <, > Comparison: less than or equal to, greater than or equal to, strictly less than, strictly greater than
==, != Equality, inequality
& Bitwise AND
^ Bitwise exclusive OR
| Bitwise OR
&& Logical AND
|| Logical OR
expr ? expr : expr Conditional operator
=, *=, /=, %=, +=, -=, <<=, >>=, &=, ^=, |= Assignment
expr1 , expr2 Multiple statements