Bash cheatsheet: Difference between revisions
From Lolly's Wiki
Jump to navigationJump to search
No edit summary |
m (Text replacement - "</source" to "</syntaxhighlight") |
||
(29 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
= | [[Category:Bash]] | ||
=bash history per user= | |||
See [[SSH_FingerprintLogging|Logging the SSH fingerprint]] | |||
=bash prompt= | |||
Put this in your ~/.bash_profile | |||
<syntaxhighlight lang=bash> | |||
typeset +x PS1="\[\e]0;\u@\h: \w\a\]\u@\h:\w# " | |||
</syntaxhighlight> | |||
=Useful variable substitutions= | |||
==split== | |||
For example split an ip: | |||
<syntaxhighlight lang=bash> | |||
$ delimiter="." | |||
$ ip="10.1.2.3" | |||
$ declare -a octets=( ${ip//${delimiter}/ } ) | |||
$ echo "${#octets[@]} octets -> ${octets[@]}" | |||
4 octets -> 10 1 2 3 | |||
</syntaxhighlight> | |||
==dirname== | ==dirname== | ||
< | <syntaxhighlight lang=bash> | ||
$ myself=/usr/bin/blafasel ; echo ${myself%/*} | $ myself=/usr/bin/blafasel ; echo ${myself%/*} | ||
/usr/bin | /usr/bin | ||
</ | </syntaxhighlight> | ||
==basename== | ==basename== | ||
< | <syntaxhighlight lang=bash> | ||
$ myself=/usr/bin/blafasel ; echo ${myself##*/} | $ myself=/usr/bin/blafasel ; echo ${myself##*/} | ||
blafasel | blafasel | ||
</ | </syntaxhighlight> | ||
= | ==Path name resolving function== | ||
== | <syntaxhighlight lang=bash> | ||
# 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 | |||
} | |||
</syntaxhighlight> | |||
=Arrays= | |||
==Reverse the order of elements== | |||
An example for services in normal and reverse order for start/stop | |||
<syntaxhighlight lang=bash> | |||
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 | |||
</syntaxhighlight> | |||
This results in: | |||
<syntaxhighlight lang=bash> | |||
$ echo ${SERVICES_STOP[*]} ; echo ${SERVICES_START[*]} | |||
service1 service2 service3 service4 | |||
service4 service3 service2 service1 | |||
</syntaxhighlight> | |||
=Loops= | |||
==Numbers== | |||
$ for i in {0..9} ; do echo $i ; done | $ for i in {0..9} ; do echo $i ; done | ||
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 | ||
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 | ||
[[ | ==Exit controlled loop== | ||
Just put your code between <i>while</i> and <i>do</i> and use <i>continue</i> alias : in the loop. | |||
<syntaxhighlight lang=bash> | |||
#!/bin/bash | |||
while | |||
# some code | |||
(( <your control expression> )) | |||
do | |||
: | |||
done | |||
</syntaxhighlight> | |||
For example: | |||
<syntaxhighlight lang=bash> | |||
#!/bin/bash | |||
i=1 | |||
while | |||
i=$[ $i + 1 ]; | |||
(( $i < 10 )) | |||
do | |||
: | |||
done | |||
</syntaxhighlight> | |||
=Functions= | |||
==Log with timestamp== | |||
<syntaxhighlight lang=bash> | |||
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 | |||
} | |||
</syntaxhighlight> | |||
<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= | |||
<syntaxhighlight lang=bash> | |||
$ echo $[ 3 + 4 ] | |||
7 | |||
$ echo $[ 2 ** 8 ] # 2^8 | |||
256 | |||
</syntaxhighlight> | |||
=init scripts= | |||
==A basic skeleton== | |||
<syntaxhighlight lang=bash> | |||
#!/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 | |||
</syntaxhighlight> | |||
= Logging and output in your scripts = | |||
== Add a timestamp to all output == | |||
<syntaxhighlight lang=bash> | |||
#!/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 | |||
</syntaxhighlight> | |||
== Add a timestamp to all output and send to file== | |||
<syntaxhighlight lang=bash> | |||
#!/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 | |||
</syntaxhighlight> | |||
=Parameter parsing= | |||
In progress... no time... | |||
<syntaxhighlight lang=bash> | |||
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 | |||
</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