Skip to content

Instantly share code, notes, and snippets.

@bhavanki
Created November 4, 2021 21:33
Show Gist options
  • Save bhavanki/be38d60d27f8cd484c3481e034e3f698 to your computer and use it in GitHub Desktop.
Save bhavanki/be38d60d27f8cd484c3481e034e3f698 to your computer and use it in GitHub Desktop.
My cheat sheet for helpful Bashisms

Read the lines output from a command into an array variable

readarray -t var < <(command)
var=()
while IFS= read -r line; do
  var+=( "$line" )
done < <(command)

Read the single line output from a command into an array variable

IFS=" " read -r -a var <<< "$(command)"

Read the lines of a file into an array variable

readarray -t var < /path/to/file
var=()
while IFS= read -r line; do
  var+=( "$line" )
done < /path/to/file

Feed string contents to a command (here string)

bc <<< "scale=4;$used/$available"

Parameter substitution

https://www.tldp.org/LDP/abs/html/parameter-substitution.html

${name}          # the value
${name-default}  # the value if set, or else default
${name:-default} # the value if set and non-empty, or else default
${name=default}  # the value if set, or else default and assign it
${name:=default} # the value if set and non-empty, or else default
                 # and assign it
${name+alt}      # alt if set, or else empty
${name:+alt}     # alt if set and non-empty, or else default
${name?msg}      # the value if set, or else echo msg and exit 1
${name:?msg}     # the value if set and non-empty, or else echo msg
                 # and exit 1

${#name}         # length of value or first element in array
${name#pattern}  # value with shortest pattern removed from front
${name##pattern} # value with longest pattern removed from front
${name%pattern}  # value with shortest pattern removed from back
${name%%pattern} # value with longest pattern removed from back

${name:pos}      # value starting at offset pos
${name:pos:len}  # value starting at offset pos for length len
${name/pat/rep}  # value replacing first pat with rep
${name//pat/rep} # value replacing all pat with rep
${name/#pat/rep} # value replacing prefix pat with rep
${name/%pat/rep} # value replacing suffix pat with rep

${!name}         # value of variable named ${name} (indirect)
${!n*}           # names of variables starting with n
${!n@}           # same

Fonts

Colors: https://www.shellhacks.com/bash-colors/

RED="\033[0;31m"
DEFAULT="\033[0m"
echo -e "${RED}Red text${DEFAULT}"

Font decorations:

  • 0 = normal
  • 1 = bold
  • 4 = underlined
  • 5 = blinking
  • 7 = reverse video

Colors (fg=3x, bg=4x):

  • 30 = black -> dark gray (bolded)
  • 31 = red -> light red
  • 32 = green -> light green
  • 33 = brown -> yellow
  • 34 = blue -> light blue
  • 35 = purple -> light purple
  • 36 = cyan -> light cyan
  • 37 = light gray -> white

Define a multiline string

read -r -d '' VAR <<EOF
contents
of
string
EOF

When set -e is on, read returns 1 here and will exit the script.

Read from the command line into a variable

read -p 'Enter the thing: ' var
echo "You entered $var"
  • -r causes backslashes to be literal - shellcheck wants it
  • -s stops echoes
  • -n x returns after x characters automatically

Get yes/no from the command line

read -p "Did you run all relevant unit tests? [y|n] " -n 1 -r < /dev/tty
echo
if echo "$REPLY" | grep -v -E '^[Yy]$' > /dev/null; then
    echo "You said no"
else
    echo "You said yes"
fi

Write long string to a file

cat >> path/to/file <<EOF
file
contents
here
EOF

Write long string from file to file, with string interpolation

eval "cat <<EOF
$(<path/to/fromfile)
EOF
" > path/to/tofile

Associative arrays (bash 4+)

declare -A aa
aa[foo]=bar
aa=([foo]=bar ...)
aa+=([baz]=blat ...)

echo ${aa[foo]}
for i in "${!aa[@]}" # each key
do
  echo "key  : $i"
  echo "value: ${aa[$i]}"
done
if [ ${aa[foo]+_} ]; then echo foo is there; fi # param expansion
if [[ -z ${aa[foo]:+_} ]]; then echo foo is not there; fi
echo Size is "${#aa[@]}"

unset aa[foo]

Temporary files

For a directory

WORKDIR=$(mktemp -d)
trap "rm -rf "'"'"${WORKDIR}"'"' EXIT

For a file

WORKFILE=$(mktemp -t prefix)
trap "rm -f "'"'"${WORKFILE}"'"' EXIT

Regex matching

[[ $string =~ tar\.gz$ ]] && echo "It's a tarball"

man 3 regex gives you the full syntax. So you can use [[:space:]] for whitespace, for example.

Or you can use globs with ==

[[ $string == *tar.gz ]] && echo "It's a tarball"

Get directory containing script

DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"

Check if array contains a value

See https://stackoverflow.com/questions/3685970 for alternatives when elements have spaces in them.

if [[ " ${array[@]} " =~ " ${value} " ]]; then
    # whatever you want to do when array contains value
fi
contains() {
  local el
  local target="$1"
  shift
  for el; do
    if [[ "$el" == "$target" ]]; then
      return 0
    fi
  done
  return 1
}

Split a string into a list

IFS=',' read -ra MY_ARR <<< "this,that,other"

Sort an array

readarray -t sorted < <(printf '%s\n' "${array[@]}" | sort)
readarray -t sorted < <(printf '%s\0' "${array[@]}" | sort -z | xargs -0n1) # to cope with newlines

How to check for the presence of a command

if ! command -v xyz; then
    fail
fi

Can use hash for regular commands, type for built-ins and keywords

Conditional expressions

  • -a file = file exists
  • -b file = file exists and is block special
  • -c file = file exists and is character special
  • -d file = file exists and is directory
  • -e file = file exists
  • -f file = file exists and is regular
  • -g file = file exists and has set-group-id bit set
  • -h file = file exists and is symlink
  • -k file = file exists and has sticky bit set
  • -p file = file exists and is named pipe / fifo
  • -r file = file exists and is readable
  • -s file = file exists and has size greater than zero
  • -t fd = fd is open and refers to terminal
  • -u file = file exists and has set-user-id bit set
  • -w file = file exists and is writable
  • -x file = file exists and is executable
  • -G file = file exists and owned by effective group id
  • -L file = file exists and is symlink
  • -N file = file exists and was modified since last read
  • -O file = file exists and owned by effective user id
  • -S file = file exists and is socket
  • file1 -ef file2 = refer to same device and inode
  • file1 -nt file2 = file1 newer than file2, or file1 exists and file2 doesn't
  • file1 -ot file2 = file1 older than file2, or file2 exists and file1 doesn't
  • -o optname = shell option enabled
  • -v varname = shell variable set
  • -R varname = shell variable set to name reference
  • -z string = string has zero length
  • -n string = string has non-zero length
  • string = string has non-zero length
  • string1 == string 2 = strings are equal
  • string1 = string2 = strings are equal
  • string1 != string2 = strings are not equal
  • string1 < string2 = string1 lexicographically before
  • string1 > string2 = string1 lexicographically after
  • int1 [-eq|-ne|-lt|-le|-gt|-ge] int2 = int comparison
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment