\input{gl2.slide-header-beamer}% \errorcontextlines=99 %% Subtopic Number = '1.109.2' %% Title: 'Customize or write simple scripts' %% Weight: 3 %% Description: %% Candidate should be able to customize existing scripts, or %% write simple new (ba)sh scripts. This objective includes using standard %% sh syntax (loops, tests), using command substitution, testing command %% return values, testing of file status, and conditional mailing to the %% superuser. This objective also includes making sure the correct %% interpreter is called on the first (#!) line of scripts. This objective %% also includes managing location, ownership, execution and suid-rights of %% scripts. %% Key files, terms, and utilities include: %% while %% for %% test %% chmod \title{1.109.2\\Customize or write simple scripts\\Weight 3} \date{2005 November} \author[Nick Urbanik]{Nick Urbanik \texttt{}\\ {\scriptsize This document Licensed under GPL---see section~\ref{sec:license}}} \subtitle{Linux Professional Institute Certification --- 102}% \mode
{\chead{1.109.2}}% \usepackage{varioref} \begin{document} \maketitle \mode
{\thispagestyle{empty}} \mode* \begin{frame} \frametitle{Outline} \mode {% %\footnotesize \begin{multicols}{2} \tableofcontents \end{multicols} % You might wish to add the option [pausesections] }% \mode
{% \tableofcontents }% \end{frame} \section{Context} \label{sec:context} \begin{frame} \frametitle{Topic 109 Shells, Scripting, Programming and Compiling [8]}% \framesubtitle{Where we are up to}% \begin{description} \item[1.109.1] Customize and use the shell environment [5] % \uline depends on \usepackage[normalem]{ulem}: \item[1.109.2] \textbf{\uline{Customize or write simple scripts [3]}} \end{description} \end{frame} \section{Objectives} \label{sec:objectives} \begin{frame} \frametitle{Description of Objective}% \framesubtitle{1.109.2\ \ Customize or write simple scripts [3]}% \mode{\Large}% Candidate should be able to customize existing scripts, or write simple new (ba)sh scripts. This objective includes using standard sh syntax (\hyperlink{sec:while-statement}{\alert{loops}}, \hyperlink{sec:test}{\alert{tests}}), using \hyperlink{sld:command-substitution}{\alert{command substitution}}, testing \hyperlink{sld:built-in-variables}{\alert{command return values}}, testing of \hyperlink{sld:file-tests}{\alert{file status}}, and \hyperlink{sld:alert-by-mail}{conditional \alert{mailing to the superuser}}. This objective also includes \hyperlink{sld:shebang}{making sure the correct interpreter is called on the first (\texttt{\#!}) line of scripts}. This objective also includes managing \hyperlink{sec:chmod+x}{\alert{location}, \alert{ownership}, execution} and \hyperlink{sld:suid-scripts}{\alert{suid-rights}} of scripts. \end{frame} \begin{frame}[fragile] \frametitle{Key files, terms, and utilities include:}% \framesubtitle{1.109.2\ \ Customize or write simple scripts [3]}% \mode{\large}% \begin{description} \item[{while}] --- shell builtin: does things repetively while a condition is true \item[{for}] --- shell builtin: does things repetively, once with each element of a list \item[{test}] --- used to construct a condition \item[{chmod}] --- an external command, to change the permission on a file \end{description} \end{frame} \section{The shebang: \texttt{\#!}} \label{sec:shebang} \begin{frame}[fragile] \frametitle{The Shebang: \texttt{\#!}}% \label{sld: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{frame} For shell scripts, the interpreter is \texttt{/bin/sh}, so the first line of all our shell scripts is: \begin{verbatim} #! /bin/sh \end{verbatim} If you make any typing mistake in the name of the interpreter, you will get an error message such as ``bad interpreter: No such file or directory.'' \section{Making the script executable} \mode{\label{sec:chmod+x}} \begin{frame} \frametitle{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: \par% \cmdbox{mkdir \(\sim\)/bin} \item If your script is called \texttt{script}, then this command will make it executable: \par% \cmdbox{chmod +x script} \end{itemize} \end{frame} \section{Should you make a script SUID?} \label{sec:suid-scripts} \begin{frame} \frametitle{Should you make a script SUID?}% \label{sld:suid-scripts}% \begin{itemize} \item Normally, when \alert{you} run a script, the process is owned by \alert{you}, and has the \alert{same access rights as you} \item If a script has the SUID permission, then: \begin{itemize} \item it does not matter who executes it! \item the owner of the process is the owner of the file \item This is \alert{very dangerous}, especially if the \alert{owner of the file is \texttt{root}!} \end{itemize} \item \alert{Never} make a shell script SUID, unless you really, really know what the risks are and how to avoid them \item Instead, write it in a language such as Perl, with taint checking, and make it as simple as possible. \item See Topic \emph{1.114.1 Perform security administration tasks} for details of manipulating SUID/SGID permissions. \end{itemize} \end{frame} \section{True and False} \label{sec:true-and-false} \begin{frame} \frametitle{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{frame} \section{Shell Variables} \label{sec:variables} \begin{frame}[fragile] \frametitle{Variables---1} \label{sld:variables} \begin{itemize} \item Variables not declared; they just appear when assigned to \item \blue{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 \blue{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{frame} \begin{frame}[fragile] \frametitle{Variables---Assignments} \begin{itemize} \item You can put \alert{multiple assignments} \blue{on one line:} \par% \texttt{i=0 j=10 k=100} \item You can \alert{set a variable temporarily} \blue{while executing a program:} \begin{alltt} $ \textbf{echo $EDITOR} emacsclient $ \textbf{EDITOR=gedit crontab -e} $ \textbf{echo $EDITOR} emacsclient \end{alltt}%$ \end{itemize} \end{frame} \begin{frame}[fragile] \frametitle{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 \alert{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{frame} \begin{frame} \frametitle{Variables---\texttt{\textbf{unset}}ting Them} \begin{itemize} \item You can make a variable hold the null string by assigning it to nothing, but it does not disappear totally: \cmdbox{VAR=} \cmdbox{env | grep '\textasciicircum{}VAR'}\\ \texttt{VAR=} \item You can make it disappear totally using \texttt{\textbf{\red{unset}}}: \par% \cmdbox{unset VAR}\\ \cmdbox{env | grep '\textasciicircum{}VAR'} \end{itemize} \end{frame} \section{Special Variables} \label{sec:special-variables} \begin{frame} \frametitle{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: \par% \cmdbox{shell-script param1 param2 param3 param4} \par% {\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{frame} \begin{frame}[fragile] \frametitle{Special Built-in Variables} \label{sld: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{frame} % Parameters may be passed to a shell script. `\texttt{\$0}' is the % name of the shell script itself. The first parameter is % called `\texttt{\$1}', the second is `\texttt{\$2}' and so on. The % number of parameters is `\texttt{\$\#}'. A list of all the parameters % is in the variables `\texttt{\$*}' and `\texttt{\$@}'. The only % difference between `\texttt{\$*}' and `\texttt{\$@}' is when they are % enclosed in double quotes---see section \vref{pag:dollar-star-quoting} % on quoting. % \texttt{IFS} is the ``\emph{internal-field separator}''. The shell % automatically splits strings into fields divided by the % \texttt{IFS}\@. Here is a simple example: % \begin{verbatim} % $ (IFS=:; echo $PATH) % /usr/kerberos/bin /usr/local/bin /usr/bin /bin /usr/bin/X11 /usr/games % /usr/bin /usr/X11R6/bin /opt/OpenNMS/bin /usr/java/jdk1.3.1_01/bin % /home/nicku/bin /sbin /usr/sbin /usr/local/sbin % \end{verbatim} % I changed \texttt{IFS} in a subshell so that the value of \texttt{IFS} % in the current shell would not be changed. Sort of like a local variable. % \section{Special Characters} % \label{sec:special-characters} % Comments start with a `\texttt{\#}'. % Statements are separated either by newlines, or by semicolons % `\texttt{;}'. % The dot command is useful for \alert{sourceing} a login script: % \begin{verbatim} % . ~/.bash_profile % \end{verbatim} % It is useful here, because it does not execute the commands in a % separate subshell. Hence, all changes to variables remain. % The `\texttt{\$}' symbol indicates that a variable name comes next, % and gives the value of that variable. % The backslash \texttt{"\bs"} has many meanings, mostly similar to it's % behaviour in the C programming language. At the end of a line, a % backslash allows a long line to be split into shorter pieces. % There are many other characters that are special to the shell. See % chapter~4 of \url{http://tldp.org/LDP/abs/html/index.html}. \section{Quoting} \label{sec:quoting-and-special-chars} \subsection{Quoting and Funny Chars} \begin{frame} \frametitle{Special Characters}% \framesubtitle{Many characters have a special meaning to the shell}% % \begin{itemize} % \item Many characters are \emphcolour{special} to the shell, and % have a particular meaning to the shell. % \end{itemize} % \vspace*{-1ex} \mode{\footnotesize\par\vspace*{-1.6\baselineskip}\par}% \setlength{\extrarowheight}{0pt}% \noindent% \begin{tabular}[t]{@{}>{\ttfamily}cl@{}} \toprule% \multicolumn{1}{c}{\textbf{Character}} & \multicolumn{1}{c@{}}{\textbf{Meaning}} \\ \midrule% $\sim$ & Home directory \\ ` & Command substitution. Better: \texttt{\$(\ldots)} \\ \# & Comment \\ \$ & Variable expression \\ \& & Background Job \\ \texttt{*} & File name matching wildcard \\ \texttt{|} & Pipe \\ ( & Start subshell \\ ) & End subshell \\ {[} & Start character set file name matching \\ {]} & End character set file name matching \\ \{ & Start command block \\ ; & Command separator \\ \bs & Quote next character \\ ' & Strong quote \\ " & Weak quote \\ \texttt{<} & Redirect Input \\ \texttt{>} & Redirect Output \\ \texttt{/} & Pathname directory separator \\ ? & Single-character match in filenames \\ ! & Pipline logical NOT \\ \meta{space or tab} & shell normally splits at white space\\ \bottomrule \end{tabular} \end{frame} % \begin{frame} % \frametitle{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{frame} % \begin{frame} % \frametitle{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{frame} \subsection{Quoting} \label{sec:quoting} \begin{frame}[fragile] \frametitle{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{frame} \begin{frame}[fragile] \frametitle{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{frame} \begin{frame}[fragile] \frametitle{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{frame} \begin{frame} \frametitle{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: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{frame} \section{Command Substitution} \label{sec:command-substitution} \begin{frame}[fragile] \frametitle{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{frame} \begin{frame}[fragile] \frametitle{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} nicku.org $ \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} nicku.org \end{alltt}%$ \item We put \texttt{\$(\ldots)} around the command. You can then assign the output of the command. \end{itemize} \end{frame} % \section{The Basic Statements} % \label{sec:statements} \section{The if statement} \label{sec:if-statement} \begin{frame}[fragile] \frametitle{\texttt{\red{}if} Statement}% \label{sld:if-statement}% \begin{itemize} \item Syntax: {\mode{\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: {\mode{\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{frame} \section{\texttt{while} statement} \mode{\label{sec:while-statement}} \begin{frame}[fragile] \frametitle{\texttt{\red{}while} Statement}% \begin{itemize} \item Syntax: {\mode{\small}% \begin{alltt} while \meta{test-commands} do \meta{loop-body-statements} done \end{alltt} } \item Example: {\mode{\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{frame} \section{The for statement} \label{sec:for-statement} \begin{frame}[fragile] \frametitle{\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{frame} \begin{frame}[fragile] \frametitle{\texttt{\red{}for} Loops: Another Example}% %\mode{\small}% \begin{itemize} \item Here the shell turns \texttt{*.txt} into a list of file names ending in ``\texttt{.txt}'':% {\mode{\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:% {\mode{\small}% \begin{verbatim} i=0 for parameter do let 'i = i + 1' echo "parameter $i is $parameter" done \end{verbatim} } \end{itemize} \end{frame} \section{The \texttt{test} program} \mode{\label{sec:test}} % The \texttt{test} program is used to perform comparisons of strings, % numbers and files, often used with the \texttt{if} and \texttt{while} % statements. I will not waste space by copying the manual page here: % do \texttt{man test} to read all about \texttt{test}. % You can call the test program two ways: one as the name \texttt{test}, % the other (more common way) as \texttt{[ ... ]}. If we look, there is % a program called ``\texttt{[}'':%] % \begin{verbatim} % $ which [ % /usr/bin/[ % $ ls -l /usr/bin/[ % lrwxrwxrwx 1 root root 4 Nov 25 13:36 /usr/bin/[ -> test % \end{verbatim}%]]] % \paragraph{Important Note:} % there must be white space before and after the ``\texttt{[}'':%] % \begin{verbatim} % i=0 % while ["$i" -lt 10]; do % echo -n "$i " % i=`expr $i + 1` % done % bash: [0: command not found % \end{verbatim}%$] \subsection{Conditions} \begin{frame}[fragile] \frametitle{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: {\mode{\scriptsize}% \begin{alltt} [ "$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{frame} \begin{frame}[fragile] \frametitle{Conditions---Integer Comparisons}% \label{sld:test-arith}% \begin{itemize} \item Examples of \emphcolour{numeric} integer comparisons: {\mode{\small}% \begin{alltt} [ "$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{frame} \begin{frame}[fragile] \frametitle{Conditions---File Tests, NOT Operator}% \label{sld:file-tests}% \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} {\mode{\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{frame} \begin{frame}[fragile] \frametitle{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)} {\mode{\small} \begin{alltt} # \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 ``\texttt{[}`` and before the ``\texttt{]}'' are essential}. \item Do \texttt{man test} to see the information about all the operators. \end{itemize} \end{frame} \section{Arithmetic} \begin{frame}[fragile] \frametitle{Arithmetic Assignments}% \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: {\mode{\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{frame} \section{Input \& Output} \subsection{Output with \texttt{echo}} \label{sec:output} \begin{frame}[fragile] %\frametitle{Output: \texttt{\red{echo}} and \texttt{\red{printf}}}% \frametitle{Output with \texttt{\red{echo}}}% \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{frame} \subsection{Input with \texttt{read}} \label{sec:read} \begin{frame}[fragile] \frametitle{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{frame} \section
{Finding examples of shell scripts on your computer} \label{sec:examples} Your Linux system has a large number of shell scripts that you can refer to as examples. I counted about 1400. Here is one way of listing their file names: {\footnotesize \begin{verbatim} $ file /bin/* /usr/bin/* /usr/sbin/* /sbin/* /etc/rc.d/* /usr/X11R6/bin/* \ | grep -i "shell script" | awk -F: '{print $1}' \end{verbatim}%$ } Let's see how this works. I suggest executing the commands separately to see what they do: {\footnotesize \begin{verbatim} $ file /bin/* /usr/bin/* $ file /bin/* /usr/bin/* | grep -i "shell script" $ file /bin/* /usr/bin/* | grep -i "shell script" | awk -F: '{print $1}' \end{verbatim} } The \texttt{awk} program is actually a complete programming language. It is mainly useful for selecting columns of data from text. \texttt{awk} automatically loops through the input, and divides the input lines into fields. It calls these fields \texttt{\$1}, \texttt{\$2},\dots\texttt{\$NF}\@. \texttt{\$0} contains the whole line. Here the option \texttt{-F:} sets the \emph{field separator} to the colon character. Normally it is any white space. So printing \texttt{\$1} here prints what comes before the colon, which is the file name. Suppose you want to look for all shell scripts containing a particular command or statement? Looking for example shell scripts that use the \texttt{mktemp} command: {\footnotesize \begin{verbatim} $ file /bin/* /usr/bin/* /usr/sbin/* /sbin/* /etc/rc.d/* /usr/X11R6/bin/* \ | grep -i 'shell script'| awk -F: '{print $1}' | xargs grep mktemp \end{verbatim} } \section{Alerting about problems by email} \label{sec:alert-by-mail} \begin{frame}[fragile] \frametitle{Alerting about problems by email}% \label{sld:alert-by-mail}% \mode{\footnotesize} \begin{verbatim} #! /bin/sh # A quick script whipped up by Nick to send mail if # root file system is more than 90 per cent full. percentful=$(df / | awk 'NR > 1{sub("%", "", $5);print $5}') if [ "$percentful" -gt 90 ] then message="root file system is $percentful% full" echo "$message" | mail -s $message root fi \end{verbatim}%$ \end{frame} How does that work? \begin{itemize} \item \cmdbox{df /} prints how full the hard disk is full. \begin{itemize} \item The fifth column is the percentage full. \item The first row is a row of headings: \end{itemize} \begin{verbatim} \cmd{df /} Filesystem 1K-blocks Used Available Use% Mounted on /dev/md0 28840124 23822440 3552692 88% / \end{verbatim} \item We pipe this through \texttt{awk} which \begin{itemize} \item chooses lines greater than 1: \texttt{NR > 1} \item substituted ``\%'' with nothing: \mbox{\texttt{sub("\%", "", \$5)}} \item prints the remaining number. \end{itemize} \item The number is assigned to the variable \texttt{percentful} \item we use the \texttt{test} program in its ``\texttt{[~\ldots~]}'' guise, as the condition for the \texttt{if} statement to check whether this is greater than 90\% \item If so, we email the message in the body, and also in the subject, to the \texttt{root} mail user, which of course, has been assigned by a mail alias to another user (see Topic 1.114.1 \texttt{Perform security administration tasks}) \item You could run that script from cron. \end{itemize} \mode {% \begin{frame} \frametitle{Topics Covered} %\footnotesize \begin{multicols}{2} \tableofcontents[pausesections,pausesubsections] \end{multicols} % You might wish to add the option [pausesections] \end{frame} } \mode \section{License Of This Document} \label{sec:license} \begin{frame} \frametitle{License Of This Document} \raggedright% Copyright \copyright\ 2005 Nick Urbanik \par You can redistribute modified or unmodified copies of this document provided that this copyright notice and this permission notice are preserved on all copies under the terms of the GNU General Public License as published by the Free Software Foundation --- either version 2 of the License or (at your option) any later version. \end{frame} \end{document}