<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">\documentclass[onecolumn,letterpaper,11pt]{article}

%===================================================================
% Include common packages, environments, counters etc
\input{common_latex/common_latex_packages}
\input{common_latex/common_latex_lab_format}
\input{common_latex/common_latex_definitions}
%===================================================================

\begin{document}
\wikidoc{file=Lab.CPPIntro}
\wikidoc{auth=mikerb@mit.edu}

%===================================================================
% Build the Cover Page in an environment style easy for wiki parsing
\begin{labcover}
\labtitle{Shell Scripting Lab 01 - Basics}
\vspace{0.1in}
\labpng{0.25}{figures/script01.png}
\vspace{0.3in}
\labdate{Spring 2023}
\vspace{0.1in}
\labauth{Michael Benjamin, mikerb@mit.edu}
\labauth{Department of Mechanical Engineering}
\labauth{MIT, Cambridge MA 02139}
\labtoc{small}
\end{labcover}

%===================================================================
% Add a blank page so first body page is on the right when doubleside
\blankpage

%===================================================================
% Begin the body of the text
%===================================================================
\section{The Power of Scripts}
%===================================================================

This mini-lab sequence is mostly about writing shell scripts, and more
specifically {\em Bash} scripts. Broadly, a script is any way of
stitching together a sequence of steps, invoked by a single command.
We will introduce some of these other ways too. The goals of these
labs are to (a) Empower you to use scripts to your advantage, (b)
Understand the scripts we already use in every corner of our lab
workflow, and most of all (c) Enable you to unleash the {\em fun} of
the command line. After all scripts, in all their forms, is really
what makes the command line incredibly powerful, and this power is
really joy if you enjoy computers, programming and automating complex
processes down to a single new command/script of your creation.

%===================================================================
\section{Preliminaries}
%===================================================================

Depending your starting point, some of these preliminary topics may or
may not be familiar. To ensure a common baseline, we review them here.

%===================================================================
\subsection{The Bash Shell}

There are several shell options on both MacOS and GNU/Linux. We choose
\cmd{bash}. On both MacOS and GNU/Linux this can be found in
\var{/bin/}. On MacOS, the default shell for the Terminal application
is \cmd{zsh}, and \cmd{bash} needs to be explicitly chosen in the
preferences for the Terminal application.

\vspace{0.1in}
\noindent
The primary reason we prefer \cmd{bash} is that its presence on a
GNU/Linux or MacOS install is virtually universal. On GNU/Linux, it is
an additional feature to be installed. While it is easy to install in
the GNU/Linux package manager, in some cases, we may wish to work on a
machine or robotic platform where we do not have root privileges to
install \cmd{zsh}. Furthermore, as a general rule of thumb, to
maximize portability across machines, and given the choice between
Option A and Option B where the latter is a superset of the former, we
prefer the subset option. For a good discussion of comparing \cmd{zsh}
and \cmd{bash}, see:

\noindent
\urlx{https://www.educba.com/zsh-vs-bash/}

\vspace{0.1in}
\noindent
Even on this site, which leans toward \cmd{zsh}, w.r.t. scripting, the
assessment for \cmd{zsh} is \var{"Scripting is not that good as
  compared to Bash"}.

\vspace{0.1in}
\noindent
As we will see later, it is perfectly reasonable to use \cmd{zsh} as
your personal choice for a shell, and write bash scripts that declare
\cmd{bash} to be the prevailing shell for the duration of the
script. That being said, our preference is to work in a single shell
environment in both our command-line and our shell scripting
activities. Thus throughout the remainder of this and later scripting
labs, everything is \cmd{bash}.

%===================================================================
\subsection{Operating System Assumptions}

These labs assume that you are working on either a MacOS or GNU/Linux
machine.  For the latter, our test environment is Ubuntu 20.04. Most
command-line utilities are exactly the same on both machines. Some
utilities may differ slightly in the command-line options. In some
cases, one utility may have a counterpart that goes by a different
name on the other system.  For example, the \cmd{say} utility on the
Mac and \cmd{espeak} utility in Linux.

\vspace{0.1in}
\begin{consoleverb}
  $ say hello     // MacOS
  $ espeak hello  // Linux
\end{consoleverb}
\vspace{0.1in}

%==================================================================
\subsection{Package Managers}

These labs also assume you have access to a {\em package manager} on
either MacOS or Linux machines, so you can install any some utilities
for our exercises along the way if need be.

\vspace{0.1in}
\noindent
MacOS machines by default do not come installed with a package
manager.  Users typically install HomeBrew or MacPorts. Linux machines
typically come with a package manager (\cmd{apt}) already installed.
With a package manager natively installed in Linux, getting new utilities
is a super quick, and perhaps for this reason, Linux users are more
likely to just customize their environment on a new machine by adding
packages they commonly use.

\begin{packed_itemize}
\item If you are a MacOS user, install a package manager like HomeBrew
  if you have not already.
\item If you are a Linux user, install the \var{espeak} and
  \var{aplay} packages if they are not already installed, e.g, \var{\$
    sudo apt install espeak}.
\end{packed_itemize}

\vspace{0.1in}
\noindent
The MacOS commands \cmd{say} and \cmd{afplay} have counterparts in
GNU/Linux GNU/Linux with \cmd{espeak} and \cmd{aplay}. We will use
these in our examples.

%===================================================================
\subsection{Configuring Bash}

Configuring \cmd{bash} is done by editing the file \var{.bashrc} in
the user's home directory. This file is read each time a new
\cmd{bash} session begins, e.g., either in a new Terminal window, or
when running \cmd{bash} from within an existing session, or remotely
logging into another machine with \cmd{ssh}. This file is a kind of
script in itself in the sense that, when being "loaded", the loading
process has the same result as if the user typed each one of the lines
in the \var{.bashrc} file by hand. 

\vspace{0.1in}
\noindent
This will be a core file in this and later labs. Making additions and
adjustments to this file, and thus your \cmd{bash} configuration, is
something you should feel completely comfortable with before long.

\vspace{0.1in}
\noindent
Configuration of \cmd{bash} also may utilize other configuration
files. For now the focus is on \var{.bashrc} but you can read about
this topic further:

\vspace{0.1in}
\noindent
\urlx{https://linuxize.com/post/bashrc-vs-bash-profile/}

\vspace{0.1in}
\noindent
Note, since this file is in the user's home directory, sometimes it will
be refered to as \var{~/.bashrc}. And even though this file is loaded
at the start of the \cmd{bash} session, it can be re-loaded simply by
typing:

\vspace{0.1in}
\begin{consoleverb}
$ source ~/.bashrc
\end{consoleverb}

%===================================================================
\subsection{The Shell History}

The command-line shell (\cmd{bash}, \cmd{zsh}, and other shell
variants) maintains a history of commands typed by the user during the
current session. The history can be viewed by typing:

\vspace{0.1in}
\begin{consoleverb}
$ history
  ...
  750  cd
  751  emacs -nw .bashrc
  752  echo $HISTFILESIZE
  753  echo $HISTSIZE 
  754  cd ~/project23
  755  history
\end{consoleverb}
\vspace{0.1in}

\noindent
By default the history length is 500, but can be changed by setting
the \var{\$HISTSIZE} setting in the \var{.bashrc} file. In the example
above, typing \var{!echo} would re-invoke the command \var{echo
  \$HISTSIZE} since it was the most recent command that began with
\var{echo}. Typing \var{!752}, would revinvoke instead the command
\var{echo \$HISTFILESIZE}. 


%===================================================================
\subsection{Command-line Return Values}

Every command entered on the command line has a return value. By
convention, a return value of 0 indicates success. A non-zero return
value indicates some sort of failure, and depending on the
application, the particular non-zero return value may indicate the
kind of error. This is a critically important convention since it
allows existing command-line utilities to be stitched together into
scripts in a predictable way. It is however only a convention and
there is no way to enforce the development of (your) new scripts to
follow this convention. However, this is our goal, and the topic
will reappear many times.

\vspace{0.1in}
\noindent
The return value for a command is found in the value of a special
shell variable \var{\$?}. For example, consider the GNU/Linux command
\cmd{touch}. This command takes a single argument, a file name. If it exists,
it will update the modification date of the file. If it does not exist, it
will try to create the file. In either case it will return a value of 0, indicating
success:

\vspace{0.1in}
\begin{consoleverb}
  $ touch ~/foobar
  $ echo $?
  0
\end{consoleverb}
\vspace{0.1in}

\noindent
If it cannot create the file, it will take no action but will return a
failure value of 1. In the example below, \cmd{touch} fails because it
names a file in a directory that does not exist:

\vspace{0.1in}
\begin{consoleverb}
  $ touch ~/bad_directory12345/foobar
  $ echo $?
  1
\end{consoleverb}

\vspace{0.1in}
\noindent
The return value can be used to condition actions in a script. Note that
in the following, \var{command1} and \var{command2} will both be invoked
regardless of the outcome of \var{command1}:

\vspace{0.1in}
\begin{consoleverb}
$ command1; command2
\end{consoleverb}
\vspace{0.1in}

\noindent
But in the following, \var{command2} will only be invoked in the case
that \var{command1} is successful

\vspace{0.1in}
\begin{consoleverb}
$ command1 &amp;&amp; command2
\end{consoleverb}
\vspace{0.1in}

\noindent
And in the following, \var{command2} will only be invoked in the case
that \var{command1} is {\em not} successful.

\vspace{0.1in}
\begin{consoleverb}
$ command1 || command2
\end{consoleverb}
\vspace{0.1in}


%===================================================================
\newpage
\section{Script Forms }
%===================================================================

Our working definition of a script is a sequence of actions stitched
together to be invoked with a single action. Here are the categories
of scripts we can encounter:

\begin{packed_itemize}
\item Command-line sequences
\item Bash aliases
\item Text files
\item Bash functions
\item Bash scripts
\end{packed_itemize}

\vspace{0.1in}
\noindent
Bash aliases provide the most immediate power with little required
knowledge. Bash scripts provide the most general power and can be
the most complex. They are the true work horses and primary motivation
for this lab sequence. But all forms of scripting have their time and
place and will be introduced.

%===================================================================
\subsection{Command-line Sequences}

A command-line sequence is the simplest form of what we're willing to
call a script. Consider the simple script below:

\vspace{0.1in}
\begin{consoleverb}
$ say hello; sleep 2; say "shall we play a game?"         // MacOS
$ espeak hello; sleep 2; espeak "shall we play a game?"   // GNU/Linux
\end{consoleverb}
\vspace{0.1in}

\vspace{0.1in}
\noindent
Invoking this example, you should hear the computer say the first
sentence \var{"hello"}, followed by a 2 second pause, followed by the
second sentence \var{"Shall we play a game?"}

\vspace{0.1in}
\noindent
While technically a form of a script, the ephemeral nature of this
kind of script is what may cause some to not consider this a real
script. After all, the whold idea of a script is to arm yourself with
a shortcut that persists for later use. There are however two ways in
which a command-line sequence like the above satisfy this criteria of
persistence. First, after typing the above on the command-line, the
user can repeat the sequence by hitting the up-arrow on the
command-line to the originally typed line to invoke it once again
without having to re-type it. Second, depending what else may be in
the shell history, typing \var{\$ !say} will re-invoke the above
command-line sequence as long as there is not a more recent command in
the history that begins with \var{"say"}.

\vspace{0.1in}
\noindent
As an example from the MOOS-IvP environment, consider a session where
the user repeatedly launches a mission, but first wants to clear log
files from a previous mission and ensure no stray MOOS processes are
running, by invoking \cmd{ktm}. A sequence like this is common:

\vspace{0.1in}
\begin{consoleverb}
$ ktm; ./clean.sh; ./launch_vehicle.sh --shore=192.168.1.37 
\end{consoleverb}
\vspace{0.1in}

\noindent
The user could further add to this sequence a step to \cmd{cd} to the
mission folder, thus making it easier to invoke if for some reason the
user has changed folders since the last run. All the user would need to
do is type \var{\$ !ktm} each time they want to launch the mission.

\vspace{0.1in}
\noindent
Normally if a command-line sequence is useful beyond the current session,
then then an alias would be made to capture the sequence. But since the
above example as an IP address that may only be relevant for the moment,
an alias may be not as attractive. 

%===================================================================
\subsection{Shell Aliases}

An {\em alias} is a way of more permanently capturing a command-line
sequence. It helps overcome two drawbacks of the informal command-line
sequences like the above examples. First, a command-line sequence is
always only available in the shell history of the terminal window in
which it was typed. Second, if some other command in the shell history
matches the initial characters of the command-line sequence, then it
will be harder to re-invoke from the history with the \var{\$ !command}
method. 

\vspace{0.1in}
\noindent
An alias is a way of associating a command-line sequence to a single
word comprising the alias. For example, along the lines of the above
example, the following alias could be defined to launch a mission:

\vspace{0.1in}
\begin{consoleverb}
$ alias=mylaunch=`ktm; ./clean.sh; ./launch_vehicle.sh`
\end{consoleverb}
\vspace{0.1in}

\noindent
If the above is typed on the command-line, it will only be available
for that \cmd{bash} session in that Terminal window. By placing it in
the \var{.bashrc} file, it will be in all future \cmd{bash} sessions
in all Terminal windows.

\vspace{0.1in}
\noindent
Experienced users have dozens if not hundreds of aliases defined in
their \cmd{bash} configuration. Some of the less frequently used
aliases can be hard to remember. The list of all prevailing aliases
can be retrieved by simply typing \cmd{alias} on the command-line. If
there is a large number of aliases, this can be filtered by using the
GNU/Linux \cmd{grep} command. For example if you vaguely recall making
an alias a long time ago that had something to do with \var{"launch"},
the following will help find the alias:

\vspace{0.1in}
\begin{consoleverb}
$ alias | grep launch
alias mylaunch=`ktm; ./clean.sh; ./launch_vehicle.sh`
\end{consoleverb}
\vspace{0.1in}

\noindent
If you find this a common thing, you can make it easier with yet another
alias:

\vspace{0.1in}
\begin{consoleverb}
$ alias=afind=`alias | grep `
\end{consoleverb}
\vspace{0.1in}

\noindent
It helps to create some mnemonic to help remember what the alias is
about. In the above case, \var{afind} is short for "alias find". Do
whatever works for you.

%===================================================================
\subsection{Bash Shell Functions}

Bash shell functions are another type of scripting, more powerful than
aliases because they can accept {\em arguments}.  Only certain types
of alias definitions can accept arguments. In the previous example:

\vspace{0.1in}
\begin{consoleverb}
$ alias afind=`alias | grep `
\end{consoleverb}
\vspace{0.1in}

\noindent
The \var{afind} alias accepts as an argument a pattern. For example:

\vspace{0.1in}
\begin{consoleverb}
$ afind launch
\end{consoleverb}
\vspace{0.1in}

\noindent
The argument \var{launch} is easily passed since the alias is comprised
of two parts: the command followed by the argument. The argument is does
not appear within the definition of the alias. Consider instead the alias
borrowing from the earlier example: 

\vspace{0.1in}
\begin{consoleverb}
$ alias letsplay=`say hello; sleep 2; say "shall we play a game?" `   
\end{consoleverb}
\vspace{0.1in}

\noindent
While this alias is fine, it will always sleep for 2 seconds between
utterences. Suppose you would like to have the option for sleeping
5 or 8 seconds. You could create multiple versions:

\vspace{0.1in}
\begin{consoleverb}
$ alias letsplay2=`say hello; sleep 2; say "shall we play a game?" `   
$ alias letsplay5=`say hello; sleep 5; say "shall we play a game?" `   
$ alias letsplay8=`say hello; sleep 8; say "shall we play a game?" `   
\end{consoleverb}
\vspace{0.1in}

\noindent
But how about a version that takes {\em any} number of seconds as an
argument. We want to invoke

\vspace{0.1in}
\begin{consoleverb}
$ letsplay 23
\end{consoleverb}
\vspace{0.1in}

\noindent
For this we need to create a \cmd{bash} function like the following:

\vspace{0.1in}
\begin{consoleverb}
function letsplay()
{
  say hello
  sleep $1
  say "shall we play a game?''
}
\end{consoleverb}
\vspace{0.1in}

\noindent
This function definition can also be added to the \var{~/.bashrc} file
just like an alias.

%===================================================================
\subsection{Bash Scripts}




used within ano




A script can take on many forms. 

o arguments
o actions depend on intermediate results or findings

o All .sh scripts should return a value


o cmd; cmd; cmd;

* grep, pipe




%=======================================================================
\section{Lab Summary and Further Tips}
%=======================================================================

Here are a few links to the \var{learncpp.com} website that may be worth 
exploring:

\begin{packed_itemize}
\item 
If you are new to programming in general, or would like to know a bit
about the difference between a compiled language and interpreted
language, see:

\vspace{0.01in}
\noindent
\urlx{http://www.learncpp.com/cpp-tutorial/02-introduction-to-programming-languages/}
\vspace{0.1in}

\item 
If you'd like a bit of historical context of C++ vs C and prior languages, see:

\vspace{0.01in}
\noindent
\urlx{http://www.learncpp.com/cpp-tutorial/03-introduction-to-cc}

\end{packed_itemize}


\end{document}



</pre></body></html>