Shebang
#!/bin/bash
set
set -o noclobber to enable noclobber
set +o to disable noclobber
set -f enable disble file globbing
set +f disable disble file globbing
set -x : show the command
$ pwd
+ pwd
/home/chang
Declare
-p display the attributes and value of each NAME (print out)
-a to make NAMEs indexed arrays (if supported)
-A to make NAMEs associative arrays (if supported)
-i to make NAMEs have the `integer' attribute
-l to convert the value of each NAME to lower case on assignment
-n make NAME a reference to the variable named by its value
-r to make NAMEs readonly
-t to make NAMEs have the `trace' attribute
-u to convert the value of each NAME to upper case on assignment
-x to make NAMEs export
declare -r var1=1 # Read only
echo var1 = $var1 # var1 = 1
(( var1++ )) # x.sh: line 4: var1: readonly variable
declare -i number # integer
# The script will treat subsequent occurrences of number as an integer.
number=3
echo Number = $number # Number = 3
number=three
echo Number = $number # Number = 0
# Tries to evaluate the string three as an integer.
n=6/3
echo n = $n # n = 6/3
declare -i n
n=6/3
echo n = $n # n = 2
declare s=string # default string
declare -i int # integer
declare -a nums # Array
declare -f function_name # Function
declare -x var3=373 # Export to Env variable
Variable Default
(:-) : default value is returned when the vaiable is null or the variable is unset
(-): default value is returned when the variable is unset
set username
echo ${#username} # 0
echo ${username:-bob} # bob
unset username
echo $(username-bob} #bob
Variables
HOST=$(hostname)
# User executing the script:
CURRENTUSER=$(whoami)
# Current date:
CURRENTDATE=$(date +%F)
# Host IP address:
IPADDRESS=$(hostname -I | cut -d ' ' -f1)
# SHOW MESSAGES
echo Today is $CURRENTDATE
echo Hostname: $HOST ($IPADDRESS)
echo User info for $CURRENTUSER:
grep $CURRENTUSER /etc/passwd
v=9
echo $((v + 1))
Array and Dictionary
declare -a name
name[0]=bob
name[1]=smaith
echo ${name[0]}
unset name
declare -A name # dictionary
name=([first]=bob [last]=smith)
echo ${name[first]}
Arithmetic Evaluation
echo $((3*5))
a=$((3+5))
echo $a
r1=3; r2=2
((r1 > r2)) && echo OK
Conditional Statements for Shell Scripts
#!/bin/bash
CURRENTDAYOFTHEMONTH=$(date +%d)
if [ $CURRENTDAYOFTHEMONTH -le 10 ]; then
echo We are within the first 10 days of the month;
elif [ $CURRENTDAYOFTHEMONTH -le 20 ]; then
echo We are within the first 20 days of the month;
else
echo We are within the last 10 days of the month;
fi
The For Loop
for FILE in file1.txt file2.txt file3.txt; do
chmod 640 $FILE
done
for FILE in $(ls -1 | grep file)
do
chmod 640 $FILE
done
The While Loop
while read LINE; do
USERNAME=$(echo $LINE | cut -d':' -f 1)
USERID=$(echo $LINE | cut -d':' -f 3)
echo The UID of $USERNAME is $USERID
done < /etc/passwd
RANDOMNUM=$(shuf -i1-10 -n1)
NUMBER=0
while [ $NUMBER != $RANDOMNUM ]
do
read -p Enter a number between 1 and 10: NUMBER
done
echo Congratulations! Your guess was right!
single vs double blackets
[[ … ]] works only in the Korn Bash, Zsh. extension of [].
http://mywiki.wooledge.org/BashFAQ/031
[[ a = a && b = b ]] vs [ a = a && b = b ]: syntax error ==> [ a = a ] && [ b = b ]
[[ (a = a || a = b) ]] vs [ ( a = a ) ]: syntax error, () is interpreted as a subshell
[[ a < b ]]: lexicographical comparison vs [ a < b ]: Same as above. required or else does redirection like for any other command.
As a rule of thumb, [[ is used for strings and files.
file=file name
[[ -f $file ]] && echo $file is a regular file
[[ -d '/tmp' ]] && echo temp is dir
[[ -e sh.sh ]] && echo file or dir exist
[[ -ss =~ ^- ]] && echo regular expression match
If you want to compare numbers, use an ArithmeticExpression, e.g.
declare -i i=10
declare -p i
#i=0 # i is string, not work
#declare -p i
while ((i !=0)); do
echo $i
((i--))
done
File read and write
>>read
>>my name is Chang
>>echo $REPLY
read -p Type yiour name: username # Prompt
read -n1 -p Continue y or n: # Accepting only one character
read -n1 -p Continue y or n: -s # Silent mode no type shows in screen
read -p Enter username ( more than 8 chars): username
echo $(#username)
if [[ ! -f ./$filename ]]; then
echo Source file not present!
exit 1
fi
while IFS= read -r line ; do
echo $line
fi
done < $filename
cat << EOF > file.txt
The current working directory is: $PWD
You are logged in as $(whoami)
EOF
echo this is a line | tee file.txt
Command line Arguments
./myscript.sh fred jones staff
$0 $1 $2 $3
$# # number of argument
$* # Complete list of arguments as a single string
$@ # Complete list of arguments as an array
while (($#)); do
echo $1
shift
done
Getopts
getopts cd -cd #No argument needed
getopts c:d -c fred -d # C requires argument
getopts c:d: -c fred -d joe # C and D require arguments
getopts :cd -h # Begins with colon for custom help
getopts.sh -c -- fred # -- end of options, fred is an argument
while getopts ':c:d:' opt; do
case $opt in
c) sudo useradd -m $OPTARG
break;;
d) sudo userdel -r OPTARG
break;;
*) echo Usage: $0 [-c|-d] <user;;
esac
done
Scheduling script
crontab -e # edit
crontab -l # showed all the comments
30 11 * * 3 echo hello > /tmp/hello # Once a week wed at 11:30 (min) (hour) (day) (Mon) (week))
sudo sed -Ei '/^($|#)/d' /avr/spool/cron/crontabs/pi
crontab -1
cat /etc/anacrontab
Configuration file: events happen after the system boot either daily, weekly, or monthly.
1, 10, 15 minutes after boot time daily, weekly, and monthly
Dut to the shutdown, if cronjobs did not run, the jobs will run by schedulling by anacron after system boot.
# /etc/anacrontab: configuration file for anacron
# See anacron(8) and anacrontab(5) for details.
SHELL=/bin/sh
HOME=/root
LOGNAME=root
# These replace cron's entries
1 5 cron.daily run-parts --report /etc/cron.daily
7 10 cron.weekly run-parts --report /etc/cron.weekly
@monthly 15 cron.monthly run-parts --report /etc/cron.monthly
Service Units
Systemd service units describe the excution of services. /etc/stsemd/system
Systemd is PID1
Service As an Example
Creating a pipe file we can communicate with client. a client send data to the pipe, TERM1. Server, TERM2, process the data. Unlike a standard pipe this allows for IPC or inter-process communication. The second command writes to the pipe (blocking). The & puts this into the background so you can continue to type commands in the same shell. It will exit when the FIFO is emptied by the next command.
sudo mkfifo /var/log/pipe
sudo cmod 666 /var/log/pipe
echo hello > var/log/pipe &
script.sh
Any data sent to the pipe will be echoed back in lowercase to pipe.out
#!/bin/bash
declare -l line
until [[ $line == stop ]]; do
line=$(cat /var/log/pipe)
echo $line >> /var/log/pip.out
done
sudo mv script.sh /root/bin/
sudo chmod 755 /root/bin/script.sh
Service Unit
vi script.service
[Unit]
Description=Demo pipe processing service
After=sshd.service
[Service]
Type=simple
ExecStart=/root/bin/script.sh
ExecStop=/bin/kill $MAINPID
killMode=process
[Install]
WantedBy=multi-user.target
Running script service
sudo mv script.service /etc/systemd/system
sudo systemctl daemon-reload
sudo systemctl enable --now script.service
sudo systemctl status script.service
echo HelloWord > /var/log/pipe
cat /var/log/pipe.out