%\documentclass[colorBG,slideColor,troispoints,pdf]{prosper} %\documentclass[colorBG,total,slideColor,pdf]{prosper} \documentclass[total,pdf]{prosper} %\documentclass[colorBG,slideColor,ps]{prosper} \usepackage[toc,highlight,Tycja]{HA-prosper} \usepackage{alltt,key,xr,cols,rcs,acro,nick,% graphicx,varioref,explanation,booktabs,amssymb} \RCS $Revision: 1.12 $ % Copyright (c) 2004 by Nick Urbanik . % This material may be distributed only subject to the terms and % conditions set forth in the Open Publication License, v1.0 or later % (the latest version is presently available at % http://www.opencontent.org/openpub/). % For Prosper sizing: see % http://sourceforge.net/mailarchive/forum.php?thread_id=1373415&forum_id=4400 % export GS_OPTIONS="-sPAPERSIZE=a4" \externaldocument[lt-]% {../../linux_training-plus-config-files-ossi/build/masterfile} % \usepackage[pdfpagemode=None,pdfauthor={Nick Urbanik}]{hyperref} \renewcommand*{\bs}{\texttt{\char '134}} % Backslash `\' \newcommand*{\labTitle}{Shell Programming---an Introduction} \newcommand*{\subject}{Operating Systems and Systems Integration} \newcommand*{\emphcolour}[1]{\emph{\red#1}} \providecommand*{\RPM}{\acro{RPM}\xspace} \providecommand*{\CD}{\acro{CD}\xspace} % Get rid of that horrible hash mark in slide numbers in ppr-prv.cls: \def\no{} \title{Shell Programming---an Introduction} %% \author{Nick Urbanik} %% \email{nicku@vtc.edu.hk} %% \institution{A computing department} \slideCaption{OSSI --- Shell Programming --- ver. \RCSRevision} \author{Nick Urbanik\\ \email{nicku@vtc.edu.hk}\\ \institution{A computing department}} \subtitle{\footnotesize{}Copyright Conditions: Open Publication License (see \url{http://www.opencontent.org/openpub/})}% %% \subtitle{\footnotesize{}Copyright Conditions: GNU FDL (see %% \url{http://www.gnu.org/licenses/fdl.html})} %\Logo{\includegraphics[width=15mm]{ict-logo-smaller}} \setlength{\extrarowheight}{0pt} \DefaultTransition{Wipe} \TitleSlideNav{FullScreen} \NormalSlideNav{ShowBookmarks} \LeftFoot{OSSI --- ver. \RCSRevision} \RightFoot{Shell Programming} \begin{document} \maketitle \tsection{Intro} %\tableofcontents \begin{slide}{Aim} \small After successfully working through this exercise, You will: \begin{itemize} \item write simple shell scripts using \texttt{for}, \texttt{if}, \texttt{while}, \texttt{case}, \texttt{getopts} statements; \item write shell script functions, and be able to handle parameters; \item understand basic regular expressions, and be able to create your own regular expressions; \item understand how to execute and debug these scripts; \item understand some simple shell scripts written by others. \end{itemize} \end{slide} \begin{slide}{Why Shell Scripting?} \begin{itemize} \item Basic startup, shutdown of Linux, \UNIX systems uses large number of shell scripts \begin{itemize} \item understanding shell scripting important to understand and perhaps modify behaviour of system \end{itemize} \item Very high level: powerful script can be very short \item Can build, test script incrementally \item Useful on the command line: ``one liners'' \end{itemize} \end{slide} \begin{slide}{Where to get more information} \label{sld:references} \begin{itemize} \item the Libarary has two copies of the book, \emph{Learning the Bash Shell}, second edition, by Cameron Newham \& Bill Rosenblatt, O'Reilly, 1998. \item There is a free on-line book about shell programming at: \url{http://tldp.org/LDP/abs/html/index.html} and \url{http://tldp.org/LDP/abs/abs-guide.pdf}. It has hundreds of pages, and is packed with \emph{examples}. \item The handy reference to shell programming is: \begin{alltt} $ \textbf{pinfo bash} \end{alltt}%$ or \begin{alltt} $ \textbf{man bash} \end{alltt}%$ \item {\Large\emphcolour{IMPORTANT}:} \texttt{bash} provides simple on-line help for all built-in commands, e.g., \begin{alltt} $ \textbf{help let} \end{alltt}%$ \end{itemize} \end{slide} \begin{slide}{The Shell is an Interpreter} \begin{itemize} \item Some languages are compiled: C, \C++, Java,\dots \item Some languages are interpreted: Java bytecode, Shell \item Shell is an interpreter: kernel does not run shell program directly: \begin{itemize} \item kernel runs the shell program \texttt{/bin/sh} with script file name as a parameter \item the kernel cannot execute the shell script directly, as it can a binary executable file that results from compiling a C program \end{itemize} \end{itemize} \end{slide} %\tsection{Running Scripts} \begin{slide}{The Shebang} \begin{itemize} \item You ask the Linux kernel to execute the shell script \item kernel reads first two characters of the executable file \begin{itemize} \item If first 2 chars are ``\#!'' then \item kernel executes the name that follows, with the file name of the script as a parameter \end{itemize} \item Example: a file called \texttt{find.sh} has this as the first line: \begin{alltt} #! /bin/sh \end{alltt} \item then kernel executes this: \begin{alltt} /bin/sh find.sh \end{alltt} \item What will happen in each case if an executable file begins with: \begin{itemize} \item \begin{verbatim} #! /bin/rm \end{verbatim} \item \begin{verbatim} #! /bin/ls \end{verbatim} \end{itemize} \end{itemize} \end{slide} \begin{slide}[toc={Making Executable}]{Making the script executable} \label{sld:making-script-executable} To easily execute a script, it should: \begin{itemize} \item be on the \texttt{PATH} \item have execute permission. \end{itemize} How to do each of these? \begin{itemize} \item Red Hat Linux by default, includes the directory $\sim$\texttt{/bin} on the \texttt{PATH}, so create this directory, and put your scripts there: \begin{alltt} $ \textbf{mkdir \(\sim\)/bin} \end{alltt}%$ \item If your script is called \texttt{script}, then this command will make it executable: \begin{alltt} $ \textbf{chmod +x script} \end{alltt}%$ \end{itemize} \end{slide} \tsection{Quoting and Funny Chars} \begin{slide}{Special Characters} \begin{itemize} \item Many characters are \emphcolour{special} to the shell, and have a particular meaning to the shell. \end{itemize} \vspace*{-1ex} \footnotesize\setlength{\extrarowheight}{0pt} \begin{tabular}[t]{@{}>{\ttfamily}cll@{}} \toprule% \multicolumn{1}{c}{\textbf{Character}} & \textbf{Meaning} & \multicolumn{1}{c@{}}{\textbf{See slide}}\\ \midrule% $\sim$ & Home directory & \S\,\ref{sld:making-script-executable}\\ ` & Command substitution. Better: \texttt{\$(\ldots)} & \S\,\ref{sld:command-substitution} \\ \# & Comment & \\ \$ & Variable expression & \S\,\ref{sld:variables} \\ \& & Background Job & \vref{lt-sec:basic-shell-and-tools-approach-background-processes} \\ \texttt{*} & File name matching wildcard & \vref{lt-sec:basic-shell-and-tools-approach-filename-generation} \\ \verb+|+ & Pipe & \vref{lt-sec:basic-shell-and-tools-approach-pipelines} \\ \bottomrule \end{tabular} \end{slide} \begin{slide}[toc={Special Chars---2}]{Special Characters---continued: 2} \footnotesize\setlength{\extrarowheight}{0pt} \begin{tabular}[t]{@{}>{\ttfamily}cll@{}} \toprule% \multicolumn{1}{c}{\textbf{Character}} & \textbf{Meaning} & \multicolumn{1}{c@{}}{\textbf{See slide}}\\ \midrule% ( & Start subshell & \S\,\ref{sld:change-IFS}, \ref{sld:vars-in-subshell}, \ref{sld:blocks} \\ ) & End subshell & \S\,\ref{sld:change-IFS}, \ref{sld:vars-in-subshell}, \ref{sld:blocks} \\ {[} & Start character set file name matching & \vref{lt-sec:basic-shell-and-tools-approach-pipelines} \\ {]} & End character set file name matching & \vref{lt-sec:basic-shell-and-tools-approach-pipelines} \\ \{ & Start command block & \S\,\ref{sld:blocks} \\ ; & Command separator & \S\,\ref{sld:error-handling} \\ \bs & Quote next character & \S\,\ref{sld:more-about-quoting} \\ ' & Strong quote & \S\,\ref{sld:more-about-quoting} \\ " & Weak quote & \S\,\ref{sld:more-about-quoting} \\ \bottomrule \end{tabular} \end{slide} \begin{slide}[toc={Special Chars---3}]{Special Characters---continued: 3} \footnotesize\setlength{\extrarowheight}{0pt} \begin{tabular}[t]{@{}>{\ttfamily}cll@{}} \toprule% \multicolumn{1}{c}{\textbf{Character}} & \textbf{Meaning} & \multicolumn{1}{c@{}}{\textbf{See slide}}\\ \midrule% \texttt{<} & Input redirect & \vref{lt-sec:basic-shell-and-tools-approach-input-redirection} \\ \texttt{>} & Output redirect & \vref{lt-sec:basic-shell-and-tools-approach-output-redirection} \\ \texttt{/} & Pathname directory separator & \\ ? & Single-character match in filenames & \vref{lt-sec:basic-shell-and-tools-approach-filename-generation} \\ ! & Pipline logical NOT & \S\,\ref{sld:not-operator}\\ \meta{space or tab} & shell normally splits at white space & \S\ref{sld:ifs} \\ \bottomrule \end{tabular} \par\medskip\par% \begin{itemize} \item Note that references to pages in the tables above refer to the modules in the workshop notes \end{itemize} \end{slide} \begin{slide}{Quoting} \label{sld:quoting} \begin{itemize} \item Sometimes you want to use a special character \emph{literally}; i.e., without its special meaning. \item Called \emphcolour{quoting} \item Suppose you want to print the string: \texttt{2 * 3 > 5 is a valid inequality}? \item If you did this: \begin{alltt} $ \textbf{echo 2 * 3 > 5 is a valid inequality} \end{alltt}%$ the new file `\texttt{5}' is created, containing the character `\texttt{2}', then the names of all the files in the current directory, then the string ``\texttt{3 is a valid inequality}''. \end{itemize} \end{slide} \begin{slide}{Quoting---2} \label{sld:quoting2} \begin{itemize} \item To make it work, you need to protect the special characters `\texttt{*}' and `\texttt{>}' from the shell by quoting them. There are three methods of quoting: \begin{itemize} \item Using double quotes (``weak quotes'') \item Using single quotes (``strong quotes'') \item Using a backslash in front of each special character you want to quote \end{itemize} \item This example shows all three: \begin{alltt} $ \textbf{echo "2 * 3 > 5 is a valid inequality"} $ \textbf{echo '2 * 3 > 5 is a valid inequality'} $ \textbf{echo 2 \bs* 3 \bs> 5 is a valid inequality} \end{alltt}%$ \end{itemize} \end{slide} \begin{slide}{Quoting---When to use it?} \begin{itemize} \item Use quoting when you want to pass special characters to another program. \item Examples of programs that often use special characters: \begin{itemize} \item \texttt{find}, \texttt{locate}, \texttt{grep}, \texttt{expr}, \texttt{sed} and \texttt{echo} \end{itemize} \item Here are examples where quoting is required for the program to work properly: \begin{alltt} $ \textbf{find . -name \bs*.jpg} $ \textbf{locate '/usr/bin/c*'} $ \textbf{grep 'main.*(' *.c} $ \textbf{i=$(expr i \bs* 5)} \end{alltt} \end{itemize} \end{slide} \tsection{Variables} \begin{slide}{True and False} \begin{itemize} \item Shell programs depend on executing external programs \item When any external program execution is successful, the exit status is zero, 0 \item An error results in a non-zero error code \item To match this, in shell programming: \begin{itemize} \item The value 0 is true \item any non-zero value is false \end{itemize} \item This is opposite from other programming languages \end{itemize} \end{slide} \begin{slide}{Variables---1} \label{sld:variables} \begin{itemize} \item Variables not declared; they just appear when assigned to \item Assignment: \begin{itemize} \item no dollar sign \item no space around equals sign \item examples: \begin{alltt} $ \textbf{x=10} # \textnormal{correct} $ \textbf{x = 10} # \textnormal{wrong: try to execute program called ``\texttt{x}''} \end{alltt} \end{itemize} \item Read value of variable: \begin{itemize} \item put a `\texttt{\$}' in front of variable name \item example: \begin{alltt} $ \textbf{echo "The value of x is $x"} \end{alltt} \end{itemize} \end{itemize} \end{slide} \begin{slide}{Variables---Assignments} \begin{itemize} \item You can put multiple assignments on one line: \begin{alltt} i=0 j=10 k=100 \end{alltt} \item You can set a variable temporarily while executing a program: \begin{alltt} $ \textbf{echo $EDITOR} emacsclient $ \textbf{EDITOR=gedit crontab -e} $ \textbf{echo $EDITOR} emacsclient \end{alltt}%$ \end{itemize} \end{slide} \begin{slide}{Variables---Local to Script} \label{sld:vars-in-subshell} \begin{itemize} \item Variables disappear after a script finishes \item Variables created in a sub shell disappear \begin{itemize} \item parent shell cannot read variables in a sub shell \item example: \begin{alltt} $ \textbf{cat variables} #! /bin/sh echo $HOME HOME=happy echo $HOME $ \textbf{./variables} /home/nicku happy $ \textbf{echo $HOME} /home/nicku \end{alltt} \end{itemize} \end{itemize} \end{slide} \begin{slide}{Variables---Unsetting Them} \begin{itemize} \item You can make a variable hold the null string by assigning it to nothing, but it does not disappear totally: \begin{alltt} $ \textbf{VAR=} $ \textbf{set | grep '^VAR'} VAR= \end{alltt} \item You can make it disappear totally using \texttt{\textbf{\red{}unset}}: \begin{alltt} $ \textbf{unset VAR} $ \textbf{set | grep '^VAR'} \end{alltt} \end{itemize} \end{slide} \begin{slide}{Command-line Parameters} \begin{itemize} \item Command-line parameters are called \texttt{\$0}, \texttt{\$1}, \texttt{\$2}, \ldots \item Example: when call a shell script called ``\texttt{shell-script}'' like this: \begin{alltt} $ \textbf{shell-script param1 param2 param3 param4} \end{alltt}%$ %\vspace*{-1ex} {\normalsize\setlength{\extrarowheight}{-0.5pt}% \begin{tabular}[t]{@{}ll@{}} \toprule% \emph{variable} & \emph{value}\\ \midrule% \texttt{\$0} & \texttt{shell-script}\\ \texttt{\$1} & \texttt{param1}\\ \texttt{\$2} & \texttt{param2}\\ \texttt{\$3} & \texttt{param3}\\ \texttt{\$4} & \texttt{param4}\\ \texttt{\$\#} & number of parameters to the program, e.g., 4\\ \bottomrule \end{tabular}} \par\medskip\par \begin{itemize} \item Note: these variables are read-only. \end{itemize} \end{itemize} \end{slide} \begin{slide}{Special Built-in Variables} \begin{itemize} \item Both \texttt{\$@} and \texttt{\$*} are a list of all the parameters. \item The only difference between them is when they are quoted in quotes---see manual page for \texttt{bash} \item \texttt{\$?} is exit status of last command \item \texttt{\$\$} is the process ID of the current shell \item Example shell script: \begin{alltt} #! /bin/sh echo $0 is the full name of this shell script echo first parameter is $1 echo first parameter is $2 echo first parameter is $3 echo total number of parameters is $# echo process ID is $$ \end{alltt}%$ %$ \end{itemize} \end{slide} \begin{slide}[toc={Variables: use Braces \texttt{\$\{\ldots\}}},% bm={Variables: use Braces \texttt{\$\{\ldots\}}}]% {Variables: use Braces \texttt{\red\$\{\ldots\}}} \begin{itemize} \item It's good to put braces round a variable name when getting its value \item Then no problem to join its value with other text: \begin{alltt} $ \textbf{test=123} $ \textbf{echo $\{test\}} 123 \textbf{# No good, variable $test456 is undefined:} $ \textbf{echo $test456} $ \textbf{echo $\{test\}456} 123456 \end{alltt}% \end{itemize} \end{slide} \begin{slide}[toc={After \texttt{\$9}}]% {Braces and Parameters after \textbf{\$9}} \begin{itemize} \item Need braces to access parameters after \texttt{\$9}: \begin{alltt} $ \textbf{cat paramten} #! /bin/sh echo $10 echo $\{10\} $ \textbf{./paramten a b c d e f g h i j} a0 j \end{alltt} \item Notice that \texttt{\$10} is the same as \texttt{\$\{1\}0}, i.e., the first parameter ``\texttt{a}'' then the literal character zero ``\texttt{0}'' \end{itemize} \end{slide} \begin{slide}{More about Quoting} \label{sld:more-about-quoting} \begin{itemize} \item {\mbox{}\red{}Double quotes}: \texttt{"..."} stop the special behaviour of all special characters, except for: \begin{itemize} \item variable interpretation (\texttt{\$}) \item backticks (\texttt{`}) --- see slide~\ref{sld:command-substitution} \item the backslash (\bs) \end{itemize} \item {\mbox{}\red{}Single quotes} \texttt{'...'}: \begin{itemize} \item stop the special behaviour of \emphcolour{all} special characters \end{itemize} \item {\mbox{}\red{}Backslash}: \begin{itemize} \item preserves literal behaviour of character, except for newline; see slides~\S\ref{sld:conditions-combining-comparisons}, \S\ref{sld:arithmetic-expressions-with-((...))}, \S\ref{sld:for-statement-1} \item Putting ``\bs'' at the end of the line lets you continue a long line on more than one physical line, but the shell will treat it as if it were all on one line. \end{itemize} \end{itemize} \end{slide} \tsection{Command Substitution} \begin{slide}[toc={Command Substitution},bm={Command Substitution}]% {Command Substitution --- \texttt{\red\$(...)} or \texttt{\red`...`}} \label{sld:command-substitution} \begin{itemize} \item Enclose command in \texttt{\red\$(...)} or backticks:%$ \texttt{\red`...`} \item Means, ``{\magenta{}Execute the command in the \texttt{\red\$(...)}\magenta{} and put the output back here.}'' \item Here is an example using \texttt{\textbf{\red{}expr}}: \begin{alltt} $ \textbf{expr 3 + 2} 5 $ \textbf{i=expr 3 + 2} # \textnormal{error: try execute command `\texttt{3}'} $ \textbf{i=$(expr 3 + 2)} # \textnormal{correct} $ \textbf{i=`expr 3 + 2`} # \textnormal{also correct} \end{alltt}%$ \end{itemize} \end{slide} \begin{slide}[toc={Example of Cmd Subst}]{Command Substitution---Example} \begin{itemize} \item We want to put the \emphcolour{output of the command} \texttt{hostname} into a \emph{\blue{}variable}: \begin{alltt} $ \textbf{hostname} nickpc.tyict.vtc.edu.hk $ \textbf{h=hostname} $ \textbf{echo $h} hostname \end{alltt}%$ \item Oh dear, we only stored the \emph{\blue{}name} of the command, not the \emphcolour{output} of the command! \item \emphcolour{Command substitution} solves the problem: \begin{alltt} $ \textbf{h=$(hostname)} $ \textbf{echo $h} nickpc.tyict.vtc.edu.hk \end{alltt}%$ \item We put \texttt{\$(\ldots)} around the command. You can then assign the output of the command. \end{itemize} \end{slide} \tsection{Conditions} \begin{slide}[toc={Comparing Strings}]{Conditions---String Comparisons} \begin{itemize} \item All programming languages depend on \emphcolour{conditions} for \texttt{if} statements and for \texttt{while} loops \item Shell programming uses a built-in command which is either \texttt{test} or \texttt{[...]} \item Examples of \emphcolour{string} comparisons: \begin{alltt}\scriptsize [ "$USER" = root ] # true if the value of $USER is "root" [ "$USER" != root ] # true if the value of $USER is not "root" [ -z "$USER" ] # true if the string "$USER" has zero length [ string1 \bs< string2 ] # true if string1 sorts less than string2 [ string1 \bs> string2 ] # true if string1 sorts greater than string2 \end{alltt} \item Note that we need to quote the `\texttt{>}' and the `\texttt{<}' to avoid interpreting them as file redirection. \item \emphcolour{Note:} {\green{}the spaces after the ``[`` and before the ``]'' are essential.} \item {\mbox{}\blue{}Also spaces are \emphcolour{essential} around operators} \end{itemize} \end{slide} \begin{slide}[toc={Comparing Integers}]{Conditions---Integer Comparisons} \label{sld:test-arith} \begin{itemize} \item Examples of \emphcolour{numeric} integer comparisons: \begin{alltt}\small [ "$x" -eq 5 ] # true if the value of $x is 5 [ "$x" -ne 5 ] # true if integer $x is \emph{not} 5 [ "$x" -lt 5 ] # true if integer $x is \(<\) 5 [ "$x" -gt 5 ] # true if integer $x is \(>\) 5 [ "$x" -le 5 ] # true if integer $x is \(\le\) 5 [ "$x" -ge 5 ] # true if integer $x is \(\ge\) 5 \end{alltt}% \item Note again that the spaces after the ``[`` and before the~``]'' are essential. \item {\mbox{}\blue{}Also spaces are \emphcolour{essential} around operators} \end{itemize} \end{slide} \begin{slide}[toc={File Tests \& NOT}]{Conditions---File Tests, NOT Operator} \label{sld:not-operator} \begin{itemize} \item The shell provides many tests of information about \emphcolour{files}. \item Do \texttt{man test} to see the complete list. \item Some examples: \end{itemize} {\footnotesize% \begin{alltt} $ \textbf{[ -f file ]} # true if file is an ordinary file $ \textbf{[ ! -f file ]} # true if file is NOT an ordinary file $ \textbf{[ -d file ]} # true if file is a directory $ \textbf{[ -u file ]} # true if file has SUID permission $ \textbf{[ -g file ]} # true if file has SGID permission $ \textbf{[ -x file ]} # true if file exists and is executable $ \textbf{[ -r file ]} # true if file exists and is readable $ \textbf{[ -w file ]} # true if file exists and is writeable $ \textbf{[ file1 -nt file2 ]} # true if file1 is newer than file2 \end{alltt}%$ } \begin{itemize} \item \emphcolour{Note again:} {\green{}the spaces after the ``[`` and before the ``]'' are essential.} \item {\mbox{}\blue{}Also spaces are \emphcolour{essential} around operators} \end{itemize} \end{slide} \begin{slide}[toc={AND OR Conditions}]{Conditions---Combining Comparisons} \label{sld:conditions-combining-comparisons} \begin{itemize} \item Examples of \emphcolour{combining comparisons} with AND: \texttt{\red{}-a} and OR: \texttt{\red{}-o}, and \emphcolour{grouping} with \texttt{\bs(\ldots\bs)} \begin{alltt}\small # \textnormal{true if the value of \texttt{$x} is 5 AND \texttt{$USER} is not equal to root:} [ "$x" -eq 5 -a "$USER" != root ] # \textnormal{true if the value of \texttt{$x} is 5 OR \texttt{$USER} is not equal to \texttt{root}:} [ "$x" -eq 5 -o "$USER" != root ] # \textnormal{true if ( the value of \texttt{$x} is 5 OR \texttt{$USER} is not equal to \texttt{root} ) AND} # \textnormal{( \texttt{$y} \(>\) 7 OR \texttt{$HOME} has the value happy )} [ \bs( "$x" -eq 5 -o "$USER" != root \bs) -a \bs \bs( "$y" -gt 7 -o "$HOME" = happy \bs) ] \end{alltt} \item Note again that {\green{}the spaces after the ``[`` and before the ``]'' are essential}. \item Do \texttt{man test} to see the information about all the operators. \end{itemize} \end{slide} \tsection{Arithmetic} \begin{slide}{Arithmetic Assignments} \footnotesize \begin{itemize} \item Can do with the external program \texttt{\textbf{\red{}expr}} \begin{itemize} \item \ldots but \texttt{expr} is not so easy to use, although it is very standard and \emph{\green{}portable}: see \texttt{man~expr} \item Easier is to use the built in \texttt{\textbf{\red{}let}} command \begin{itemize} \item see \texttt{help let} \end{itemize} \item Examples: {\small \begin{alltt} $ \textbf{let x=1+4} $ \textbf{let ++x} # Now x is 6 $ \textbf{let x='1 + 4'} $ \textbf{let 'x = 1 + 4'} $ \textbf{let x="(2 + 3) * 5"} # now x is 25 $ \textbf{let "x = 2 + 3 * 5"} # now x is 17 $ \textbf{let "x += 5"} # now x is 22 $ \textbf{let "x = x + 5"} # now x is 27; NOTE NO $ \end{alltt}%$ } %\vspace*{-2ex} \item Notice that you do not need to quote the special characters with \texttt{let}. \item Quote if you want to use white space. \item Do not put a dollar in front of variable, even on right side of assignment; see last example. \end{itemize} \end{itemize} \end{slide} \begin{slide}[toc=\texttt{\$((\ldots))},bm=\texttt{\$((\ldots))}]% {Arithmetic Expressions with \texttt{\red\$((\ldots))}} \label{sld:arithmetic-expressions-with-((...))} \begin{itemize} \item The shell interprets anything inside \texttt{\$((\ldots))} as an arithmetic expression \item You could calculate the number of days left in the year like this: \begin{alltt} $ \textbf{echo "There are \bs $(( (365-$(date +%j)) / 7 )) weeks \bs left till December 31"} \end{alltt}%$ \item {\mbox{}\green{}No dollar sign} in front of variables in these arithmetic expressions. \end{itemize} \end{slide} \begin{slide}[toc=\texttt{((\ldots))},bm=\texttt{((\ldots))}]% {Arithmetic Conditions with \texttt{\red((\ldots))}} \label{sld:arithmetic-conditions-new} \begin{itemize} \item A (less portable) alternative to the arithmetic conditions in slide~\ref{sld:test-arith} is putting the expression in \texttt{((\ldots))} \item So you can do \begin{alltt} (( (3>2) && (4<=1) )) \end{alltt} %\vspace*{-2ex} instead of \begin{alltt} [ \bs( 3 -gt 2 \bs) -a \bs( 4 -le 1 \bs) ] \end{alltt} %\vspace*{-2.5ex} \item Operators that work with \texttt{let}, \texttt{\$((\ldots))} and \texttt{((\ldots))} include: \begin{alltt} ++ -- ** + - * / % << >> & | ~ ! ^ < > <= >= == != ? : \end{alltt} which have \emph{exactly} the same effect as in the C programming language \begin{itemize} \item except exponentiation operator \texttt{**}, i.e., \texttt{echo~\$((2**20))} prints the value of $2^{20}$, i.e., 1048576 \item For details, see \begin{alltt} $ \textbf{help let} \end{alltt}%$ \end{itemize} \end{itemize} \end{slide} \tsection{Statements} \begin{slide}[toc={\texttt{if} Statement},bm={\texttt{if} Statement}]% {\texttt{\red{}if} Statement} \label{sld:if-statement} \begin{itemize} \item Syntax: {\small \begin{alltt} if \meta{test-commands} then \meta{statements-if-test-commands-1-true} {\mbox{}\red{}elif} \meta{test-commands-2} then \meta{statements-if-test-commands-2-true} else \meta{statements-if-all-test-commands-false} fi \end{alltt} } \item Example: {\small \begin{verbatim} if grep nick /etc/passwd > /dev/null 2>&1 then echo Nick has a local account here else echo Nick has no local account here fi \end{verbatim} } \end{itemize} \end{slide} \begin{slide}[toc={\texttt{while} Statement},% bm={\texttt{while} Statement}]% {\texttt{\red{}while} Statement} \begin{itemize} \item Syntax: {\small \begin{alltt} while \meta{test-commands} do \meta{loop-body-statements} done \end{alltt} } \item Example: {\small \begin{verbatim} i=0 while [ "$i" -lt 10 ] do echo -n "$i " # -n suppresses newline. let "i = i + 1" # i=$(expr $i + 1) also works done \end{verbatim} } \end{itemize} \end{slide} \begin{slide}[toc={\texttt{for} Statement},bm={\texttt{for} Statement}]% {\texttt{\red{}for} Statement} \label{sld:for-statement-1} \begin{itemize} \item Syntax: { \begin{alltt} for \meta{name} in \meta{words} do \meta{loop-body-statements} done \end{alltt} } \item Example: { \begin{verbatim} for planet in Mercury Venus Earth Mars \ Jupiter Saturn Uranus Neptune Pluto do echo $planet done \end{verbatim}%$ } \begin{itemize} \item The backslash ``\texttt{\bs}'' quotes the newline. It's just a way of folding a long line in a shell script over two or more lines. \end{itemize} \end{itemize} \end{slide} \begin{slide}[toc={\texttt{for} Loops: Another Example},% bm={\texttt{for} Loops: Another Example}]% {\texttt{\red{}for} Loops: Another Example} \small% \begin{itemize} \item Here the shell turns \texttt{*.txt} into a list of file names ending in ``\texttt{.txt}'':% {\small% \begin{verbatim} for i in *.txt do echo $i grep 'lost treasure' $i done \end{verbatim} } \item You can leave the \texttt{in \meta{words}} out; in that case, \texttt{\meta{name}} is set to each parameter in turn:% {\small% \begin{verbatim} i=0 for parameter do let 'i = i + 1' echo "parameter $i is $parameter" done \end{verbatim} } \end{itemize} \end{slide} \begin{slide}[toc={\texttt{for (( ; ; ))}},bm={\texttt{for (( ; ; ))}}]% {\texttt{\red{}for} Loops: second, C-like syntax} \begin{itemize} \item There is a second (less frequently used, and less portable) C-like \texttt{\textbf{\red{}for}} loop syntax:% {\small% \begin{alltt} for (( \meta{expr1} ; \meta{expr2} ; \meta{expr3} )) do \meta{loop-body-statements} done \end{alltt} } %\vspace*{-3ex} \item Rules: same as for arithmetic conditions---see slide~\ref{sld:arithmetic-conditions-new} \item Example: {\small% \begin{verbatim} for (( i = 0; i < 10; ++i )) do echo $i done \end{verbatim}%$ } \end{itemize} \end{slide} \begin{slide}[toc={\texttt{break} and \texttt{continue}},% bm=={\texttt{break} and \texttt{continue}}]% {\texttt{\red{}break} and \texttt{\red{}continue}} \begin{itemize} \item Use inside a loop \item Work like they do in C \item \texttt{\textbf{\red{}break}} terminates the innermost loop; execution goes on after the loop \item \texttt{\textbf{\red{}continue}} will skip the rest of the body of the loop, and resume execution on the next itteration of the loop. \end{itemize} \end{slide} \begin{slide}[toc={Blocks: \texttt{\{\ldots\}}},bm={Blocks: \texttt{\{\ldots\}}}]% {Blocks: \texttt{{\red\{}\ldots{\red\}}}} \label{sld:blocks} \begin{itemize} \item A \emphcolour{subshell} is one way of grouping commands together, but it starts a new process, and any variable changes are localised \item An alternative is to group commands into a \emphcolour{block}, enclosing a set of commands in braces: \texttt{{\red\{}\ldots{\red\}}} \item Useful for grouping commands for \emphcolour{file input} or \emphcolour{output} \begin{itemize} \item \ldots\,though variables are not localised \end{itemize} %(see %slide~\ref{sld:grouping-for-file-io}). \item See next slide for another application. \end{itemize} \end{slide} \begin{slide}[toc={Flow Control: \texttt{\textbar\textbar} \texttt{\&\&}},% bm={Flow Control: \texttt{\textbar\textbar} \texttt{\&\&}}]% {Error Handling: \texttt{\red\textbar\textbar}, \texttt{\red\&\&} and \texttt{\red{}exit}} \label{sld:error-handling} \begin{itemize} \item Suppose we want the user to provide exactly two parameters, and exit otherwise \item A common method of handling this is something like:% {\footnotesize% \begin{alltt} \red[ $# -eq 2 ] || \{ echo "Need two parameters"; exit 1; \} \end{alltt}%$ }% %\vspace*{-2.5ex} \item Read this as ``the number of parameters is two OR exit'' \item Works because this logical OR uses short-circuit Boolean evaluation; the second statement is executed only if the first fails (is false) \item Logical AND ``\texttt{\&\&}'' can be used in the same way; the second statement will be executed only if the first is successful (true) \item A note about blocks: must have semicolon ``\texttt{;}'' or newline at end of last statement before closing brace \end{itemize} \end{slide} \tsection{Input \& Output} \begin{slide}[toc={Output: \texttt{echo} and \texttt{printf}},% bm={Output: \texttt{echo} and \texttt{printf}}]% {Output: \texttt{\red{}echo} and \texttt{\red{}printf}} \begin{itemize} \item To perform output, use \texttt{\red{}echo}, or for more formatting, \texttt{\textbf{\red{}printf}}. \item Use \texttt{echo -n} to print no newline at end. \item Just \texttt{echo} by itself prints a newline \item \texttt{printf} works the same as in the C programming language, except no parentheses or commas: \begin{alltt} $ \textbf{printf "%16s\bs{}t%8d\bs{}n" $my_string $my_number} \end{alltt}%$'" \item Do \texttt{man printf} (or look it up in the \texttt{bash} manual page) to read all about it. \end{itemize} \end{slide} \begin{slide}[toc={Input: the \texttt{read} Command},% bm={Input: the \texttt{read} Command}]% {Input: the \texttt{\red{}read} Command} \begin{itemize} \item For input, use the built-in shell command \texttt{\red{}\textbf{read}} \item \texttt{read} reads standard input and puts the result into one or more variables \item If use one variable, variable holds the whole line \item Syntax: \begin{alltt} read \meta{var1}\ldots \end{alltt} %\vspace*{-3ex} \item Often used with a \texttt{while} loop like this: \begin{alltt} while read var1 var2 do # do something with $var1 and $var2 done \end{alltt} %\vspace*{-3ex} \item Loop terminates when reach end of file \item To prompt and read a value from a user, you could do: \begin{verbatim} while [ -z "$value" ]; do echo -n "Enter a value: " read value done # Now do something with $value \end{verbatim} \end{itemize} \end{slide} \begin{slide}[toc={Split with \texttt{set}},bm={Split with \texttt{set}}]% {\texttt{\red{}set}: Splitting a Multi-Word Variable} \begin{itemize} \item Sometimes may want to split a multi-word variable into single-word variables \item \texttt{read} won't work like this: \begin{alltt} MY_FILE_INFO=$(ls -lR | grep $file) # \ldots echo ${MY_FILE_INFO} | read perms links \bs user group size month day time filename \end{alltt}%$ %\vspace*{-2ex} \item Use the builtin command \texttt\textbf{\red{set}} instead: \begin{alltt} MY_FILE_INFO=$(ls -lR | grep $file) # \ldots set ${MY_FILE_INFO} perms=$1 links=$2 user=$3 group=$4 size=$5 month=$6 day=$7 time=$8 filename=$9 \end{alltt} \end{itemize} \end{slide} \begin{slide}[toc={More about \texttt{set}, and \texttt{IFS}}]% {More about \texttt{set}, and \texttt{IFS}} \label{sld:ifs} \begin{itemize} \item \texttt{set} splits its arguments into pieces (usually) at whitespace \item It sets the first value as \texttt{\$1}, the second as \texttt{\$2}, and so on. \item Note that you can change how \texttt{set} and the shell splits things up by changing the value of a special variable called \texttt{\red{}IFS} \item \texttt{IFS} stands for \emphcolour{Internal Field Separator} \item Normally the value of \texttt{IFS} is the string ``\meta{space}\meta{tab}\meta{newline}'' \item Next slide shows how changing \texttt{IFS} to a colon let us easily split the \texttt{PATH} into separate directories:\hfill$\rightsquigarrow$~{\tiny next~slide} \end{itemize} \end{slide} \begin{slide}[toc={Example: Changing \texttt{IFS}},% bm={Example: Changing \texttt{IFS}}]% {Example: Changing \texttt{\red{}IFS}} \label{sld:change-IFS} \begin{itemize} \item Notice that here, I make the change to \texttt{IFS} in a subshell. I have simply typed the loop at the prompt. \item As I said in slide~\ref{sld:vars-in-subshell}, changes in a subshell are local to the subshell: {\footnotesize% \begin{alltt} $ \textbf{echo $PATH} /usr/bin:/bin:/usr/X11R6/bin:/home/nicku/bin $ \textbf{(IFS=:} > \textbf{for dir in $PATH} > \textbf{do} > \textbf{ echo $dir} > \textbf{done} > \textbf{)} /usr/bin /bin /usr/X11R6/bin /home/nicku/bin \end{alltt}%$ } \end{itemize} \end{slide} \tsection{Command-line Parameters} \begin{slide}[toc={\texttt{case} Statement},bm={\texttt{case} Statement}]% {\texttt{\red{}case} Statement} \begin{itemize} \item Similar to the \texttt{switch} statement in C, but more useful and more general \item Uses pattern matching against a string to decide on an action to take \item Syntax: \begin{alltt} case \meta{expression} in \meta{pattern1} ) \meta{statements} ;; \meta{pattern2} ) \meta{statements} ;; \ldots esac \end{alltt} \end{itemize} \end{slide} \begin{slide}{\texttt{case} Statement: Example} \begin{itemize} \item This example code runs the appropriate program on a graphics file, depending on the file extension, to convert the file to another format: \end{itemize} %\vspace*{-2.5ex} \begin{alltt} case $filename in *.tif) tifftopnm $filename > $ppmfile ;; *.jpg) tjpeg $filename > $ppmfile ;; *) echo -n "Sorry, cannot handle this " echo "graphics format" ;; esac \end{alltt}%$ \end{slide} \begin{slide}[toc={\texttt{shift} Up},bm={\texttt{shift} Up}]% {\texttt{\red{}shift}: Move all Parameters Up} \begin{itemize} \item Sometimes we want to process command-line parameters in a loop \item The \texttt{\textbf{\red{}shift}} statement is made for this %\item Say that we have four parameters: \texttt{\$1} has the value % \texttt{one}, \texttt{\$2} has the value \texttt{two}, % \texttt{\$3} has the value \texttt{three} and \texttt{\$4} has the % value \texttt{four} \item Say that we have four parameters: \par\smallskip\par% \begin{tabular}[t]{@{}>{\ttfamily}l>{\ttfamily}ll% >{\ttfamily}l>{\ttfamily}l@{}} \toprule% \textnormal{\textbf{parameter}} & \textnormal{\textbf{value}} && \textnormal{\textbf{parameter}} & \textnormal{\textbf{value}}\\ \cmidrule(r{0.75em}){1-2} \cmidrule(l{0.25em}){4-5}% \$1 & one && \$3 & three \\ \$2 & two && \$4 & four \\ \bottomrule \end{tabular} \par\smallskip\par% \item Then after executing the \texttt{shift} statement, the values are now: \par\smallskip\par% \begin{tabular}[t]{@{}>{\ttfamily}l>{\ttfamily}ll% >{\ttfamily}l>{\ttfamily}l@{}} \toprule% \textnormal{\textbf{parameter}} & \textnormal{\textbf{value}} && \textnormal{\textbf{parameter}} & \textnormal{\textbf{value}}\\ \cmidrule(r{0.75em}){1-2} \cmidrule(l{0.25em}){4-5}% \$1 & two && \$3 & four \\ \$2 & three && \$4 & \textnormal{\emph{no longer exists}} \\ \bottomrule \end{tabular} %\texttt{\$1} has the value \texttt{two}, \texttt{\$2} has the %value \texttt{three} and \texttt{\$3} has the value \texttt{four}, %and \texttt{\$4} no longer exists. \end{itemize} \end{slide} \begin{slide}{\texttt{shift}: Many Places} \begin{itemize} \item You can give a number argument to \texttt{shift}: \begin{itemize} \item If before, we have four parameters: \par\smallskip\par% \begin{tabular}[t]{@{}>{\ttfamily}l>{\ttfamily}ll% >{\ttfamily}l>{\ttfamily}l@{}} \toprule% \textnormal{\textbf{parameter}} & \textnormal{\textbf{value}} && \textnormal{\textbf{parameter}} & \textnormal{\textbf{value}}\\ \cmidrule(r{0.75em}){1-2} \cmidrule(l{0.25em}){4-5}% \$1 & one && \$3 & three \\ \$2 & two && \$4 & four \\ \bottomrule \end{tabular} \par\smallskip\par% %\texttt{\$1} has the %value \texttt{one}, \texttt{\$2} has the value \texttt{two}, %\texttt{\$3} has the value \texttt{three} and \texttt{\$4} has %the value \texttt{four} \item After executing the statement: \begin{alltt} $ \textbf{shift 2} \end{alltt}%$ we have two parameters left: \par\smallskip\par% \begin{tabular}[t]{@{}>{\ttfamily}l>{\ttfamily}ll% >{\ttfamily}l>{\ttfamily}l@{}} \toprule% \textnormal{\textbf{parameter}} & \textnormal{\textbf{value}} && \textnormal{\textbf{parameter}} & \textnormal{\textbf{value}}\\ \cmidrule(r{0.75em}){1-2} \cmidrule(l{0.25em}){4-5}% \$1 & three && \$3 & \textnormal{\emph{no longer exists}} \\ \$2 & four && \$4 & \textnormal{\emph{no longer exists}} \\ \bottomrule \end{tabular} %\texttt{\$1} has the value %\texttt{three} and \texttt{\$2} has the value \texttt{four}; %\texttt{\$3} and \texttt{\$4} no longer exist. \end{itemize} \end{itemize} \end{slide} \begin{slide}{Command-Line Options---1} \begin{itemize} \item Sometimes we want to modify the behaviour of a shell script \begin{itemize} \item For example, want an \emphcolour{option} to show more information on request \item could use an option ``\texttt{-v}'' (for ``verbose'') to tell the shell script that we want it to tell us more information about what it is doing \item If script is called \texttt{showme}, then we could use our \texttt{-v} option like this: \begin{alltt} $ \textbf{showme -v} \end{alltt}%$ \item the script then shows more information. \end{itemize} \end{itemize} \end{slide} \begin{slide}{Command-Line Options---2} \begin{itemize} \item For example, We might provide an option to give a starting point for a script to search for \SUID programs \item Could make the option \texttt{-d \meta{directory}} \item If script is called \texttt{findsuid}, could call it like this: \begin{alltt} $ \textbf{findsuid -d /usr} \end{alltt}%$ to tell the script to start searching in the directory \texttt{/usr} instead of the current directory \end{itemize} \end{slide} \begin{slide}{Command-Line Options---3} \begin{itemize} \item We could do this using \texttt{shift}, a \texttt{while} loop, and a \texttt{case} statement, like this: \begin{alltt} while [ -n "$(echo $1 | grep '-')" ] do case $1 in -v) VERBOSE=1 ;; -d) shift DIRECTORY=$1 ;; *) echo "usage: $0 [-v] [-d dir]" exit 1 ;; esac shift done \end{alltt}%$ \end{itemize} \end{slide} \begin{slide}[toc={\texttt{getopts}---4},bm={\texttt{getopts}---4}]% {\texttt{\red{}getopts}: Command-Line Options---4} \begin{itemize} \item Problems with above solution: inflexibility: \begin{itemize} \item Does not allow options to be ``bundled'' together like \texttt{-abc} instead of \texttt{-a -b -c} \item Requires a space between option and its argument, i.e., doesn't let you do \texttt{-d/etc} as well as \texttt{-d /etc} %% \item Other parameters must come last; they cannot be mixed up %% with the options \item Better method: use the built-in command \texttt{\textbf{\red{}getopts}}: {\footnotesize% \begin{alltt} while getopts ":vd:" opt do case opt in v) VERBOSE=1 ;; d) DIRECTORY=$OPTARG ;; *) echo "usage: $0 [-v] [-d dir]" exit 1 ;; esac done shift $((OPTIND - 1)) \end{alltt}%$ } \end{itemize} \end{itemize} \end{slide} % This is what emacs' shell script mode does for insert->options: %% while getopts ::vd: OPT; do %% case $OPT in %% :|+:) %% ;;v|+v) %% ;;d|+d) %% "$OPTARG" %% ;;*) %% echo "usage: ${0##*/} [+- ARG] [+-vd ARG} [--] ARGS..." %% exit 2 %% esac %% done %% shift $[ OPTIND - 1 ] %$ \begin{slide}[toc=\texttt{getopts}---5]% {\texttt{getopts}: Command-Line Options---5} \begin{itemize} \item \texttt{getopts} takes two arguments: \begin{itemize} \item first comes the string that can contain letters and colons. \begin{itemize} \item Each letter represents one option \item A colon comes after a letter to indicate that option takes an arguement, like \texttt{-d \texttt{directory}} \item A colon at the beginning makes \texttt{getopts} less noisy, so you can provide your own error message, as shown in the example. \end{itemize} \item The second is a variable that will hold the option (without the hyphen ``\texttt{-}'') \end{itemize} \item Shift out all processed options using the variable \texttt{OPTIND}, leaving any other arguments accessible \item Search for \texttt{getopts} in the \texttt{bash} man page \end{itemize} \end{slide} \tsection{Temporary Files, Signals} \begin{slide}{Temporary Files: \texttt{mktemp}} \label{sld:mktemp} \begin{itemize} \item Sometimes it is convenient to store temporary data in a temporary file \item The \texttt{\textbf{\red{}mktemp}} program is designed for this \item We use it something like this: \begin{alltt} TMPFILE=$(mktemp /tmp/temp.XXXXXX) || exit 1 \end{alltt}%$ \item \texttt{mktemp} will create a new file, replacing the ``\texttt{XXXXXX}'' with a random string \item Do \texttt{man mktemp} for the complete manual. \end{itemize} \end{slide} \begin{slide}[toc={Signals that Kill}]{Signals that may Terminate your Script} \begin{itemize} \item Many key strokes will send a \emphcolour{signal} to a process \item Examples: \begin{itemize} \item \key{Control-C} sends a \texttt{\red{}SIGINT} signal to the current process running in the foreground \item \key{Control-\bs} sends a \texttt{\red{}SIGQUIT} signal \end{itemize} \item When you log out, all your processes are sent a \texttt{\red{}SIGHUP} (hangup) signal \item If your script is connected to another process that terminates unexpectedly, it will receive a \texttt{\red{}SIGPIPE} signal \item If anyone terminates the program with the \texttt{\red{}kill} program, the default signal is \texttt{\red{}SIGTERM} \end{itemize} \end{slide} \begin{slide}{Signals: \texttt{trap}} \begin{itemize} \item Sometimes you want your script to clean up after itself nicely, and remove temporary files \item Do this using \texttt{\red\textbf{trap}} \end{itemize} \end{slide} \begin{slide}{Signals: \texttt{trap} Example} \begin{itemize} \item Supose your script creates some temporary files, and you want to remove them if your script recieves any of these signals \item You can ``catch'' the signal, and remove the files when the signals are received before the program terminates \item Suppose the temporary files have names stored in the variables \texttt{TEMP1} and \texttt{TEMP2} \item Then you would trap these signals like this: \begin{alltt} trap "rm $TEMP1 $TEMP2" HUP INT QUIT PIPE TERM \end{alltt} %\vspace*{-2.5ex} \item Conveniently, (but not very portably), \texttt{bash} provides a ``pretend'' signal called \texttt{EXIT}; can add this to the list of signals you trap, so that the temporary files will be removed when the program exits normally. \end{itemize} \end{slide} \tsection{Functions} \begin{slide}{Functions} \begin{itemize} \item The shell supports functions and function calls \item {\mbox{}\green{}A function works like an external command}, except that it does not start another process \item Syntax: \begin{alltt} function \meta{functname} \{ \meta{shell commands} \} \end{alltt} %\vspace*{-2.5ex} Or: \begin{alltt} \meta{functname} () \{ \meta{shell commands} \} \end{alltt} \end{itemize} \end{slide} \begin{slide}{Parameters in Functions} \begin{itemize} \item Work the same as parameters to entire shell script \item First parameter is \texttt{\$1}, second is \texttt{\$2},\ldots, the tenth parameter is \texttt{\$\{10\}}, and so on. \item \texttt{\$\#} is the number of parameters passed to the function \item As with command line parameters, they are read-only \item Assign to meaningful names to make your program more understandable \end{itemize} \end{slide} \begin{slide}{Example, Calling a Function} \begin{itemize} \item This is a simple example program: \begin{verbatim} #! /bin/sh function cube { echo $(($1 * $1 * $1)) } j=$(cube 5) echo $j # Output is 125 \end{verbatim} \item Note the use of \emphcolour{command substitution to get a return value} \item The \emphcolour{function prints result to standard output}. \end{itemize} \end{slide} \tsection{Debugging} \begin{slide}{Debugging Shell Scripts---1} \begin{itemize} \item If you run the script with: \begin{alltt} $ \textbf{sh -v \meta{script}} \end{alltt}%$ then each statement will be printed as it is executed \item If you run the script with: \begin{alltt} $ \textbf{sh -x \meta{script}} \end{alltt}%$ then an execution trace will show the value of all variables as the script executes. \end{itemize} \end{slide} \begin{slide}{Debugging Shell Scripts---2} \begin{itemize} \item Use \texttt{echo} to display the value of variables as the program executes \item You can turn the \texttt{\red{}-x} shell option on in any part of your script with the line: \begin{alltt} set -x \end{alltt} and turn it \emphcolour{off} with: \begin{alltt} set +x \end{alltt} \begin{itemize} \item No, that's not a typo: \texttt{\red{}+x} turns it \emphcolour{off}, \texttt{\red{}-x} turns it \emphcolour{on}. \end{itemize} \item The book \emph{Learning the bash Shell} includes a \texttt{bash} shell debugger if you get desperate \end{itemize} \end{slide} \begin{slide}{Writing Shell Scripts} \begin{itemize} \item Build your shell script \emphcolour{incrementally}: \begin{itemize} \item Open the {\red{}editor} in one window (\emph{and {\red{}leave} it open}), have a terminal window open in which to {\green{}run your program as you write it} \item \emphcolour{Test as you implement}: this makes shell script development \emphcolour{easy} \item Do \emphcolour{not} write a very complex script, and \emph{\blue{}then} begin testing it! \end{itemize} \item Use the standard software engineering practice you know: \begin{itemize} \item Use \emphcolour{meaningful} variable names, function names \item Make your program \emphcolour{self-documenting} \item Add \emphcolour{comment blocks} to explain obscure or difficult parts of your program \end{itemize} \end{itemize} \end{slide} \begin{slide}{Useful External Programs---1} Each of these has a manual page, and many have info manuals. Read their online documentation for more information. \begin{itemize} \item \texttt{awk} --- powerful tool for processing columns of data \item \texttt{basename} --- remove directory and (optionally) extension from file name \item \texttt{cat} --- copy to standard output \item \texttt{cut} --- process columns of data \item \texttt{du} --- show disk space used by directories and files \item \texttt{egrep}, \texttt{grep} --- find lines containing patterns in files \item \texttt{find} --- find files using many criteria \end{itemize} \end{slide} \begin{slide}{Useful External Programs---2} \begin{itemize} \item \texttt{last} --- show the last time a user was logged in \item \texttt{lastb} --- show last bad log in attempt by a user \item \texttt{rpm} --- \RPM package manager: manage software package database \item \texttt{sed} --- stream editor: edit files automatically \item \texttt{sort} --- sort lines of files by many different criteria \item \texttt{tr} --- translate one set of characters to another set \item \texttt{uniq} --- replace repeated lines with just one line, optionally with a count of the number of repeated lines \end{itemize} \end{slide} \tsection{Regular Expressions} \begin{slide}{Regular Expressions} \begin{itemize} \item Many programs and programming languages use \emphcolour{regular expressions}, including {\green{}Java} 1.4 and later, {\green{}Perl}, {\green{}VB.NET}, {\green{}C\#} (and any language using the .NET Framework), {\green{}PHP}, {\green{}Python}, {\green{}Ruby}, {\green{}Tcl} and {\green{}MySQL} (plus many others; even {\green{}MS~Word} uses regular expressions under \mbox{\underline{E}dit $\to$ \underline{F}ind $\to$ \underline{M}ore $\to$ \underline{U}se wildcards}) \item These programs use regular expressions: \begin{itemize} \item \texttt{\textbf{\green{}grep}}, \texttt{\textbf{\green{}egrep}}, \texttt{\textbf{\green{}sed}}, \texttt{\textbf{\green{}awk}} \end{itemize} \item All programmer's editors support regular expressions ({\green{}Emacs}, {\green{}vi},\,\ldots) \item \emph{Regular expressions} provide a \emphcolour{powerful language} for \emphcolour{manipulating data} and \emphcolour{extracting important information} from masses of data \end{itemize} \end{slide} \begin{slide}[toc={What is In a RegEx?}]{What is In a Regular Expression?} \begin{itemize} \item There are two types of character in a regular expression: \begin{itemize} \item \emphcolour{Metacharacters} \begin{itemize} \item These include: \item {\mbox{}\red{}\texttt{*} \texttt{\bs} \texttt{.} \texttt{+} \texttt{?} \texttt{\textasciicircum} \texttt{(} \texttt{)} \texttt{[} \texttt{\{} \texttt{\textbar}} \end{itemize} \item Ordinary, \emphcolour{literal} characters: \begin{itemize} \item i.e., all the other characters that are not metacharacters \end{itemize} \end{itemize} \end{itemize} \end{slide} \begin{slide}{Literal characters} \begin{itemize} \item Find all lines containing "\texttt{chan}" in the password file: \begin{alltt} $ \textbf{grep chan /etc/passwd} \end{alltt}%$ \item The \emphcolour{regular expression} is "\texttt{\blue{}chan}" \item It is made entirely of literal characters \item It matches only lines that contain the \emphcolour{exact} string \item It will match lines containing the words \texttt{\red{}chan}, \texttt{{\red{}chan}ged}, \texttt{mer{\red{}chan}t}, \texttt{me{\red{}chan}ism},\ldots \end{itemize} \end{slide} \begin{slide}{Character Classes: \texttt{[\ldots]}} \begin{itemize} \item A character class represents \emph{one} character \item Examples:% {\footnotesize% \begin{alltt} # Find all words in the dictionary that contain a vowel: $ \textbf{grep "[aeiou]" /usr/share/dict/words} # Find all lines that contain a digit: $ \textbf{grep "[0123456789]" /usr/share/dict/words} # Find all lines that contain a digit: $ \textbf{grep "[0-9]" /usr/share/dict/words} # Find all lines that contain a capital letter: $ \textbf{grep "[A-Z]" /usr/share/dict/words} \end{alltt}% } \end{itemize} \end{slide} \begin{slide}[toc=\texttt{[\textasciicircum\ldots]}]% {Negated Character Classes: \texttt{[\textasciicircum\ldots]}} \begin{itemize} \item Examples of negated character classes: {\small% \begin{alltt} # Find all words in the dictionary # that contain a character that is not a vowel: $ \textbf{grep "[^aeiou]" /usr/share/dict/words} # Two ways of finding all lines that contain # a character that is not a digit: $ \textbf{grep "[^0123456789]" /usr/share/dict/words} $ \textbf{grep "[^0-9]" /usr/share/dict/words} # Find all lines that contain a character # that is not a digit, or a letter $ \textbf{grep "[^0-9a-zA-Z]" /usr/share/dict/words} \end{alltt}% } \item Remember: each set of square brackets represents exactly \emphcolour{one} character. \end{itemize} \end{slide} \begin{slide}{Match Any Character} \begin{itemize} \item The dot ``\texttt{.}'' matches \emphcolour{any single character}, except a newline. \item The pattern `\texttt{.....}' matches all lines that contain at least five characters \end{itemize} \end{slide} \begin{slide}[toc={Match Start or End}]{Matching the Beginning or End of Line} \begin{itemize} \item To match a line that contains exactly five characters: \begin{alltt} $ \textbf{grep '^.....$' /usr/share/dict/words} \end{alltt} \item The hat, {\red\verb+^+} represents the \emphcolour{position} right at the \emphcolour{start} of the line \item The dollar \texttt{\red\$} represents the \emphcolour{position} right at the \emphcolour{end} of the line. \item Neither \verb+^+ nor \texttt{\$} represents a character \item {\mbox{}\green{}They represent a position} \item Sometimes called \emphcolour{anchors}, since they anchor the other characters to a specific part of the string \end{itemize} \end{slide} \begin{slide}[toc={Repetitions}]{Match Repetitions: \texttt{*}, \texttt{?}, \texttt{+}, \texttt{\{n\}}, \texttt{\{n,m\}}} \begin{itemize} \item To match \emphcolour{zero} or more: \item \texttt{a*} represents zero or more of the lower case letter \texttt{a}, so the pattern will match \texttt{""} (the empty string), ``\texttt{a}'', ``\texttt{aa}'', ``\texttt{aaaaaaaaaaaaaaa}'', ``\texttt{qwewtrryu}'' or the ``nothing'' in front of any string! \item To match \emphcolour{one} or more: \item `\texttt{a+}' matches one or more ``\texttt{a}''s \item `\texttt{a?}' matches zero or one ``\texttt{a}'' \item `\texttt{a\{10\}}' matches exactly 10 ``\texttt{a}''s \item `\texttt{a\{5,10\}}' matches between 5 and 10 (inclusive) ``\texttt{a}''s \end{itemize} \end{slide} \begin{slide}{Matching Alternatives: ``\texttt{\textbar}''} \begin{itemize} \item the vertical bar represents alternatives: \item The regular expresssion `\texttt{nick|albert|alex}' will match either the string ``\texttt{nick}'' or the string ``\texttt{albert}'' or the string ``\texttt{alex}'' \item Note that the vertical bar has very low precedence: \item the pattern `\texttt{\textasciicircum{}fred\textbar{}nurk}' matches ``\texttt{fred}'' only if it occurs at the start of the line, while it will match ``\texttt{nurk}'' at any position in the line \end{itemize} \end{slide} \begin{slide}[toc=Examples]{Putting it All Together: Examples} \begin{itemize} \item Find all words that contain at least three `\texttt{a}'s: \begin{alltt} $ \textbf{egrep 'a.*a.*a' /usr/share/dict/words} \end{alltt}%$ \begin{itemize} \item Why is this different from \begin{alltt} $ \textbf{egrep 'aaa' /usr/share/dict/words} \end{alltt}%$ \end{itemize} \item Find all words that begin in `\texttt{a}' and finish in `\texttt{z}', ignoring case: \begin{alltt} $ \textbf{egrep -i '^a.*z$' /usr/share/dict/words} \end{alltt}%$ \begin{itemize} \item How is this different from: \begin{alltt} $ \textbf{egrep -i '^a.*z' /usr/share/dict/words} \end{alltt}%$ \end{itemize} \item Find all words that contain at least two vowels: \begin{alltt}\small $ \textbf{grep '[aeiou].*[aeiou]' /usr/share/dict/words} \end{alltt}%$ \item Find all words that contain \emph{exactly} two vowels: \begin{alltt} $ \textbf{egrep \bs '^[^aeiou]*[aeiou][^aeiou]*[aeiou][^aeiou]*$' \bs /usr/share/dict/words} \end{alltt}%$ \item Find all lines that are empty, or contain only spaces: \begin{alltt} $ \textbf{grep '^\ *$' file} \end{alltt}%' \end{itemize} \end{slide} \tsection{\texttt{awk} and \texttt{sed}} \begin{slide}{Basic \texttt{awk}} \begin{itemize} \item \texttt{\textbf{\red{}awk}} is a complete programming language \item Mostly used for one-line solutions to problems of \emphcolour{extracting columns of data} from text, and processing it \item A complete book is available on \texttt{awk}; you can buy it here: \url{http://www.oreilly.com/catalog/awkprog3/} or \item read it on your computer, as it is the official manual for \texttt{gawk} (\GNU awk); do \begin{alltt} $ \textbf{info gawk} \end{alltt}%$ or read it in Emacs. \begin{itemize} \item A printable postscript file of the book (353 pages) is on my computer at \path{/usr/share/doc/gawk-3.1.3/gawk.ps} \end{itemize} %\url{http://www.ssc.com/ssc/eap/}. \end{itemize} \end{slide} \begin{slide}{What Does \texttt{awk} Do?} \begin{itemize} \item \texttt{awk} reads file(s) or standard input \emphcolour{one line at a time}, and \item automatically \emph{\blue{}splits the line into fields}, and calls them \texttt{\$1}, \texttt{\$2},\ldots, \texttt{\$NF} \item \texttt{NF} is equal to the \emph{\blue{}number of fields} the line was split into \item \texttt{\$0} contains the \emph{\blue{}whole line} \item \texttt{awk} has an option \texttt{-F} that allows you to select another pattern as the \emph{\blue{}field separator} \begin{itemize} \item Normally \texttt{awk} splits \emph{\blue{}columns} by \emph{\blue{}white space} \end{itemize} \item To execute code after all lines are processed, create an \emph{\blue{}\texttt{END} block}. \end{itemize} \end{slide} \begin{slide}{\texttt{awk} Examples} \begin{itemize} \item Print the sizes of all files in current directory: \begin{alltt} \textbf{ls -l | awk '\{print $5\}'} \end{alltt} \item Add the sizes of all files in current directory: \begin{alltt} \textbf{ls -l | awk '\{sum += $5\} END\{print sum\}'} \end{alltt} \item Print only the permissions, user, group and file names of files in current directory: \begin{alltt} \textbf{ls -l | awk '\{print $1, $3, $4, $NF\}'} \end{alltt} \end{itemize} \end{slide} \begin{slide}{\texttt{sed}---the Stream Editor} \begin{itemize} \item \texttt{sed} provides many facilities for editing files \item The \emphcolour{substitute} command, \texttt{\red{}s///}, is the most important \item The syntax (using \texttt{sed} as an editor of standard input), is: \begin{alltt} $ \textbf{sed 's/\meta{original}/\meta{replacement}/'} \end{alltt}%$ \item Example: replace the first instance of \texttt{Windows} with \texttt{Linux} on each line of the input: \begin{alltt} \textbf{sed 's/Windows/Linux/'} \end{alltt} \item Example: replace \emph{all} instances of \texttt{Windows} with \texttt{Linux} on each line of the input: \begin{alltt} \textbf{sed 's/Windows/Linux/g'} \end{alltt} \begin{itemize} \item Note: by default, \texttt{sed} uses ``basic regular expressions'', which require a backslash `\bs' in front of the metacharacters '\{', `\texttt{(}', `\texttt{)}', `\texttt{\textbar}', `\texttt{+}' and `\texttt{?}'. \item To use ``extended regular expressions'' (which we covered here), call \texttt{sed} with the option \texttt{-r}, as in this example: \begin{alltt} $ \textbf{sed -r s/a+//} \end{alltt}%$ \end{itemize} \end{itemize} \end{slide} \begin{slide}{\texttt{sed}---Backreferencees} \begin{itemize} \item You can match part of the \texttt{\meta{original}} in a \texttt{sed -r} substitute command, and put that part back into the replacement part. \item You enclose the part you want to refer to later in \texttt{(\ldots)} \item You can get the first value in the replacement part by \texttt{\bs1}, the second opening parenthesis of \texttt{(\ldots)} by \texttt{\bs2}, and so on. \end{itemize} \end{slide} \begin{slide}[toc={Backrefs Example}]{\texttt{sed}---Backreferencees: Example} \begin{itemize} \item If you do \begin{alltt} $ \textbf{find /etc | xargs file -b} \end{alltt}%$ you will get a lot of output like this: \begin{alltt} symbolic link to bg5ps.conf.zh_TW.Big5 symbolic link to rc.d/rc.local symbolic link to rc.d/rc symbolic link to rc.d/rc.sysinit symbolic link to ../../X11/xdm/Xservers \end{alltt} %\vspace*{-2.5ex} \item If you want to edit each line to remove everything after ``\texttt{symbolic link}'', then you could pipe the data through \texttt{sed} like this: \begin{alltt} $ \textbf{find /etc | xargs file -b \bs | sed -r 's/(symbolic link).*/\bs1/'} \end{alltt}%$ %\vspace*{-2.5ex} \item See slide~\ref{sld:find-examples-1} for an application \end{itemize} \end{slide} \tsection{\texttt{find}} \begin{slide}{\texttt{find} Examples} \label{sld:find-examples-1} \begin{itemize} \item Count the number of unique manual pages on the computer: \begin{alltt} $ \textbf{find /usr/share/man -type f | wc -l} \end{alltt} \item Print a table of types of file under the \texttt{/etc} directory, with the most common file type down at the bottom: \begin{alltt} $ \textbf{find /etc | xargs file -b \bs | sed -r 's/(symbolic link).*/\bs1/' \bs | sort \bs | uniq -c \bs | sort -n} \end{alltt} \end{itemize} \end{slide} \begin{slide}{Finding SUID Programs} \begin{itemize} \item Finding \SUID or \SGID files: \begin{alltt} $ \textbf{sudo find / -type f \bs \bs( perm -2000 -o -perm -4000 \bs) \bs > files.secure} \end{alltt}%$ \item Let's compare with a list of \SUID and \SGID files to see if there are any changes, since \SUID and \SGID programs can be a \emphcolour{security risk}: \begin{alltt} $ \textbf{sudo find / -type f \bs \bs( perm -2000 -o -perm -4000 \bs) \bs | diff - files.secure} \end{alltt}%$ \end{itemize} \end{slide} \begin{slide}[toc={Long \texttt{find} Example}]% {A \texttt{find} Example with Many Options} \begin{itemize} \item Set all directories to have the access mode 771, set all backup files (*.BAK) to mode 600, all shell scripts (*.sh) to mode 755, and all text files (*.txt) to mode 644: \end{itemize} {\footnotesize% \begin{alltt} $ \textbf{find . \bs( -type d -a exec chmod 771 \{\} \bs; \bs) -o \bs \bs( -name "*.BAK" -a exec chmod 600 \{\} \bs; \bs) -o \bs \bs( -name "*.sh" -a exec chmod 755 \{\} \bs; \bs) -o \bs \bs( -name "*.txt" -a exec chmod 644 \{\} \bs; \bs)} \end{alltt}%$ } \end{slide} \begin{slide}[toc=\texttt{rpm} Queries]{\texttt{rpm} Database Query Commands} \begin{itemize} \item The \texttt{rpm} software package management system includes a database with very detailed information about every file of every software package that is installed on the computer. \item You can query this database using the \texttt{rpm} command. \item The manual page does not give the complete picture, but there is a book called \emph{Maximum RPM} that comes on the Red Hat documentation \CD \item This package is installed on \texttt{ictlab} \item \sloppypar{}You can see the appropriate section at this \URL: {\tiny\url{http://ictlab.tyict.vtc.edu.hk/doc/maximum-rpm-1.0/html/s1-rpm-query-parts.html}} \end{itemize} \end{slide} \end{document}