Skip to content

Bash

#!/bin/bash

set -e
set -o pipefail

# https://stackoverflow.com/a/246128/1061279
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
SCRIPT_NAME=$(basename "${0}")
ROOT_DIR="$(git rev-parse --show-toplevel)"
readonly DIR
readonly SCRIPT_NAME
readonly ROOT_DIR

Files

Delete all files matching a pattern in sub folders

find . -name "*.lock" -type f -delete

Clear an already existing file

>|file.txt

Search root for the name

sudo find / -name file.txt

Create a new blank file

touch file.txt

Get file size in bytes

wc -c file.txt

Get the size in bytes of a compressed file

gzip -c file.txt | wc -c
bzip2 -c file.txt | wc -c
tar -cf - file.txt | wc -c

Validate JSON

cat foo.json | jq empty
parse error: Expected separator between values at line 154, column 30

Remove File Extension

name=$(echo "$filename" | cut -f 1 -d '.')
# or
echo "${filename%%.*}"

Extract filename and extension in Bash

~% FILE="example.tar.gz"

~% echo "${FILE%%.*}"
example

~% echo "${FILE%.*}"
example.tar

~% echo "${FILE#*.}"
tar.gz

~% echo "${FILE##*.}"
gz

Full File Path

readlink -f file.txt

Checks

# Check if chart dir exists
if [ -d "${CHART_PATH}" ]; then
  echo "Chart path already exists, ${CHART_PATH}"
  exit 1
fi

# Check if i2c-tools is installed
if ! command -v git &> /dev/null; then
    echo "git is not installed"
    exit 1
fi

Check if substring is in string

string='My long string'
if [[ $string == *"My long"* ]]; then
  echo "It's there!"
fi

Check empty variable

if [ -z "${REPOSITORY}" ]; then
  echo "Could not get the repository"
  exit 1
fi

Single line checks

# Check is variable is null
function is_null {
  [ -z "$1" ]
}

# Check if directory exists
function dir_exists(){
  [ -d "${1}" ]
}

# Check if command exists
function command_exists(){
  command -v "${1}" &> /dev/null
}

is_null "$left" && echo "is null"

String manipulation

Get variable from a command
VAR=$(basename "/tmp/file.txt")
Get last character
VAR="${VAR: -1}"
Remove last character
VAR="${VAR::-1}"

Get domain without the .com

http://user:[email protected]:80/some/path/url?data&and#hash -> example

# https://unix.stackexchange.com/a/428990/93726
echo "http://user:[email protected]:80/some/path/url?data&and#hash" | sed -e "s/[^/]*\/\/\([^@]*@\)\?\([^:/]*\).*/\2/" | sed "s/^www\.//" | cut -f 1 -d '.'
Get first word only
s=${var%% *}
Replace space with dash in var
s="${var// /-}"
To lower
s="${var,,}"
Get extension
s="${var#*.}"
Get last part of extension
s="${var##*.}"
Remove extension
s="${var%%.*}"
get the file and all but last part of file extension
s="${var%.*}"

Checksums

Get checksum of remote file

wget -qO- https://github.com/nicholaswilde/helm-template/archive/main.zip | sha256sum
echo "62df608caba8f2591755f99efac0097c3d7acf313e237e328aa2c046d500efd1  main.zip" | sha256sum -c

Scripts

Get single options, -v, -h, etc.

# https://www.jamescoyle.net/how-to/1774-bash-getops-example
# https://opensource.com/article/19/12/help-bash-program
# Get the options
while getopts ":hv" o; do
  case "${o}" in
    h) # display Help
      help
      exit 0;;
    v)
      printf "${SCRIPT_NAME} version ${APP_VERSION}\n"
      exit 0;;
    \?) # incorrect option
      usageerror;;
  esac
done

# https://unix.stackexchange.com/a/214151/93726
shift "$((OPTIND-1))"

Miscellaneous

printf

printf "%s is the value" "${var}"

# Expand the tab, \t or new line \n
var="value\t"
printf "%b is the value" "${var}"

Insert first line of file

sed  -i '1i text' filename

Delete a tmp dir on exit

# https://stackoverflow.com/a/687052/1061279
trap 'rm -rf -- "$TMP_DIR"' EXIT

Download and extract file in one line. Works with tar & zip files.

wget -qO- https://github.com/nicholaswilde/helm-template/archive/main.zip | bsdtar -xvf-

Make variable global from inside function

eval VAR="value"

Compare semver

# https://stackoverflow.com/a/4025065/1061279
function vercomp () {
  if [[ $1 == $2 ]]; then
    return 0
  fi
  local IFS=.
  local i ver1=($1) ver2=($2)
  # fill empty fields in ver1 with zeros
  for ((i=${#ver1[@]}; i<${#ver2[@]}; i++)); do
    ver1[i]=0
  done
  for ((i=0; i<${#ver1[@]}; i++)); do
    if [[ -z ${ver2[i]} ]]; then
      # fill empty fields in ver2 with zeros
      ver2[i]=0
    fi
    if ((10#${ver1[i]} > 10#${ver2[i]})); then
      return 1
    fi
    if ((10#${ver1[i]} < 10#${ver2[i]})); then
      return 2
    fi
  done
  return 0
}

function testvercomp () {
  vercomp $1 $2
  case $? in
    0) op='=';;
    1) op='>';;
    2) op='<';;
  esac
  if [[ $op != $3 ]]; then
    echo "The minimum required version of git is $2"
    exit 1
  fi
}

testvercomp ${GIT_VER} ${MIN_VER} '>'

Generate Random String

cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 64 | head -n 1

Function Library

#!/bin/bash
# Load the  myfunctions.sh 
# My local path is /home/vivek/lsst2/myfunctions.sh
. /home/vivek/lsst2/myfunctions.sh

# Define local variables
# var1 is not visitable or used by myfunctions.sh
var1="The Mahabharata is the longest and, arguably, one of the greatest epic poems in any language."

# Invoke the is_root()
is_root && echo "You are logged in as root." || echo "You are not logged in as root."

# Find out if user account vivek exits or not
is_user_exits "vivek" && echo "Account found." || echo "Account not found."

# Display $var1
echo -e "*** Orignal quote: \n${var1}"

# Invoke the to_lower()
# Pass $var1 as arg to to_lower()
# Use command substitution inside echo
echo -e "*** Lowercase version: \n$(to_lower ${var1})"

Run As Different User

sudo -u "${TARGET_USER}" bash <<"EOF9"
  command
EOF9

Return Value from Function

function myfunc(){
    local  myresult='some value'
    echo "$myresult"
}

result=$(myfunc)   # or result=`myfunc`
echo $result

Sort Semver Using Sort

printf "1.0\n2.0\n2.12\n2.10\n1.2\n1.10" | sort -t "." -k1,1n -k2,2n -k3,3n
1.0
1.2
1.10
2.0
2.10
2.12

ID (Debian, Ubuntu)

echo $(. /etc/os-release && echo $ID)

Arch

Subs aarch64 with arm64, x86_64 with amd64, and armv7l and armv6l with arm

ARCH=$(uname -m | sed -e 's/x86_64/amd64/' -e 's/\(arm\)\(64\)\?.*/\1\2/' -e 's/aarch64$/arm64/')
echo $ARCH

Subs aarch64 with arm64, x86_64 with amd64, and armv7l and armv6l with armhf

ARCH=$(uname -m | sed -e 's/x86_64/amd64/' -e 's/\(arm\)\(64\)\?.*/\1\2hf/' -e 's/aarch64$/arm64/')
echo $ARCH

Check if system needs to be restarted

sudo needrestart

We use the systemctl command as follows to restart services one-by-one:

sudo systemctl restart nginx
sudo systemctl restart firewalld

We can use bash for loop as follows:

for s in systemd-udevd  firewalld  polkit  sshd nginx; do
  sudo systemctl restart "$s"
done

How to restart systemd with PID # 1 without rebooting Linux box

sudo systemctl daemon-reexec

And verify it again:

sudo lsof | grep 'DEL.*lib' | cut -f 1 -d ' ' | sort -u

Check Array for Value

if [[ " ${array[*]} " =~ " ${value} " ]]; then
    # whatever you want to do when array contains value
fi

if [[ ! " ${array[*]} " =~ " ${value} " ]]; then
    # whatever you want to do when array doesn't contain value
fi

Search for string in files

grep -R <stringToSearch> <dirName>

If you want to get number of occurrences use wc -l as pipe

grep -R "text" . | wc -l
bold=$(tput bold)
normal=$(tput sgr0)
blue=$(tput setaf 4)
echo "this is ${bold}bold${normal} but this isn't"
Print all colors
for c in {0..255}; do tput setaf $c; tput setaf $c | \cat -v; echo =$c; done | column

See here for colors.

Bypass Alias

A simple directive which disables all aliases and functions for the command immediately following it. Shortcut for the bash built-in 'command' - "command linefoo".

\foo

Push your present working directory to a stack that you can pop later

Add directories to stack
pushd /tmp

```shell titl="Remove directory from stack" popd

## Run script in subshell

```shell
sudo -u "${TARGET_USER}" bash <<"EOF"
    cd "$HOME"
    mkdir -p "${HOME}/git/nicholaswilde/"
    git clone https://github.com/nicholaswilde/dotfiles.git "${HOME}/git/nicholaswilde/dotfiles"
    cd "${HOME}/git/nicholaswilde/dotfiles"
    # set the correct origin
    git remote set-url origin [email protected]:nicholaswilde/dotfiles.git
    # installs all the things
    make
EOF

Get IPv4

grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}'

Show Environmental Variables

printenv
printenv | less
printenv | more

Show Shell Functions

declare -F | awk '{print $3}' | grep -v '^_'

Show Shell Aliases

alias

Replace Tabs with Spaces

sed -i 's/\t/     /g' tab-file.txt

Git Status

git diff --quiet; nochanges=$?
if [ $nochanges -eq 0 ]; then
    # there are no changes
else
    # there are changes
fi

Alternatively, if you don't need to store the exit status in a variable, you can do:

if git diff --quiet; then
    # there are no changes
else
    # there are changes
fi

Since git diff is a porcelain Git command and you want to do things programmatically, you should probably use the plumbing Git command called git diff-index instead (which also has a --quiet flag, but which must be supplied a tree-ish argument):

if git diff-index --quiet HEAD; then
    # there are no changes
else
    # there are changes
fi

As pointed out in a comment below, the approach outlined above does not cover untracked files. To cover them as well, you can use the following instead:

if [ -z "$(git status --porcelain)" ]; then
    # there are no changes
else
    # there are changes
fi

Relative Path

Using realpath from GNU coreutils 8.23 is the simplest, I think:

realpath --relative-to="$file1" "$file2"

For example:

realpath --relative-to=/usr/bin/nmap /tmp/testing
../../../tmp/testing

Get Between Patterns

aaa
bbb
pattern1
aaa pattern2
bbb
ccc
pattern2
ddd
eee
pattern1
fff
ggg
sed -n '/^pattern1/,/^pattern2/{p;/^pattern2/q}'
sed -n '/^pattern1/,${p;/^pattern2/q}'

Test if option is set

If you do set -f, or otherwise disable globbing:, $- will contain f:

$ echo $-
himBHs
$ set -f
$ echo $-
fhimBHs
$ bash -fc 'echo $-'
fhBc

So:

[[ $- = *f* ]]

Or:

case $- in
 *f*)  ... ;;
esac

References