Contents

Bash Cheat Sheet

Syntax

File Operators

The complete list of bash 4.2 test operators:
      -a FILE        True if file exists.
      -b FILE        True if file is block special.
      -c FILE        True if file is character special.
      -d FILE        True if file is a directory.
      -e FILE        True if file exists.
      -f FILE        True if file exists and is a regular file.
      -g FILE        True if file is set-group-id.
      -h FILE        True if file is a symbolic link.
      -L FILE        True if file is a symbolic link.
      -k FILE        True if file has its `sticky' bit set.
      -p FILE        True if file is a named pipe.
      -r FILE        True if file is readable by you.
      -s FILE        True if file exists and is not empty.
      -S FILE        True if file is a socket.
      -t FD          True if FD is opened on a terminal.
      -u FILE        True if the file is set-user-id.
      -w FILE        True if the file is writable by you.
      -x FILE        True if the file is executable by you.
      -O FILE        True if the file is effectively owned by you.
      -G FILE        True if the file is effectively owned by your group.
      -N FILE        True if the file has been modified since it was last read.
    
      FILE1 -nt FILE2  True if file1 is newer than file2 (according to
                       modification date).
    
      FILE1 -ot FILE2  True if file1 is older than file2.
    
      FILE1 -ef FILE2  True if file1 is a hard link to file2.

String Operators

The complete list of bash 4.2 string operators:
      -z STRING      True if string is empty.
    
      -n STRING
         STRING      True if string is not empty.
    
      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.
      STRING1 > STRING2
                     True if STRING1 sorts after STRING2 lexicographically.

String Manipulation

${str:position}			# substring starting at position
${str:position:len}		# substring starting at position with length len
${str#ubstring}			# delete shortest match from front
${str##substring}		# delete longest match from front
${str%substring}		# delete shortest match from back
${str%%substring}		# delete longest match from back
${str/pattern/replacement}	# pattern replace
${str/#pattern/replacement}	# pattern replace at front
${str/%pattern/replacement}	# pattern replace at end
${str//pattern/replacement}	# global pattern replace

Arrays

Bash has limited support for associative arrays. Note that indexes must be numbers!
# Declaring associative arrays (since Bash v4)
declare -A arr

# Initializing arrays
arr=("string 1", "string 2", "string 3")
arr=([1]="string 1", [2]="string 2", [3]="string 3")

# Assigning values for indexed arrays
arr[4]="string 4"

# Assigning values for associative arrays
arr["my key"]="my value"

# Accessing the array
${arr[@]}         # Returns all indizes and their items (doesn't work with associative arrays)
${arr[*]}         # Returns all items
${!arr[*]}        # Returns all indizes
${#arr[*]}        # Number elements
${#arr[$n]}       # Length of $nth item

# Pushing to array
arr+=("new string value", "another new value")

Here Document

Bash allow here documents like this
cat <<EOT
[...]
EOT
To disable substitution in the here doc text quote the marker with single or double quotes.
cat <<'EOT'
To strip leading tabs use
cat <<-EOT

Regular Expressions

Regexp Matching

Use conditions with doubled [] and the =~ operator. Ensure not to quote the regular expression. Only BRE are allowed. If the regexp has whitespaces put it in a variable first.
if [[ "$string" =~ ^[0-9]+$ ]]; then 
    echo "Is a number"
fi

Regexp Match Extraction

Variant #1: You can do this with grouping in bash. Despite only BRE being supported grouping works also. Note how you need to set the regexp into a variable because you must not quote it in the if condition!
REGEXP="2013:06:23 ([0-9]+):([0-9]+)"
if [[ "$string" =~ $REGEXP ]]; then
    echo "Hour ${BASH_REMATCH[1]} Minute ${BASH_REMATCH[2]}"
fi
Variant #2: Actually using "expr" can much simpler especially when only on value is to be extracted:
hour=$(expr match "$string" '2013:06:23 \([0-9]\+\)')

Validate IPs

If you need to validate an IP try the following function
function validate_ip {
        local net=$1
        [[ $net =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/[0-9]{1,2}$ ]] || return 1
        [[ ${net#*/} -le 32 ]] || return 1
        local ip=${net%/*}
        local -a oc=(${ip//\./ })
        [[ ${oc[0]} -le 255 && ${oc[1]} -le 255 && ${oc[2]} -le 255 && ${oc[3]} -le 255 ]] || return 1
        return 0
}

Debugging Scripts

For simple tracing add a
set -x
in the script or append the "-x" to the shebang or run the script like this
bash -x <script name>
As "set -x" enables tracing you can disable it with "set +x" again. This allows tracing only a part of the code (e.g. a condition in an inner loop). Additionally to "-x" you may want to set "-v" to see the shell commands that are executed. Combine both to
set -xv

Writing Safer Scripts

Using
set -e
in a script you ensure that you never forget to check an exit code. Because if you do and the command calls returns an exit code != 0 the script just terminates. Of course you can also use it to not write checks if it is ok to just bail out.

Network Connections

# Establish a connection to 91.92.93.94:80 on file handle 4 with
if ! exec 4<> /dev/tcp/91.92.93.94/80; then
	echo "ERROR: Connection failed!"
fi
# Write something
echo -e "GET / HTTP/1.0\n" >&4

# Read something
cat <&4
# Close the socket
exec <&4-
exec >&4-

Simulate Reading From a File

Sometimes you might need to pass a file name when you want to pipe output from a commands. Then you could write to a file first and then used it, but you can also use the ">()" or "<()" operator. This can be used with all tools that demand a file name paramter:
diff <(echo abc;echo def) <(echo abc;echo abc)

History

History Handling

Here are some improvements for the bash history handling:
unset HISTFILE      # Stop logging history in this bash instance
HISTIGNORE="[ ]*"   # Do not log commands with leading spaces
HISTIGNORE="&"      # Do not log a command multiple times
# Change up/down arrow key behaviour to navigate only similar commands
bind '"\e[A":history-search-backward'
bind '"\e[B":history-search-forward'

Adding Timestamps

To add timestamps to your history set the following environment variable:
HISTTIMEFORMAT="%Y-%m-%d %H:%M:%S " # Log with timestamps

Easier History Navigation

If you do not like Ctrl-R to nagivate the history you can define other keys as PgUp and PgDown in /etc/inputrc:
   "\e[5~": history-search-backward
   "\e[6~": history-search-forward

Misc

Command Completion

How to setup your own bash completion schemas. Here is a git example:
complete -W 'add branch checkout clone commit diff grep init log merge mv pull push rebase rm show status tag' git

complete -p    # To list defined completion schemes
Note that the above example propably already comes prepared with your Linux distribution. You might want to check default definitions installed in /etc/bash_completion.d for a good starting point.

Kill all childs on exit

trap true TERM
kill -- -$$

Apply ulimit Changes Instantly

The problem behind this is documented in this blog post but it boils down to try to use the "-i" switch:
sudo -i -u <user>
If it doesn't work you might need to investigate and change the PAM configuration.

PS1: Escape Non-Print Chars

To avoid incorrect line break behaviour when editing the command line you need to escape control characters in PS1 like this:
\[color definition\]
For example:
\[\033[31m\] some text \[\033[0m\]