Bash shell scripts use echo
for output and read
for input.
The echo
command will add lines to the output stream. The return character
(CR
or \n
) in the output stream will start a new line, and
thus one echo can produces several lines of output.
The read
command will read the tokens on one line of input,
and the read in a while loop can be used to read an entire file.
To explore these two commands, we propose a simple demonstration project. The project is based on the gnuplot program which reads a command file, a data file and writes an image file with an x-y plot.
You have a data file which should have two variables per line, with at least one space separating the x and y values. You want to plot these points together with a curve determined by a function of x. Your project is to create an x-y plot of the data along with the function, specified by an expression of x using standard operations plus (+), minus (-), multiply (*), divide (/) and exponentiation (**). The gnuplot program is invoked by the command:
gnuplot commmandFile
where commandFile
is the name of a file will commands that specify the
data input file name, the function of x and the image dimensions and the image file name.
The three steps in the project:
You will be given a gnuplot template file, and you can assume that your data file has well formed, space separated numbers. There may be two few numbers or two many numbers.
if
statement and while
statement. Finally this
will all be put to together in one shell statements with the code blocks
put into functions.
Topics:
The following sections have several files listed, which are all
shell scripts. They begin with sha-bang comment, which means they will run
as a bash shell script when the file is executable and you begin the command
with the file name. Following the file listing, there is a test session
with testing$
as the prompt
note: On the centos.css.udel.edu
all these files are in
the directory /usr/local/share/WS/bashio
. You can copy them to your working
directory, make sure they are executable and try them using the ./filename
to make sure you are running the script in your current working directory.
more..
This section will lead you through 4 versions of a scripts in the files
echo1
, ... ,echo4
. These scripts will output
a file will valid gnuplot commands to produce an image file.
The file echo1
begins with the
sha-bang line #!/bin/bash
to make it a bash script. There is one echo
command in the script. To use the file as a command in your
session, type ./echo1
. Make sure the file is executable.
more...
The echo
bash command will print the values of the arguments to
standard out (STDOUT), which can be saved in a file
with a command redirection of the
form >filename
.
If the argument is a literal string, which
starts and ends with a single quote, then every character, including the
new lines. will be echoed to the terminal (or file)..
more...
#!/bin/bash echo 'set terminal svg size 400 300 set output "fig1.svg" plot "fig1.data" with points pointtype 6, 2**x'
The echo1
version prints a three line file using one literal string with two
line endings (CR). The file contains three
gnuplot commands: a set terminal
, a set output
, and
a plot
command.
The output file will tell
the gnuplot program to produce figure 1 as a
scalable vector graphic
(svg) in
the file fig1.svg
from the
data in fig1.data
.
note: Inside a single quoted string no characters have special meaning and
will all appear in the string. This includes the new line character, return, which
will allows a multi-line output with one echo
command.
more...
testing$ ./echo1 set terminal svg size 400 300 set output "fig1.svg" plot "fig1.data" with points pointtype 6, 2**x testing$ ./echo1 >commands testing$ wc -l commands 3 commandsThe first
echo1
test command prints to the terminal.
The second echo1
command redirects
the output to the file commands
. The wc -l
command counts the
lines in the file and prints the expected answer of 3 lines. The echo
command puts a return at
the end of the string so the number of lines in the file is the number of
returns in your
string plus 1.
The echo1
version is not very flexible. The next version will
using variable expansion in the string to build the commands from predefined
variables.
#!/bin/bash imageFile='fig2.svg' dataFile='fig2.data' function_x='0.83037*2.04599**x' echo -n "\ set terminal svg size 400 300 set output \"$imageFile\" plot \"$dataFile\" with points pointtype 6, $function_x "
The echo2
version uses double quotes. The variables are expanded
to the values set in the environment, and the backslash is the escape character. Here
the string starts with an escaped return. This way all the lines following with
be aligned just the way the appear in the output stream. Variables are expanded
to the values by the use of the $
.
The \"
is need for double
quotes in the output. The string ends with an double quote and the beginning of
a line. The -n
option on the echo
command prevents an
extra line from being appended to the output.
note: Inside a double quoted string their are 4 characters with special meaning and
for them to appear in the string. The quotes \"
, \$
, \`
and \\
all expand as one character, a
single \
at the end of a line, escaped return, is expanded as null, which means
the lines are joined together.
more...
note: It is good practice to choose meaningful variable names to save
parts fo the file to be created.
It makes more sense to a reader of your code who does not
know gnuplot, and you may later uses the variables to create other files. For example,
you may want to use the
imageWidth
, imageHeight
and imageFile
variables to write an html file for display of the plot.
more...
testing$ ./echo2 >commands testing$ cat commands set terminal svg size 400 300 set output "fig2.svg" plot "fig2.data" with points pointtype 6, 0.83037*2.04599**x testing$ wc -l commands 3 commandsThe
echo2
command saves
the output to the file commands
. The cat
command
outputs to the terminal window just as the echo command would have done, but the
file is saved for used by the command wc -l
to count lines.
The echo2
file still needs to be modified to change the outup string.
The next version will read the assignments from a separate file. This file is
called a run control file.
#!/bin/bash source .echorc if [ "$figTitle" ]; then echo -n "\ set figTitle \"$figTitle\" " fi echo -n "\ set terminal svg size $imageWidth $imageHeight set output \"$imageFile\" plot \"$dataFile\" with points pointtype 6, $function_x "
The echo3
version reads the variable assignments from
a hidden run control file .echorc
.
note: This is a neat trick to leverage
the shell parser to parse your setup file. This way you can run the same shell,
unchanged, to make several figures. The size of the figures, the file names
and the function are all taken from the .echorc
file.
more...
By sourcing an external file, we give up knowledge about what is in
the values of the variables. Typically, We should check the
values and us conditional execution. Here we use the if
block
if [ "$figTitle" ]; then fi
This test is true if the value variable figTitle
has any value.
This conditional execution block means the figTitle
assignment is optional
in the run control file.
testing$ cp fig1rc .echorc testing$ ./echo3 set title "data with fitted exponential" set terminal svg size 500 400 set output "fig1.svg" plot "fig1.data" with points pointtype 6, 0.83037*2.04599**x testing$ cp fig2rc .echorc testing$ ./echo3 set title "figure2: data with function 2**x" set terminal svg size 500 400 set output "fig2.png" plot "fig2.data" with points pointtype 6, 2**x
For this test there are two sample run control files fig1rc
and fig2rc
. Before each test the files are copied to
.echorc
. Both tests are getting the title, file names
and the function from the rc files.
What happens if the variables function_x
and figTitle
are missing from the .echorc
file? To test we can use the
head -4
command to copy just the first four lines of the
configuration file to .echorc
.
testing$ head -4 fig2rc > .echorc testing$ ./echo3 set terminal svg size 500 400 set output "fig2.png" plot "fig2.data" with points pointtype 6,
There are two problems with this version.
svg
, but it is written to a
png
file. We want to set the terminal type on the
gnuplot command base on the image file name suffix.#!/bin/bash source .echorc case "$imageFile" in *.png ) echo -n "\ set terminal png transparent size $imageWidth,$imageHeight set output \"$imageFile\" " ;; *.svg) echo -n "\ set terminal svg size $imageWidth $imageHeight dynamic set output \"$imageFile\" " esac echo -n "\ plot \"$dataFile\" with points pointtype 6${function_x:+, }$function_x "
The echo4
version reads the variable assignments from
a hidden file .echorc
. The
shell case
statement to select one of two
forms of the gnuplot set terminal
commands. Wild card matchins is used
to select svg (*.svg
) or png (*.png
). The
dangling comma problem is fixed with the variable expansion
${function_x:+, }
. This will only insert the ",
" if
the function_x
variable has a value. Thus the comma will only appear
if it is needed.
testing$ cp fig1rc .echorc testing$ ./echo4 set terminal svg size 500 400 dynamic set output "fig1.svg" plot "fig1.data" with points pointtype 6, 0.83037*2.04599**x testing$ cp fig2rc .echorc testing$ ./echo4 set terminal png transparent size 500,400 set output "fig2.png" plot "fig2.data" with points pointtype 6, 2**x testing$ head -4 fig2rc >.echorc testing$ ./echo4 set terminal png transparent size 500,400 set output "fig2.png" plot "fig2.data" with points pointtype 6
This is the same test script as was used for the previous version.
Notice that the terminal commands correctly match the suffix of the
image file name. The title commands are not present, since the if [ "figTitle" ]
is not in this version.
This completes the development steps for the construction of a gnuplot command
file. The final version will be combination of echo3
and echo4
.
Here is the final version as a shell function
function gnucommands { if [ "$figTitle" ]; then echo -n "\ set title \"$figTitle\" " fi case "$imageFile" in *.png ) echo -n "\ set terminal png transparent size $imageWidth,$imageHeight set output \"$imageFile\" " ;; *.svg ) echo -n "\ set terminal svg size $imageWidth $imageHeight dynamic set output \"$imageFile\" " esac echo -n "\ plot \"$dataFile\" with points pointtype 6${function_x:+, }$function_x " }
The read var1 var2 ...
shell command parse one line from STDIN
and assign tokens to the values of the variable names in the argument list. The tokens
on the line are parsed just as for shell commands, with white space between
tokens and a backslash to logically continue the line to the next physical line. If there
are more tokens than variables then the remainder of the line will be assigned
to the last variable. In particular, if there is only one variable then all the
line, excluding any leading or trailing white space, will be assigned the variable.
#!/bin/bash read line echo "$line"
This is a simple combination of a read with one variable and echo with
double quotes.
simple. When reading data you should always use the raw option -r
to avoid problems with backslash line continuation.
testing$ ./read1 >save 1 1.8 data point testing$ cat save 1 1.8 data point testing$ ./read1 >save 1\ 1.8\ 2 data point testing$ cat save 1 1.82 3.4
The first read1
command reads one line from the terminal and saves
it in a file.
The second example uses the backslash to continues the line. The
quoted return logically joins two physical lines (with no whitespace).
note on backslashes: We do not expect any backslashes in the data file, but it is
a good practice to always use read -r
to avoid backslash quoting and
line continuation.
For this project we want to change the data to put a comma between the first two tokens in the data file.
#!/bin/bash read -r x y etc echo "$x, $y"
Version read2
will read the first two
tokens from the line and echo the pair with a comma separator. The value of
the etc
variable will contain any additional text on the line.
testing$ ./read2 1 1.8 1, 1.8 testing$ ./read2 >save 1 1.8\ testing$ cat save 1, 1.8\ testing$ ./read2 x y unwanted data x, y
The is the same test as above with an etra test to make sure unwanted data does
not appear int he output.
The etc
variable will contain any extra, unwanted data.
#!/bin/bash read -r x y etc if [ -n "$etc" ]; then echo "line too long, unexpected: $etc" >&2 elif [ -z "$y" ]; then echo "line too short" >&2 fi echo "$x, $y"
The etc
should be empty, and if there is only one number the y
string will be empty.
This gives two simple tests to check for bad lines in the data file.
The read3
will echo the data par to STDOUT, and echo any error
message using the >&2
redirect (STDERR).
note on if: The if
command is terminated by fi
. Type
help if
for the details. The [
following the if
is
a shell command and must be a token surrounded by white space. Also, if you put the
then command on the same line as the test you must terminate the test command with a
semicolon.
testing$ ./read3 1 1.8 1, 1.8 testing$ ./read3 1 1.8 2 3.4 line too long, unexpected: 2 3.4 1, 1.8 testing$ ./read3 1 line too short 1, testing$ ./read3 >save 1 1.8 junk line too long, unexpected: junk testing$ cat save 1, 1.8The first three
read3
commands test the command with three commands: a correct line
with
extra spaces, a line with extra data, and a line without a y value. The last read3
command shows the usefulness of redirect to STDERR. We we save the file with the
redirect command, the error messages are still sent to the terminal, and the
save file is not cluttered with error messages. We may want to take the
"too long" message as a warning and still proceed with saved file.
Now have code for reading one line, but we need a way to read all the lines in a file, to create a data file for gnuplot.
The read
shell command will read one line from STDIN, and if it encounters
an end for file, i.e., there is not more data to read, it will return a non-zero status.
This is designed to work in the while
loop.
#!/bin/bash while read -r x y etc; do if [ -z "$y" ]; then echo "line too short" >&2 elif [ -n "$etc" ]; then echo "line too long, unexpected $etc" >&2 fi echo "$x, $y" done
All the commands in the
do
block are executed once for every line with x
, y
and
etc
assigned to the first, second and remainder tokens in the file.
testing$ cat badfile 1 1.8 2 3.2 3 4 12.6 5 31.5 32 6 60.5 testing$ ./while1 <badfile >fig1.data line too short line too long, unexpected: 32 testing$ cat fig1.data 1, 1.8 2, 3.2 3, 4, 12.6 5, 31.5 6, 60.5
The while1
command reads all the lines of the redirected file and writes
to fig1.data
. The sample file, badfile
, has two errors,
one too short and one two long. We use two redirects on the ./while1
The <badfile
while cause the reading to be done from the file,
and the >fig1.data
redirects the echo to the file.
It is clear the too-short condition is worse
then the too-long condition. We will consider one warning and one an error.
It would be useful if the error message contain the line number to locate the error.
#!/bin/bash let lineNo=0 while read -r x y etc; do let lineNo+=1 if [ "$x" -a -z "$y" ]; then echo "line $lineNo too short" >&2 errCode=1 elif [ "$etc" ]; then echo "line $lineNo too long, unexpected $etc" >&2 fi echo $x${y:+, }$y done [ -z "$errCode" ]:
To make the error and warning messages more informative we have added two variables.
The integer variable lineNo
to count lines in the file, and the
string variable errCode
to flag an error condition. The let
command is for integer variables and allows
arithmetic on integer variables. Here we start by assigning
lineNo
to 0
and then increasing it by 1
as
the very first command in the do
block. Any variable which is unassigned
will expand as a null string. So we expect $errCode
to be null after the
while loop is completed when no errors were encountered. Since the implied exit
and the end of the script will exit with status of the last command, this script with
have a success exit if the errCoded
is never assigned. A blank
line is not an error.
There are two minor improvements to the script. The codition first test has the additional
"$x" -a
, and this cause the error if x is present, but y is missing.
The comma is only put in the output if it is needed with the ${y:+, }
. This
means blank lines stay as blank lines with no error condition.
testing$ ./while2 <badfile >fig1.data && echo "good data file" line 3 too short line 5 too long, unexpected 32 testing$ ./while2 <warningfile >fig1.data && echo "good data file" line 3 too long, unexpected 8 line 5 too long, unexpected 32 good data file testing$ ./while2 <goodfile >fig1.data && echo "good data file" good data fileTo test
while2
we have three sample files a badfile
,
warningfile
and goodfile
.
We test three times, the badfile finds two
bad lines and returns a failed status, and that is why the good data file
is not echoed. The test with the warning file also finds two bad lines,
but long lines are only warnings. After the too warning,
the good data file
appears, as it does for the goodfile
.
The good file returns nothing. (This is typical of UNIX - quiet is good)
This completes the next block of script for the main solution. We also put this in a function. Since functions do not have an exit status. (If you exit from a function then the entire script is terminated.) It uses return codes instead. A non-zero return code is an error.
function datafile { let returncode=0 let lineNo=0 while read -r x y etc; do let lineNo+=1; if [ "$x" -a -z "$y" ]; then echo "line $lineNo too short" >&2; returncode=1; elif [ -n "$etc" ]; then echo "line $lineNo too long, unexpected: $etc" >&2; fi echo $x${y:+, $y} done return $returncode }
The previous scripts will, along with a short utility, will be stored in
a file called functions.sh
. This will be source in each
script, which need the function by the command:
source function.sh
Or the short form with just a "." for source.failed
return code.
Note: A bash
function is a block of commands which is invoked
in your shell by just using the name. The commands are executed with the positional parameters
set to the arguments $@
of the function command. more...
function die { echo "makefig: $@" >&2 exit 1 }
makefig1
has a complete script that
uses the scripts
developed above as functions. Most of work of this script
is done in the functions. Conditions are checked and if the
all pass then the gnuplot command is executed as the last line
of the script. The exit status of the script is the exit
status of the last command.
#!/bin/bash # makefig # reads data file and makes a gnuplot figure # # Get functions: # die, gnucommands, dataFile source functions.sh #----- # Get variables from run control file: # dataFile, commandFile, imageFile, imageHeight, imageWidth, function_x [ -e .makefigrc ] || die "file \".makefigrc\" does not exist" source .makefigrc [ "$dataFile" ] || die "no data file name" [ "$imageFile" ] || die "no data file name" [ "$imageHeight" -a "$imageWidth" ] || die "no plot dimensions" [ "$commandFile" ] || die "no command file" #----- # Make output files: # dataFile, commandFile, imageFile datafile >$dataFile || die "some lines to short" gnucommands >$commandFile gnuplot $commandFile
The file functions.sh
contains the the functions, listed earlier,
and is sourced in hte makefifg
script to define the functions for
this script. The die
function is a utility function to make it easier
to write and error message and exit with a failed exit status.
(This will be familier if you have ever looked at Perl code.) It is used
in the form
[ assert condition, which must be true to continue ] || die "message, when condition not met"Here are some typical tests from this script
dataFile
shell variable must be assigned to have a filename to
for the redirection.
imageHeight
and imageWidth
to be assigned. Do not use &&
inside the condition, since it is
a statement separation operator.
datafile
shell function will print to STDERR the line numbers which are too long
or too short and set the exit status if any are too short.
testing$ firefox & [1] 4794 testing$
The ampersand causes firefox
to run in the background.
You get a prompt to continue your shell session, while firefox
is running. If you forget the ampersand you can continue in your
shell typing ctl-z
, this will cause firefox
to be suspended. Type bg
to put firefox in the background,
you will see the command repeated with the ampersand.
The number, 4794
is the process id, you can use this to
kill firefox later. (It is better to just quit firefox in the normal
way, with the File
pulldown menu.) You can always
find the process id with the command
pgrep firefox
This will give you all firefoxes running. Remember this is a multi-users system. To just see yours:
pgrep -u $USER firefox
Once firefox is running in the background you a command line command such as:
firefox fig1.png
To have firefox render the png in a new tab in the firefox window. To close the tab click on thex
in the tab. You should get a prompt
to continue you shell.
makefig1
testing$ alias makefig=./makefig1 testing$ cp fig1rc .makefigrc testing$ makefig <badfile && echo "figure ready" line 3 too short line 5 too long, unexpected: 32 makefig: some lines to short testing$ sed -n '3p;5p' badfile 3 5 31.5 32 testing$ makefig <warningfile && echo "figure ready" line 3 too long, unexpected: 8 line 5 too long, unexpected: 32 figure ready testing$ sed -n '3p;5p' warningfile 3 7.5 8 5 31.5 32 testing$ makefig <goodfile && echo "figure ready" figure ready testing$ sed -n '/imagefile=/p' .makefigrc imagefile='fig1.svg' testing$ firefox fig1.svg testing$Error message from Firefox:
This XML file does not appear to have any style information associated with it.
The fig2rc
does not use svg.
testing$ alias makefig=./makefig1 testing$ cp fig2rc .makefigrc testing$ makefig <goodfile && echo "figure ready" figure ready testing$ grep 'imagefile=' .makefigrc imagefile='fig2.png' testing$ firefox fig2.png testing$
makefig1
will make a figure, but there a few usabily issues
we will address with a second version.
-v
to produce
as short report of what the gnuplot command will produce.-f filename
will read the variable assignments
for filename
instead of .makefigrc
.#!/bin/bash # makefig: # takes std input data file and makes a gnuplot figure # options: # -v for more reporting # -f filename to set run control file # arguments: ! functions of x to be added to the figure #----- # Get functions: # die, gnucommands, dataFile source functions.sh #----- # Get variables for argument list # verbose 0|1 # rcfile run control file with assignments # argfuns list of functions to plot rcfile='.makefigrc' verbose=0 while [ $# -gt 0 ]; do case $1 in -v) verbose=1 ;; -f) shift rcfile="$1" ;; -*) die "illegal option $1 Usage: `basename $0` [-v] [-f file] [function_x ...]" ;; *) argfuns="$argfuns${argfuns:+, }$1" esac shift done [ -e "$rcfile" ] || die "file \"$rcfile\" does not exist" #----- # Get variables for run control file # figTitle, imageWidth, imageHeight, imageFile, dataFile source $rcfile [ "$dataFile" ] || die "no data file name" [ "$imageFile" ] || die "no image file name" [ "$imageHeight" -a "$imageWidth" ] || die "no plot dimensions" #----- # Make the file # dataFile datafile >|$dataFile || die "some lines too short" #----- # print formated report function_x="$function_x${argfuns:+, }$argfuns" withFun=${function_x:+, together with $function_x} [ $verbose -eq 0 ] || echo " ${figTitle:-figure:} Make a plot of data points from the file $dataFile$withFun. The plot will be sized at $imageWidth by $imageHeight, and stored in the file $imageFile. " | fmt #----- # Make figure gnucommands | gnuplot
testing$ alias makefig=./makefig2 testing$ sed 's/.svg/.png/' fig1rc >.makefigrc testing$ makefig -h <goodfile && firefox fig1.png makefig: illegal option -h Usage: makefig2 [-v] [-f file] [function ...] testing$ makefig -v <goodfile && firefox fig1.png data with fitted exponential Make a plot of data points from the file fig1.data, together with 0.83037*2.04599**x. The plot will be sized at 500 by 400, and stored in the file fig1.png. --- New tab in firefox with fig1.png
testing$ makefig -v -f fig2rc "0.83037*2.04599**x" <badfile && firefox fig2.png line 3 too short line 5 too long, unexpected 32 makefig: some lines too short testing$ makefig -v -f fig2rc "0.83037*2.04599**x" <goodfile && firefox fig2.png figure2: data with function 2**x Make a plot of data points from the file fig2.data, together with 2**x, 0.83037*2.04599**x. The plot will be sized at 500 by 400, and stored in the file fig2.png. --- New tab in firefox with fig2.png
testing$ makefig -v -f fig3rc <goodfile && firefox fig3.png figure3: nearly exponential data Make a plot of data points from the file fig3.data. The plot will be sized at 500 by 400, and stored in the file fig3.png. --- New tab in firefox with fig3.png
testing$ makefig -v -f fig3rc "2**x" "exp(x)" "64*exp(x-6)" <goodfile && firefox fig3.png figure3: nearly exponential data Make a plot of data points from the file fig3.data, together with , 2**x, exp(x), 64*exp(x-6). The plot will be sized at 500 by 400, and stored in the file fig3.png. gnuplot> plot "fig3.data" with points pointtype 6, , 2**x, exp(x), 64*exp(x-6) ^ ^ line 0: invalid expression testing$ makefig -v -f fig3rc "-0.358 - 0.0756*x**2 + 0.0000559*x**4 + 2**x" <goodfile makefig: illegal option -0.358 - 0.0756*x**2 + 0.0000559*x**4 + 2**x Usage: makefig2 [-v] [-f file] [function ...] testing$
To problems still to fix:
There is a script file makefig3
that fixes both of these problems,
and adds a little tweek to the verbose formatted report. There is not many
changes to the makefig2
. The diff
command list a
report of the changes.
29c29 < -*) --- > -[^.0-9]*) 50c50 < function_x="$function_x${argfuns:+, }$argfuns" --- > function_x="$function_x${function_x:+${argfuns:+, }}$argfuns" 53c53,57 < withFun=${function_x:+, together with function $function_x} --- > if [ "${function_x##*,*}" ]; then > withFun="${function_x:+, together with function $function_x}" > else > withFun=", together with functions ${function_x%,*} and${function_x##*,}" > fi
The wildcard patterm in line 29 was changed from -*
to
-[^.0-9]*
. This is the pattern in the case
statement to catch illegal options. An option which begins with a
negative number, and is not illegal. The new pattern
will not match a negative number
In this version of makefig
, there are two sources of functions
to be added on the plot with the data. The function_x
comes
from the run control file and argfuns
is from the argument
list. Either of these could be null, and we do not want a comma. The
original code handled the null argfuns
correctly, but not
the null function)x
. The fix is to nest the conditional
parameter expansion - ${function_x:+${argfuns:+, }}
expands to
", " only if needed to separate two non-empty strings.
The last if then else statement will handle the case of more than one function correctly in the paragraph report. That is the list is presented with plural "functions" with an "and" inserted in place of the comma before the last function in the list.
testing$ alias makefig=./makefig3 testing$ makefig -v -f fig3rc <goodfile figure3: nearly exponential data Make a plot of data points from the file fig3.data. The plot will be sized at 500 by 400, and stored in the file fig3.png. testing$ firefox fig3.png --- New tab in firefox with fig3.png
testing$ makefig -v -f fig3rc "2**x" "exp(x)" "64*exp(x-6)" <goodfile figure3: nearly exponential data Make a plot of data points from the file fig3.data, together with the functions 2**x, exp(x) and 64*exp(x-6). The plot will be sized at 500 by 400, and stored in the file fig3.png. testing$ firefox fig3.png --- New tab in firefox with fig3.png
testing$ makefig -v -f fig3rc "-0.358 - 0.0756*x**2 + 0.0000559*x**4 + 2**x" <goodfile figure3: nearly exponential data Make a plot of data points from the file fig3.data, together with the function -0.358 - 0.0756*x**2 + 0.0000559*x**4 + 2**x. The plot will be sized at 500 by 400, and stored in the file fig3.png. testing$ firefox fig3.png --- New tab in firefox with fig3.png