Table of Contents

BASH - Cheat Sheet

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

Parameter Substituion

${str-default}             # set 'default' if not set
${str:-default}            # set 'default' if not set (even if declared null)
 
${str=default}             # set 'default' if not set
${str:=default}            # set 'default' if not set (even if declared null)
 
${str+value}               # set 'value' if $str is set, otherwise set null
${str:+value}              # set 'value' if $str is set (even if declared null), otherwise set null
 
${str?error}               # abort with 'error' if not set
${str:?error}              # abort with 'error' if not set (and not null)

Arrays

Indexed arrays require no declaration

arr=("string 1", "string 2", "string 3")
arr=([1]="string 1", [2]="string 2", [3]="string 3")
 
arr[4]="string 4"

Check below under “Hashes” for accessing the different properties of an array.


Hashes

Since Bash v4

# Hashes need declaration!
declare -A arr
 
# Assigning values to associative arrays
arr[my key]="my value"
arr["my key"]="my value"
arr[$my_key]="my value"
 
# Fetching values
echo ${arr[my key]}
echo ${arr["my key"]}
echo ${arr[$my_key]}
 
# 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 Documents

Bash allows 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

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

It is a good practice to use

set -euo pipefail

which ensures


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-

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.

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 parameter:

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 behavior to navigate only similar commands
bind '"\e[A":history-search-backward'
bind '"\e[B":history-search-forward'
bind '"\e[C":forward-char'
bind '"\e[D":backward-char'

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 navigate the history you can define other keys as PgUp and PgDown in /etc/inputrc:

   "\e[5~": history-search-backward
   "\e[6~": history-search-forward

History Hardening

For a secure bash configuration add the following settings to your global/users bashrc

HISTIGNORE=""
HISTCONTROL=""
HISTTIMEFORMAT='%Y-%m-%d %H:%M:%S '
HISTFILE=~/.bash_history
HISTFILESIZE=2000
readonly HISTFILE
readonly HISTSIZE
readonly HISTFILESIZE
readonly HISTIGNORE
readonly HISTCONTROL
readonly HISTTIMEFORMAT
shopt -s histappend

and finally mark the history file append only:

chattr +a $HISTFILE

Misc

Waiting for child processes

See: https://spin.atomicobject.com/2017/08/24/start-stop-bash-background-process/

trap "exit" INT TERM ERR
trap "kill 0" EXIT
 
background_something &
 
wait

Kill childs on exit

trap 'kill $(jobs -p)' EXIT

Command Completion

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: This example probably 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

Try to use the “-i” switch:

sudo -i -u <user>

NOTE: If it does not 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\]