User Tools

Site Tools


2018-08-25-meeting_bash_scripting

This is an old revision of the document!


Meeting (Bash Scripting)

Steve Jones 2018-08-26

Bashy
#!/bin/bash -i
# This script shows some of the features of the Bourne Again SHell 
# or bash.
# It is designed to be run from a terminal as a presentation,
#
#
# Steve Jones, for the 2008-04-26 meeting, CLUG.org
# Updates and information will be made available as quickly
# as time permits.
 
# Set the values for $LINES and $COLUMNS
eval `resize` 	# This runs a sub-shell, the output of which
		# is evaluated by the current shell.
 
# Test to see if we were given a starting slide on
# the command line
if [ "$1x" = "x" ] 	# The special variable $1 is the first 
	then Slide=1	# argument on the command line, $2 is
	else		# the second, etc... Here we check to
	Slide=$1	# see if a slide number was given.
fi	
 
# Wait for a keypress and then continue on with the script.
Wait() {
# This is a function, it is delimited by the curly brackets
# above and below. It stops at the current cursor location
# and silently waits for a keypress
read -s -n 1 Anykey
}
 
# Format stdin to fit nicely on the screen.
Format() {
# Another function, this one is handed input on stdin, it then
# breaks it into nice, screen width-or-less lines.
fmt --split-only --width=$COLUMNS
}
 
# Pause for a second
Sleep() {
read -s -n 1 -t 1 Anykey
}
 
# Wait for a keypress at the bottom of the screen then clear
# the screen before continuing on with the script.
Pause() {
echo -en "\033[$LINES;5H"
# Read (-s, Silently) (-n 1, Single Char) (-p, "Prompt") Variable
read -s -n 1 -p "$Slide " Anykey
}
 
# Set the screen for black text on a grey background.
CodeOn() {
echo -e "\033[47m\033[30m"
}
 
# Set the screen back to black on white.
CodeOff() {
echo -e "\033[0m"
}
 
# Put a title on each page and center it on the top line
Title() {
clear
EightySpaces="                                                                                "
Spaces="${EightySpaces:0:$(( ($COLUMNS/2)-$(( ${#1}/2 )) ))}"
echo "$Spaces"$1
# Okay, this needs an explanation. The EightySpaces variable is 
# just what it says, eighty space characters. The next line 
# assigns a substring of $EightySpaces, starting at the first
# character (0, or leftmost), and continuing for half of the 
# screen width minus half of the length of the title. The
# title is handed to the function at the time of invocation.
}
 
 
S-01() {
Title "What is Bash?"
# Our first slide, it is a function that contains a number of
# functions. First is the one above, which calls the title 
# function to make things pretty, then the format function below
# to make the rest prettier.
cat <<EOF | Format
 
Bash is a shell, the name is a play on words for "Bourne Again SHell" because it was derived from and intended to be an improvement on the Bourne Shell. The shell is a command interpreter. Beyond just insulation between the system and the user, it's also a powerful programming language. A shell program, called a script, is an easy-to-use tool for building applications by "gluing" together system calls, tools, utilities, and compiled binaries.
 
Virtually the entire repertoire of UNIX commands, utilities, and tools are available for invocation by a shell script. If that were not enough, internal shell commands, such as testing and loop constructs, lend  additional power and flexibility to scripts. Shell scripts are especially well suited for  administrative system tasks and other routine repetitive tasks not requiring  the bells and whistles of a full-blown tightly structured programming language.
EOF
Pause
}
 
 
S-02() {
Title "When Bash is bad"
cat <<EOF | Format
 
When not to use shell scripts;
 
	Resource-intensive tasks, especially where speed is a factor (sorting, hashing, recursion...)
 
 	Procedures involving heavy-duty math operations, especially floating point arithmetic, arbitrary precision calculations, or complex numbers (use C++ or FORTRAN instead)
 
	Cross-platform portability required (use C or Java instead)
 
	Complex applications, where structured programming is a necessity (type-checking of variables, function prototypes, etc.)
 
	Proprietary, closed-source applications (Shell scripts put the source code right out in the open for all the world to see.)
EOF
Pause
}
 
S-03() {
Title "Inside and ..."
cat <<EOF | Format
 
A 'builtin'  is a command contained within the Bash tool set, literally  built in. This is either for performance reasons -- builtins execute faster  than external commands, which usually require forking off a separate process -- or because a particular builtin needs direct access to the shell internals.
EOF
Pause
}
 
S-04() {
Title "... and outside"
cat <<EOF | Format
 
When a command or the shell itself initiates (or spawns) a new subprocess  to carry out a task, this is called forking. This new process is the child, and the process that forked it off is the parent. While the child process  is doing its work, the parent process is still executing.
EOF
Pause
}
 
S-05() {
Title "How does it know?"
cat <<EOF | Format 
 
Bash scripts start with a "Magic Number", this is the first few characters of a file, and for interpreted script files, the characters are; 
 
#!
 
EOF
Wait
 
cat <<EOF | Format 
This is followed by the path to the shell which will process the rest of the script, in this case, /bin/bash for a first line of;
 
#!/bin/bash
EOF
Pause
}
 
S-06() {
Title "What can we do?"
cat <<EOF | Format
 
Within the script almost anything you can do at a command line can be done quickly and without typing errors (or with the same typing error repeated forever). 
EOF
Pause
}
 
 
S-07() {
Title "Variable Basics"
cat <<EOF | Format
 
Color is available for those that want it, it isn't always simple to do, but the results can be pretty and effective.
 
EOF
echo -e "\033[40m\033[1;37m  White on a black background  \033[0m"
Wait
echo  "echo -e \"\033[40m\033[1;37m  White on a black background  \033[0m\""
Wait
echo -e "\033[40m\033[31m   Red on a black background   \033[0m"
echo "echo -e \"\033[40m\033[31m   Red on a black background   \033[0m\""
echo -e "\033[40m\033[32m        Green on black         \033[0m"
echo "echo -e \"\033[40m\033[32m        Green on black         \033[0m\""
echo -e "\033[40m\033[1;34m      Light Blue on black      \033[0m"
echo "echo -e \"\033[40m\033[1;34m      Light Blue on black      \033[0m\""
Wait
cat <<EOF | Format
 
The shell makes use of variables to simplify a lot of tasks, for example the lines above could be replaced like so;
 
EOF
CodeOn
cat <<EOF | Format
LightBlue="\033[40m\033[1;34m"
Normal="\033[0m"
echo -e \$LightBlue This is a test \$Normal
EOF
Wait
LightBlue="\033[40m\033[1;34m"
Normal="\033[0m"
echo -e $LightBlue This is a test $Normal
CodeOff
Pause
}
 
S-08() {
Title "Looping"
cat <<EOF | Format
 
Bash can be repetitive, but that's what computers are supposed to be. Simple (or complex) looping structures can be created, allowing a single command to be run repeatedly with different data. For example, a script to install the packages you can't live without can be set up, making rebuilding a machine back to a baseline simple, quick, and error free.
EOF
CodeOn
cat <<EOF | Format
 
Favorites="openssh-server mc xmms frozen-bubble kpat"
 
for Package in \$Favorites 
 
	do apt-get -y install \$Package
 
	done
EOF
CodeOff
 
Wait
 
cat <<EOF | Format
This will install each of the packages mentioned in the \$Favorites string without asking for permission or confirmation, be cautious as this could have unintended effects on your system.
EOF
Pause
}
 
S-09() {
Title "Looping and doing stuff"
cat <<EOF | Format
 
Taking the previous example a step further, you could add your favorite users at the same time, this will help keep user's IDs the same across multiple machines - it isn't as elegant as setting up an LDAP server, but it is quick and simple, and sometimes needed.
EOF
Pause
}
 
S-10() {
Title "Here-documents, variables, other stuff"
cat <<EOF | Format
 
One way to load data into a variable is with a variation of a "Here Document", this presentation has a pile of them, and they are called as slides.
EOF
CodeOn
cat <<EOF | Format
Users=\`cat <<-END
 bill:1001:1001:Bill Smith:/home/bill:/bin/bash:\\\$1\\\$k0jwz7m2/mXNmL1
 jill:1002:1002:Jill Jones:/home/jill:/bin/bash:\\\$1\\\$k0jwz7m2/mXNmL1
 fred:1003:1003:Fred Flint:/home/fred:/bin/bash:\\\$1\\\$k0jwz7m2/mXNmL1
 fran:1004:1004:Fran Cisco:/home/fran:/bin/bash:\\\$1\\\$k0jwz7m2/mXNmL1
 mike:1005:1005:Mike Finck:/home/mike:/bin/bash:\\\$1\\\$k0jwz7m2/mXNmL1
 tina:1006:1006:Tina Waldo:/home/tina:/bin/bash:\\\$1\\\$k0jwz7m2/mXNmL1
END\`
EOF
CodeOff
Pause
}
 
S-11() {
Title "Variables, Looping for a reason"
CodeOn
cat <<EOF | Format
 
IFS=\$'\n'
 
for Each in \$Users ; do
        Login=\$(echo \$Each | cut -d : -f 1)
        Uid=\$(echo \$Each | cut -d : -f 2)
        Gid=\$(echo \$Each | cut -d : -f 3)
        Name=\$(echo \$Each | cut -d : -f 4)
        Home=\$(echo \$Each | cut -d : -f 5)
        Shell=\$(echo \$Each | cut -d : -f 6)
        Pass=\$(echo \$Each | cut -d : -f 7)
 
  useradd -m -c "\$Name" -d \$Home -g \$Gid -u \$Uid -p \$Pass \$Login
  echo "Added \$Name to this host"
done
EOF
Wait
echo "Added Bill Smith to this host" | Format
Sleep
echo "Added Jill Jones to this host" | Format
Sleep
echo "Added Fred Flint to this host" | Format
Sleep
echo "Added Fran Cisco to this host" | Format
Sleep
echo "Added Mike Finck to this host" | Format
Sleep
echo "Added Tina Waldo to this host" | Format
CodeOff
Pause
}
 
S-12() {
Title "Functions"
cat <<EOF | Format
 
Functions are a way of writing shortcuts for larger amounts of code. Functions can include any operations that bash supports, and can reduce a series of steps to a single command. Throughout this document, several functions are used repeatedly.
 
EOF
CodeOn
cat <<EOF | Format
# Wait for a keypress at the bottom of the screen then clear
# the screen before continuing on with the script.
Pause() {
echo -en "\033[\$LINES;2H"
# Read (-s, Silently) (-n 1, Single Char) (-p, "Prompt") Variable
read -s -n 1 -p "\$Slide" Anykey
clear
}
 
echo "This is a test."
Pause
This is a test.
EOF
CodeOff
Pause
}
 
S-13() {
Title "if / then / else / fi"
cat <<EOF | Format
 
Testing something and changing the course of your script based on the outcome can create useful applications.
 
EOF
CodeOn
cat <<EOF | Format
if [ -f ~/.bash_aliases ]; then
   . ~/.bash_aliases
else
# some more ls aliases
alias ll='ls -l'
alias la='ls -A'
alias l='ls -CF'
fi
EOF
CodeOff
Pause
}
 
S-14() {
Title "while / do"
cat <<EOF | Format
 
A "while / do" statement can form loops that are exited only if a condition is met.
EOF
CodeOn
# Pause for a second
Sleep() {
read -s -n 1 -t 1 Anykey
}
 
cat <<EOF | Format
export X=1
while [ \$X -lt 9 ] 
do 	
	echo -n \$X " "
	Sleep 
   	[ x\$Anykey != "x" ] && X=9
	(( X++ )) 
 
done
EOF
Wait
export X=1
while [ $X -lt 9 ] 
do 	
	echo -n $X " "
	Sleep 
	[ x$Anykey != "x" ] && X=9
   	(( X++ )) 
done
CodeOff
Pause
}
 
S-15() {
Title "case / esac"
cat <<EOF | Format
 
The "case / esac" statement is particularly handy at parsing a list and branching when there could be dozens of values for a variable.
EOF
Wait
CodeOn
cat <<EOF | Format
LastSlide=20 ; clear
while true ; do
case \$Slide in 
	1 )
	S-01 # What is Bash
	;;
	2 )
	S-02 # When not to use bash
	;;
# ....
	20 )
	S-20 # References
	;;
	* )
	echo Oops
	;;
esac
EOF
CodeOff
Pause   # A Pause inside of a function does not
        # increment the value of $Slide
clear
CodeOn
cat <<EOF | Format
case \$Anykey in
    b|B )
    Slide=\$(( \$Slide - 1 ))
    ;;
    q|Q )
    exit 0
    ;;
    1 )
    Slide=1
    ;;
    * )
    Slide=\$(( \$Slide + 1 ))
    ;;
esac
done
EOF
CodeOff
Pause
}
 
S-16() {
Title "I/O Redirection"
cat <<EOF | Format
 
I/O stands for Input/Output, and redirection refers to the ability of the shell to take input from and send output to a variety of places. Input can come from a file, pipe or device and output can go to the same. By default, input comes from the keyboard and is called "stdin", output goes to the screen as "stdout", and a third channel, "stderr" is also sent to the screen.
EOF
CodeOn
cat <<EOF | Format
LOGFILE=script.log
echo "This statement is sent to the log file, \\"\$LOGFILE\\"." >\$LOGFILE
echo "This statement is appended to \\"\$LOGFILE\\"." >>\$LOGFILE
echo "This statement is also appended to \\"\$LOGFILE\\"." >>\$LOGFILE
echo "This statement is echoed to stdout, not \\"\$LOGFILE\\"."
# cp with more characters, this will create xyzzy.txt if it does
# not exist, otherwise it appends to the end of the file.
cat < Primes.txt >> xyzzy.txt
find /etc -type f -perm -o=rw
find: /etc/cups/ssl: Permission denied
find: /etc/ssl/private: Permission denied
find: /etc/asterisk: Permission denied
find /etc -type f -perm -o=rw 2>/dev/null 
EOF
CodeOff
Pause
}
 
S-17() {
Title "Arithmetic"
cat <<EOF | Format
 
Arithmetic expansion provides a powerful tool for performing (integer) arithmetic operations in scripts. Translating a string into a numerical expression is relatively straightforward using backticks, double parentheses, or let.
EOF
CodeOn
cat <<EOF | Format
z=0
z=\$((\$z+3))     # z=3
z=\$((z+3))      # z=6
z=\$((z/2))      # z=3
z=\$((z*5))      # z=15
((z++))         # z=16
((z++))         # z=17
((z--))         # z=16
 
echo \$z
16
EOF
CodeOff
Pause
}
 
S-18() {
Title "Debugging"
cat <<EOF | Format
 
The Bash shell contains no built-in debugger, and only bare-bones debugging-specific commands and constructs. Syntax errors or outright typos in the script generate cryptic error messages that are often of no help in debugging a non-functional script.
EOF
CodeOn
cat <<EOF | Format
#!/bin/bash
# ex74.sh
# This is a buggy script.
# Where, oh where is the error?
a=37
if [\$a -gt 27 ]
then
  echo $a
fi  
exit 0
 
./ex74.sh: [37: command not found
EOF
CodeOff
Pause
}
 
 
S-19() {
Title "String Operations"
cat <<EOF | Format
 
Substring manipulation allows you to extract a portion of a longer string;
EOF
CodeOn
cat <<EOF | Format
X="this is a test"
echo \${#X}
14
echo \${X:4}
is a test
echo \${X:4:5}
is a
echo \${X: -4}
test
echo \${X: -7:5}
a te
echo {1..6}
1 2 3 4 5 6
echo {6..1}
6 5 4 3 2 1
EOF
CodeOff
Pause
}
 
S-20() {
Title "Style"
cat <<EOF | Format | less -X
 
Comment your code. This makes it easier for others to understand (and appreciate), and easier for you to maintain.
 
    PASS="\$PASS\${MATRIX:\$((\$RANDOM%\${#MATRIX})):1}"
    #  It made perfect sense when you wrote it last year,
    #  but now it's a complete mystery.
    #  (From Antek Sawicki's "pw.sh" script.)
 
Add descriptive headers to your scripts and functions.
 
    #!/bin/bash
 
    #************************************************#
    #                   xyz.sh                       #
    #           written by Bozo Bozeman              #
    #                July 05, 2001                   #
    #           Clean up project files.              #
    #************************************************#
 
Be sure to put the #!/bin/bash at the beginning of the first line of the script, preceding any comment headers.
 
Avoid using "hard-wired" literal constants. Use meaningful variable names instead. This makes the script easier to understand and permits making changes and updates without breaking the application.
 
    if [ -f /var/log/messages ]
    then
      ...
    fi
A year later, you decide to change the script to check /var/log/syslog. It is now necessary to manually change the script, instance by instance, and hope nothing breaks.
 
Choose descriptive names for variables and functions.
 
    fl=\`ls -al \$dirname\`                 # Cryptic.
    file_listing=\`ls -al \$dirname\`       # Better.
 
 
    MAXVAL=10   # All caps used for a script constant.
    while [ "\$index" -le "\$MAXVAL" ]
    ...
 
 
    E_NOTFOUND=75                        
    #  Uppercase for an errorcode,
    # +and name begins with "E_".
    if [ ! -e "\$filename" ]
    then
      echo "File \$filename not found."
      exit \$E_NOTFOUND
    fi  
 
    GetAnswer ()                         
    # Mixed case works well for a function.
    {
      prompt=\$1
      echo -n \$prompt
      read answer
      return \$answer
    }  
 
    GetAnswer "What is your favorite number? "
    favorite_number=\$?
    echo \$favorite_number
 
Use exit codes in a systematic and meaningful way.
EOF
Pause
}
 
S-21() {
Title "References"
cat <<EOF | Format
 
The Linux Documentation Project   http://tldp.org/
 
TLDP.org has a huge amount of information on everything from basic shell scripting thru setting up your own ISP with a single PC. Sadly, it is beginning to appear outdated, but it can still be an invaluable resource.
 
Wikipedia http://en.wikipedia.org/
EOF
Pause
}
 
 
 
LastSlide=21
 
clear
while true
do
[ $Slide -lt 1 ] && Slide=$LastSlide
[ $Slide -gt $LastSlide ] && Slide=1
 
case $Slide in 
	1 )
	S-01 # What is Bash
	;;
	2 )
	S-02 # When not to use bash
	;;
	3 )
	S-03 # Builtin command def
	;;
	4 )
	S-04 # Spawned command
	;;
	5 )
	S-05 # Magic Number
	;;
	6 )
	S-06 # Starting out
	;;
	7 )
	S-07 # Variables
	;;
	8 )
	S-08 # Loops
	;;
	9 )
	S-09 # Loops and external commands
	;;
	10 )
	S-10 # Here Documents and Variables
	;;
	11 )
	S-11 # IFS, Loops, Variables, Screen Output
	;;
	12 )
	S-12 # Functions and Aliases
	;;
	13 )
	S-13 # If / Then / Else
	;;
	14 )
	S-14 # Do / While
	;;
	15 )
	S-15 # Case / Esac 
	;;
	16 )
	S-16 # I/O Redirection
	;;
	17 )
	S-17 # Arithmetic
	;;
	18 )
	S-18 # Debugging
	;;
	19 )
	S-19 # String Operations
	;;
	20 )
	S-20 # Style
	;;
	21 )
	S-21 # Reference
	;;
	* )
	echo Oops
	;;
esac
 
 
# What was that key we just got?
case $Anykey in
	b|B )
	Slide=$(( $Slide - 1 ))
	;;
	r|R )
    	clear
    	;;
    	q|Q )
	exit 0
	;;
	1 )
	Slide=1
	;;
	* )
	Slide=$(( $Slide +1 ))
	;;
esac
done
2018-08-25-meeting_bash_scripting.1781124654.txt.gz · Last modified: by steve