Bash cheatsheet

From Lolly's Wiki
Jump to navigationJump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.


bash history per user

See Logging the SSH fingerprint

bash prompt

Put this in your ~/.bash_profile

typeset +x PS1="\[\e]0;\u@\h: \w\a\]\u@\h:\w# "

Useful variable substitutions

split

For example split an ip:

$ delimiter="."
$ ip="10.1.2.3"
$ declare -a octets=( ${ip//${delimiter}/ } )
$ echo "${#octets[@]} octets -> ${octets[@]}"
4 octets -> 10 1 2 3

dirname

$ myself=/usr/bin/blafasel ; echo ${myself%/*} 
/usr/bin

basename

$ myself=/usr/bin/blafasel ; echo ${myself##*/} 
blafasel

Path name resolving function

# dir_resolve originally from  http://stackoverflow.com/a/20901614/5887626
# modified at https://lars.timmann.de/wiki/index.php/Bash_cheatsheet
dir_resolve() {
  local dir=${1%/*}
  local file=${1##*/}
  # if the name does not contain a / leave file blank or the name will be name/name
  [ "_${1/\//}_" == "_${1}_" -a -d ${1} ] && file=""
  [ "_${1/\//}_" == "_${1}_" -a -f ${1} ] && dir=""
  pushd "$dir" &>/dev/null || return $? # On error, return error code
  echo ${PWD}${file:+"/"${file}} # output full path with filename
  popd &> /dev/null
}

Arrays

Reverse the order of elements

An example for services in normal and reverse order for start/stop

declare -a SERVICES_STOP=(service1 service2 service3 service4)
declare -a SERVICES_START
for(( i=$[ ${#SERVICES_STOP[*]} - 1 ] ; i>=0 ; i-- ))
do
  SERVICES_START+=(${SERVICES_STOP[$i]})
done

This results in:

$ echo ${SERVICES_STOP[*]} ; echo ${SERVICES_START[*]}
service1 service2 service3 service4
service4 service3 service2 service1


Loops

Numbers

$ for i in {0..9} ; do echo $i ; done

or

$ for ((i=0;i<=9;i++)); do echo $i; done

so gehen natürlich auch andere Sprünge, z.B. immer 3 weiter:

$ for ((i=0;i<=9;i+=3)); do echo $i; done

or or or

$ for ((i=0,j=1;i<=9;i+=3,j++)); do echo "$i $j"; done

Exit controlled loop

Just put your code between while and do and use continue alias : in the loop.

#!/bin/bash
while
  # some code
  (( <your control expression> ))
do
  :
done

For example:

#!/bin/bash

i=1
while
  i=$[ $i + 1 ];
  (( $i < 10 ))
do
  :
done

Functions

Log with timestamp

function printlog () {
# Function:
#   Log things to logfile 
#
# Parameter:
#   1: logfile
#   *: You can call printlog like printf (except the first parameter is the logfile)
#
# OR
#
#   Just pipe things to printlog
#
  local logfile=${1}
  shift
  if [ -n "${*}" ]
  then
    format=${1}
    shift
    printf "%s ${format}" "$(/bin/date '+%Y%m%d %H:%M:%S')" ${*} >> ${logfile}

  else
    while read input
    do
      printf "%s %s\n" "$(/bin/date '+%Y%m%d %H:%M:%S')" "${input}" >> ${logfile}
    done
  fi
}
$ printf "test\n\ntoast\n" | printlog /dev/stdout
20190603 12:48:13 test
20190603 12:48:13 
20190603 12:48:13 toast
$ printlog /dev/stdout "test\n"
20190603 12:48:19 test
$ printlog /dev/stdout "test %s %d %s\n" "bla" 0 "bli"
20190603 12:48:25 test bla 0 bli
$

Calculations

$ echo $[ 3 + 4 ] 
7 
$ echo $[ 2 ** 8 ] # 2^8
256

init scripts

A basic skeleton

#!/bin/bash

NAME=<myname>  # The name of the daemon
USER=<runuser> # The user to run the daemon as

SELF=${0##*/}
CALLER=$(id -nu)

# Check if called as ${USER}
if [ "_${CALLER}_" != "_${USER}_" ]
then
  # If not do a su if called as root
  if [ "_${CALLER}_" == "_root_" ]
  then
    exec su -l ${USER} -c "$0 $@"
  else
    echo "Please start this script only as user ${USER}"
    exit 1
  fi
fi


if [ $# -eq 1 ]
then
  command=$1
else
  # Called as ${NAME}-start.sh or ${NAME}-stop.sh
  command=${SELF%.sh}
  command=${command##${NAME}-}
  [ "_${command}_" == "_${NAME}_" ] && command=""
fi

case ${command} in
start)
  # start commands
  ;;
stop)
  # stop commands
  ;;
restart)
  $0 stop
  $0 start
  ;;
*)
  [ ! -z "${command}" ] && echo "ERROR: Unknown option ${command}!"
  echo "Usage: $0 (start|stop|restart)";
  echo "Or call as ${NAME}-(start|stop|restart).sh"
  exit 1
  ;;
esac

Logging and output in your scripts

Add a timestamp to all output

#!/bin/bash

# Find temp filename
FIFO=$(mktemp)

# Clenup on exit
trap 'rm -f ${FIFO}' 0 

# Delete file created by mktemp
rm "${FIFO}"

# Create a FIFO instead
mkfifo "${FIFO}"

# Read from FIFO and add date at the beginning
sed -e "s|^|$(date '+%d.%m.%Y %H:%M:%S') :: |g" < ${FIFO} &

# Redirect stdout & stderr to FIFO
exec > ${FIFO} 2>&1

#
# Now your program
#
echo bla
echo bli >&2

Add a timestamp to all output and send to file

#!/bin/bash

LOGFILE=/tmp/bla.log

# Find temp filename
FIFO=$(mktemp)

# Clenup on exit
trap 'rm -f ${FIFO}' 0 

# Delete file created by mktemp
rm "${FIFO}"

# Create a FIFO instead
mkfifo "${FIFO}"

# Read from FIFO and add date at the beginning
sed -e "s|^|$(date '+%d.%m.%Y %H:%M:%S') :: |g" < ${FIFO} > ${LOGFILE}&

# Redirect stdout & stderr to FIFO
exec > ${FIFO} 2>&1

#
# Now your program
#
echo bla
echo bli >&2

Parameter parsing

In progress... no time...

while [ $# -gt 0 ]
do
  case $1 in
  -h|--help)
    usage help
    shift;
    exit 0;
    ;;
  --?*=?*|-?*=?*)
    param=${1%=*}
    value=${1#*=}
    shift;
    ;;
  --?*=|-?*=)
    param=${1%=*}
    usage "${param} needs a vlaue!"
    ;;
  *)
    if [ $# -lt 2 ] ; then usage "$1 needs a value!"; fi
    param=$1
    value=$2
    shift; shift;
    ;;
  esac

  case $param in
  *)
    other_params[$[${#other_params[*]} + 1]]="${param}=${value}"
    param=${param#--}
    param=${param/-/_}
    export ${param^^}=${value}
    ;;
  esac
done