Test[edit | edit source]

test is a bash built-in function to add conditionals in your script. [ is an alias of the test command and are analogous.

Use the test operators listed below with test or a single bracket test condition like the following example.

$ test -e /etc/hosts && echo "File exists"
File exists

## Note that the white space around [ and ] and also the operator are required.
$ [ -e /etc/hosts ] && echo "File exists"
File exists

$ if [ -e /etc/hosts ] ; then echo "File exists" ; fi
File exists
Operator Description
-e file exists
-a file exists (deprecated)

This is identical in effect to -e

-f file is a regular file (not a directory or device file)
-s file is not zero size
-d file is a directory
-b file is a block device. Eg. /dev/sda.
-c file is a character device. Eg. /dev/tty0.
-p file is a pipe. Eg. /dev/fd/0.
-h file is a symbolic link
-L file is a symbolic link
-S file is a socket
-t file (descriptor) is associated with a terminal device

This test option may be used to check whether the stdin [ -t 0 ] or stdout [ -t 1 ] in a given script is a terminal.

-r file has read permission (for the user running the test)
-w file has write permission (for the user running the test)
-x file has execute permission (for the user running the test)
-g file or directory has set-group-id (sgid) flag set

If a directory has the sgid flag set, then a file created within that directory belongs to the group that owns the directory.

-u file has set-user-id (suid) flag set
-k file or directory has sticky bit set
  • If set on a file, that file will be kept in cache memory, for quicker access.
  • If set on a directory, it restricts write permission. Setting the sticky bit adds a t to the permissions on the file or directory listing. This restricts altering or deleting specific files in that directory to the owner of those files.
-O you are owner of file
-G group-id of file same as yours
-N file modified since it was last read
f1 -nt f2 file f1 is newer than f2
f1 -ot f2 file f1 is older than f2
f1 -ef f2 files f1 and f2 are hard links to the same file
! "not" -- reverses the sense of the tests above (returns true if condition absent).

Integer[edit | edit source]

Integer tests, with test or inside single brackets.


$ test 2 -eq 2 && echo equal

Operator Description
-eq is equal to
-ne is not equal to
-gt is greater than
-ge is greater than or equal to
-lt is less than
-le is less than or equal to

Integer tests can aso be done inside double parentheses. Eg.

$ ((2==2)) && echo equal

Operator Description
== is equal
< is less than
<= is less than or equal to
> is greater than
>= is greater than or equal to

Strings[edit | edit source]

Operator Description
-z string is null, that is, has zero length
-n string is not null.
= is equal to
## Does not work in zsh. Works only in sh and bash.
$ if [ "$a" = "$b" ] ; then
  echo "Equal"
== is equal to
## Does not work in zsh. Works only in sh and bash.
$ if [ "$a" == "$b" ] ; then
  echo "Equal"

This operator inside a double bracket [[ ]] construct does pattern matching.

!= is not equal to
$ if [ "$a" != "$b" ] ; then
  echo "Not equal"

This operator inside a double bracket [[ ]] construct does pattern matching.

=~ Match using regex
$ if [ zone36-ta =~ .*ta$ ] ; then
  echo "matches"

$ if [ zone36-ta =~ *ta ] ; then
  echo "matches"

Useful Functions / Utilities[edit | edit source]

Here are some common functions I use in my scripts.

Prompt[edit | edit source]

You can prompt the user for an yes or no answer using this function. An optional default values are accepted as the second argument.

Example Usage:

if prompt "Is this okay?" Y ; then
    echo "It's Okay!"
 if ! prompt "Is this okay?" N ; then
    echo "It's not okay!"

Source: <phorkie>https://phorkie.leo.home.steamr.com/13/embed</phorkie>

max[edit | edit source]

Find the maximum of two numbers:

max (){
        echo $((A>B ? A : B))

die[edit | edit source]

Similar to the die command in PHP which prints out a message before stopping execution.


function die {
	echo $@
Caveat when using in a subshell
This will not work inside a subshell as it will just quit the subshell rather than the script. To get around this, you may need to kill the parent PID instead.

Indent[edit | edit source]

Indents text piped to the function.

# Supports up to 10 tabs
function Indent (){
	sed "s/^/${Tabs:0:$((2*$1))}/g"

echo "test" | Indent 1
echo "test" | Indent 2

Extracting / Parsing Filenames[edit | edit source]

Use the basename to extract filenames from a path and and dirname to extract the path of a filename.


$ dirname /etc/hosts
$ basename /etc/hosts

To extract only the extension or the filename without the extension, use bash's string matching or basename:

Filename=$(basename /etc/resolv.conf)
Name=`basename $Filename .conf`

Skipping N Lines[edit | edit source]

Use tail to skip the first N lines:

## -n +N outputs starting on line N.
## Eg. To skip the first 2 lines, we start on line 3:
# tail -n+3

Note that the two are equivalent, because printing from line 0 will also print starting on line 1.

# tail -n+0  
# tail -n+1

File Modification[edit | edit source]

Use stat to find when a file was access/modified. You an obtain the timestamp as a Unix timestamp by using the -c %Y parameter.

A Unix timestamp can be converted into a human readable timestamp using awk:

$ echo 1355514777 | awk '{ print strftime("%c", $1) }'
Fri 14 Dec 2012 12:52:57 PM MST

Get Unix Timestamp[edit | edit source]

$ date +"%s"

Convert Unix Timestamp to Readable Timestamp[edit | edit source]

Inversely, you can pass in the timestamp to date and print a formatted timestamp.

$ date +"%c" -d @15996137957
Thu 30 Jul 2020 01:39:17 PM MDT

Get a specific column[edit | edit source]

You can use either awk or col:

$ echo a b c | awk '{print $2}'

If the columns are in a known section of the line, you can use cut to cut a specific section out as well.

$ echo hello | cut -c -2

Preserve header while sorting without using a temporary file[edit | edit source]

You can sort an output on a specific column while preserving the header and without needing a temporary file by using a shim function that reads and prints the first line before passing the rest of the output down the pipe. Define this function:

# print the header (the first line of input)
# and then run the specified command on the body (the rest of the input)
# use it in a pipeline, e.g. ps | body grep somepattern
body() {
    IFS= read -r header
    printf '%s\n' "$header"

Then use this shim body function with the desired sort function. For example:

$ top -b -n 1 | head -n 15 | tail -n +7 \
  | body sort -k 5 -rn

Credit to https://unix.stackexchange.com/questions/11856/sort-but-keep-header-line-at-the-top for this neat trick.

Command Substitution[edit | edit source]

Command substitution allows output from a command to be substituted in place of the command itself in a script. Commands can be enclosed within backticks `command` or the POSIX compatible form $(command).

## Eg.
echo "Today is " $(date)
echo "Today is " `date`

Do not confuse $(command) with $((expression)) which is used for arithmetic expansion.

Nested Substitution[edit | edit source]

If you are trying to do something similar to:

Something=`basename `ls /home/*.txt``

You will get an error since the expansion is ambiguous. Instead, use the $(command) substitution instead:

Something=$(basename $(ls /home/*.txt))

Arithmetic Operations[edit | edit source]

Do integer arithmetic operations on variables within double brackets. For example:

ValuePlusOne=$((Value + 1))
ValueTimes12=$((Value * 12))

The operators that are supported are:

+ - / * % 

Bitwise operators also work:

^ (XOR)   | (OR)   & (AND)   << (Shift left)   >> (Shift right)

Control Blocks[edit | edit source]

Switch[edit | edit source]

read -p "What do you want to do?: " Option

case "$Option" in
		die "Complete!"
		die "Stopping installation."

Snippets[edit | edit source]

Get First IP Address[edit | edit source]

# ip a | /bin/grep inet | grep -v inet6 | tail -n+2 | head -n 1 | awk '{print $2}' | cut -d'/' -f1

Run Script within Script Directory[edit | edit source]

For scripts that need to be running on the same directory as the script location, add this line to the top of the script.

cd "$(dirname "$0")"

See Also[edit | edit source]