Bash cheatsheet: Difference between revisions

From Lolly's Wiki
Jump to navigationJump to search
No edit summary
m (Text replacement - "</source" to "</syntaxhighlight")
 
(9 intermediate revisions by the same user not shown)
Line 1: Line 1:
[[Kategorie:Bash]]
[[Category:Bash]]


=bash history per user=
=bash history per user=
You need to set LogLevel of sshd to VERBOSE in your /etc/ssh/sshd_config:
See [[SSH_FingerprintLogging|Logging the SSH fingerprint]]
<source lang=bash>
...
LogLevel VERBOSE
...
</source>
 
If you are using ssh public keys for authenticating and want to use a seperate history for each user, you can put this in your .bash_profile:
<source lang=bash>
[ -f /var/log/fingerprint.log ] && FINGERPRINT=$(nawk -v ssh_connection="${SSH_CONNECTION}" -v user=${LOGNAME} 'BEGIN{split(ssh_connection,connection)}/.*sshd\[[0-9]+\]: Accepted publickey for/ && $(NF-5)==connection[1] && $(NF-3)==connection[2] {print $NF;}' /var/log/fingerprint.log)
 
export HISTFILE=~/.bash_history_${FINGERPRINT:-${SUDO_USER:-default}}
</source>
If $FINGERPRINT is empty the sudo user will be used.
If $SUDO_USER is empty too, use "default" as extension.
 
I forced rsyslog to write another logfile where group ssh may read:
/etc/rsyslog.d/99-fingerprint.conf:
<source lang=bash>
$FileCreateMode 0640
$FileGroup ssh
auth                /var/log/fingerprint.log
</source>
 
Add user syslog to group ssh so that syslog can open a file as group ssh:
<source lang=bash>
# usermod -aG ssh syslog
</source>
 
Let only users from group ssh login via ssh except the syslog user:
/etc/ssh/sshd_config:
<source lang=bash>
# SSH is only allowed for users in this group
AllowGroups ssh
DenyUsers syslog
</source>


=bash prompt=
=bash prompt=
Put this in your ~/.bash_profile
Put this in your ~/.bash_profile
<source lang=bash>
<syntaxhighlight lang=bash>
typeset +x PS1="\[\e]0;\u@\h: \w\a\]\u@\h:\w# "
typeset +x PS1="\[\e]0;\u@\h: \w\a\]\u@\h:\w# "
</source>
</syntaxhighlight>


=Useful variable substitutions=
=Useful variable substitutions=
==split==
==split==
For example split an ip:
For example split an ip:
<source lang=bash>
<syntaxhighlight lang=bash>
$ delimiter="."
$ delimiter="."
$ ip="10.1.2.3"
$ ip="10.1.2.3"
Line 54: Line 19:
$ echo "${#octets[@]} octets -> ${octets[@]}"
$ echo "${#octets[@]} octets -> ${octets[@]}"
4 octets -> 10 1 2 3
4 octets -> 10 1 2 3
</source>
</syntaxhighlight>


==dirname==
==dirname==
<source lang=bash>
<syntaxhighlight lang=bash>
$ myself=/usr/bin/blafasel ; echo ${myself%/*}  
$ myself=/usr/bin/blafasel ; echo ${myself%/*}  
/usr/bin
/usr/bin
</source>
</syntaxhighlight>
==basename==
==basename==
<source lang=bash>
<syntaxhighlight lang=bash>
$ myself=/usr/bin/blafasel ; echo ${myself##*/}  
$ myself=/usr/bin/blafasel ; echo ${myself##*/}  
blafasel
blafasel
</source>
</syntaxhighlight>
==Path name resolving function==
==Path name resolving function==
<source lang=bash>
<syntaxhighlight lang=bash>
# dir_resolve originally from  http://stackoverflow.com/a/20901614/5887626
# dir_resolve originally from  http://stackoverflow.com/a/20901614/5887626
# modified at https://lars.timmann.de/wiki/index.php/Bash_cheatsheet
# modified at https://lars.timmann.de/wiki/index.php/Bash_cheatsheet
Line 80: Line 45:
   popd &> /dev/null
   popd &> /dev/null
}
}
</source>
</syntaxhighlight>


=Arrays=
=Arrays=
==Reverse the order of elements==
==Reverse the order of elements==
An example for services in normal and reverse order for start/stop
An example for services in normal and reverse order for start/stop
<source lang=bash>
<syntaxhighlight lang=bash>
declare -a SERVICES_STOP=(service1 service2 service3 service4)
declare -a SERVICES_STOP=(service1 service2 service3 service4)
declare -a SERVICES_START
declare -a SERVICES_START
Line 92: Line 57:
   SERVICES_START+=(${SERVICES_STOP[$i]})
   SERVICES_START+=(${SERVICES_STOP[$i]})
done
done
</source>
</syntaxhighlight>
This results in:
This results in:
<source lang=bash>
<syntaxhighlight lang=bash>
$ echo ${SERVICES_STOP[*]} ; echo ${SERVICES_START[*]}
$ echo ${SERVICES_STOP[*]} ; echo ${SERVICES_START[*]}
service1 service2 service3 service4
service1 service2 service3 service4
service4 service3 service2 service1
service4 service3 service2 service1
</source>
</syntaxhighlight>




Line 104: Line 69:
==Numbers==
==Numbers==
  $ for i in {0..9} ; do echo $i ; done
  $ for i in {0..9} ; do echo $i ; done
oder
or
  $ for ((i=0;i<=9;i++)); do echo $i; done
  $ for ((i=0;i<=9;i++)); do echo $i; done
so gehen natürlich auch andere Sprünge, z.B. immer 3 weiter:
so gehen natürlich auch andere Sprünge, z.B. immer 3 weiter:
  $ for ((i=0;i<=9;i+=3)); do echo $i; done
  $ for ((i=0;i<=9;i+=3)); do echo $i; done
oder oder oder
or or or
  $ for ((i=0,j=1;i<=9;i+=3,j++)); do echo "$i $j"; done
  $ for ((i=0,j=1;i<=9;i+=3,j++)); do echo "$i $j"; done


Line 114: Line 79:
Just put your code between <i>while</i> and <i>do</i> and use <i>continue</i> alias : in the loop.
Just put your code between <i>while</i> and <i>do</i> and use <i>continue</i> alias : in the loop.


<source lang=bash>
<syntaxhighlight lang=bash>
#!/bin/bash
#!/bin/bash
while
while
Line 122: Line 87:
   :
   :
done
done
</source>
</syntaxhighlight>


For example:
For example:
<source lang=bash>
<syntaxhighlight lang=bash>
#!/bin/bash
#!/bin/bash


Line 135: Line 100:
   :
   :
done
done
</source>
</syntaxhighlight>


=Functions=
=Functions=
==Log with timestamp==
==Log with timestamp==
<source lang=bash>
<syntaxhighlight lang=bash>
function printlog () {
function printlog () {
   if [ -n "$*" ]
# 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
   then
     printf "%s %s\n" "$(/bin/date '+%Y%m%d %H:%M:%S')" "${*}"
    format=${1}
    shift
     printf "%s ${format}" "$(/bin/date '+%Y%m%d %H:%M:%S')" ${*} >> ${logfile}
 
   else
   else
     while read input
     while read input
     do
     do
       printf "%s %s\n" "$(/bin/date '+%Y%m%d %H:%M:%S')" "${input}"
       printf "%s %s\n" "$(/bin/date '+%Y%m%d %H:%M:%S')" "${input}" >> ${logfile}
     done
     done
   fi
   fi
}
}
</source>
</syntaxhighlight>
 
<source lang=bash>
$ printf "test\n\ntoast\n" | printlog
20161103 10:47:25 test
20161103 10:47:25
20161103 10:47:25 toast
$ printlog test
20161103 10:47:30 test
</source>


<syntaxhighlight lang=bash>
$ 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
$
</syntaxhighlight>


=Calculations=
=Calculations=
$ echo $[ 3 + 4 ]
<syntaxhighlight lang=bash>
$ echo $[ 2 ** 8 ] # 2^8
$ echo $[ 3 + 4 ]  
7
$ echo $[ 2 ** 8 ] # 2^8
256
</syntaxhighlight>


=init scripts=
=init scripts=
==A basic skeleton==
==A basic skeleton==
<source lang=bash>
<syntaxhighlight lang=bash>
#!/bin/bash
#!/bin/bash


Line 220: Line 207:
   ;;
   ;;
esac
esac
</source>
</syntaxhighlight>


= Logging and output in your scripts =
= Logging and output in your scripts =
== Add a timestamp to all output ==
== Add a timestamp to all output ==
<source lang=bash>
<syntaxhighlight lang=bash>
#!/bin/bash
#!/bin/bash


Line 250: Line 237:
echo bla
echo bla
echo bli >&2
echo bli >&2
</source>
</syntaxhighlight>


== Add a timestamp to all output and send to file==
== Add a timestamp to all output and send to file==
<source lang=bash>
<syntaxhighlight lang=bash>
#!/bin/bash
#!/bin/bash


Line 281: Line 268:
echo bla
echo bla
echo bli >&2
echo bli >&2
</source>
</syntaxhighlight>


=Parameter parsing=
=Parameter parsing=
<source lang=bash>
 
In progress... no time...
<syntaxhighlight lang=bash>
while [ $# -gt 0 ]
while [ $# -gt 0 ]
do
do
  #if [ $# -ge 2 ]; then value=$2; fi
   case $1 in
   case $1 in
   -h|--help)
   -h|--help)
Line 320: Line 308:
   esac
   esac
done
done
</source>
</syntaxhighlight>

Latest revision as of 03:56, 26 November 2021


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