Bash Basics

basename & DIRNAME

basename "$FILE"
filename="$(basename $FILE)"
path="$(dirname $FILE)"

BOOLEAN

Bash doesn’t have built-in true and false boolean values that can be used to test for validity.  It’s often just as clear to use 0 or 1 for false and true, respectively.
“For interactive use, like one-liners, make sure to leave a space after !, or it will do history expansion. ((! foo)) works, so does ! ((foo)). ((foo || bar)) works as expected”[1].  Also, remember that bash considers an exit code of 0 success while an exit code of 1 is failure.  If you are working with true being equal to 1 and false being equal to 0 your may have to adjust some bash-y expectations.

#!/bin/bash

flag=0
if (( flag )); then
  echo "Condition is true";
fi

false=0
true=1

((false)) && echo false
((true)) && echo true
((!false)) && echo not false
((!true)) && echo not true

(Source: stackoverflow.com)


conditionals

if...then...elif...else...fi

if TEST-COMMAND1; then
  STATEMENTS1
elif TEST-COMMAND2; then
  STATEMENTS2
elif [ $test_var -ge 3 -a $test_var -lt 11 ]; then
  STATEMENTS3
else 
  STATEMENTS4
fi

FIND

Find files that have been changed in the last 24 hours.

find /var/lib/ -mtime -1 -ls

-1 means anything changed one day or less ago.  +1 means anything that has changed at least one day ago.  Having only 1 means exactly one day ago.

(Source: stackoverflow.com)


LOOPS

Loop through a list of files.

while read patch; do
  patch -p1 -f < ${patch} &>> $PATCH_LOG
done < <(ls -l ${PATCH_SUBDIR}/*.patch)

parameter expansions (substitutions)

For even more reference, see the holy grail of bash PEs.

Replace a character in a string with another character.

$ VER=4.19.87
$ NEWVER="${VER//./_}"
$ echo "$NEWVER"
4_19_87

PATHS

When you experience odd behavior with finding or not finding an installed or uninstalled program, you may need to refresh your remembered locations.

hash -r

See the following references: man hash | pip3 is looking for a wrong path | what does hash -r command do? | what is the purpose of the hash command


PRINTING

printf an exclamation mark.

Option 1: use single-quoted strings.

printf 'Unable to find config file.  Exiting!'

Option 2: add to the end of the string, outside of the quotes

printf "Unable to find config file.  Exiting" !

Option 3: print ASCII representation of ! character

printf "\041"

Option 4: Mix double quotes with single quotes (extension to Option 1)

printf "Unable to find config file.  Exiting"'!'" (that prints an exclamation mark just fine.\n"

Option 5: use a format string

printf "This (%s) is an exclamation mark%s\n" ! !

printf Two Variables
printf "%s %s" % "${MYSTRING1}" "${MYSTRING2}"

 


sequence

for i in $(seq 1 $END); do printf "$i\n"; done

 


set

The Set Built-in

Unless specifically required to not be, the second line of all Bash scripts ought to be: set -euf -o pipefail

Echo on for single command
set -x
ls $mydir
set +x

NOTE: In Jenkins, set -x is the default.  This echoes all commands.  Override by putting the following at the top of the “Execute Shell” build step:

#!/bin/bash +x

Strings

Change Case – Lower/upper

(I do not use “downcase” – it’s “lower case – that word needs to be exonerated from its duty of describing a letter case.)

Entire Word

I’m not even going to try to out-do this answer.

Though I will drop it here for quick reference:

$ myvar="True"
$ echo "$myvar" | tr '[:upper:]' '[:lower:]'
true

First Letter

foo="$(tr '[:lower:]' '[:upper:]' <<< ${foo:0:1})${foo:1}"
String Substitution

Again, great post on StackOverflow.  Dropping it here for quick reference.

#!/bin/bash
firstString="I love Suzi and Marry"
secondString="Sara"
echo "${firstString/Suzi/$secondString}" 
# prints 'I love Sara and Marry'

Style guide

Formatting

shfmt

$ shfmt -w -i 4 -sr <file(s) or path...>

How-to

man page
TLDP BASH Programming How-To
TLDP Advanced Bash-Scripting Guide
Google’s Shell Style Guide

Linter

shellcheck

Tests

-f Check if a file exists

FILE=/tmp/myfile.txt
if [[ -f "$FILE" ]]; then
  echo "$FILE exists"
fi

-z Check if a variable is empty

if [ -z "$var" ]; then
  printf '$var is empty\n';
else
  printf '$var is not empty\n';
fi

SUDO

One-liner including password

NOTE: Do not use this if you do not want others to see your script or bash history, which includes your user’s sudo password.

Use case: SSH to a Clonezilla Server that is already running and already has a specific user created on it.  This user can SSH but the files on the Clonezilla live system are read-only so you can not edit /etc/sudoers file to specify NOPASSWD for your user.

$ echo <password> | sudo -S <command>

Trickster: to avoid exposing the password on the command history, start the command with a SPACE character.

Check for sudo in your shell scripts
if [ $(id -u) -eq 0 ]; then
  printf "You are root\n"
else
  printf "You are not root\n"
fi

trap

For easier cleanup, use trap

 


Variables

Default Value
MyVar="${DEPLOY_ENV:-default_value}"