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
