When creating scripts, it is often a requirement to generate some
form of file for storing some temporary information or even something
more permanent, like a log file. Sometimes you may even want to
create another script for later execution. There are a number
of ways to do this, some of which we have already met.
There are two simple ways to create another file, one uses the
cat command in conjunction with the redirect symbol,
the other way is to use the echo command in
conjunction with the redirect symbol. The example
Indented Cat is
a good example of the cat method in the Pipes
and Redirects section. This example only contains
litteral text however. It is more appropriate to see something
like the example below, which shows a
variable being used in the source data block.
cat >> $sql0 <<-EOA
SET ECHO OFF
SET FEEDBACK OFF
SET HEADING OFF
SELECT my_package.my_function($column)
FROM v\$database
WHERE name LIKE '%&1%';
EXIT
EOA
sqlplus -s $uid/$password@database @$sql0 $sql_arg_1 > $log0
The file created has its name stored in the variable $sql0
and as we can see the block between the EOA flags is
the data that goes into the file. The data block is actually a
segment of SQL*Plus statements, as indicated by the filename variable.
As is common with SQL*Plus code, the key words are picked out
in ALL CAPS, with objects (tables, procedures, columns, etc.)
all in lower case. The SELECT line contains a reference
to a called, packaged, PL/SQL function which has a column name
as an argument. Here the column name is held in a variable called
$column and this will be substituted at script run-time
by the real value.
There are some unfortunate consequences of generating SQL*Plus
statements from within a shell script which you have to be aware
of. Firstly, don't forget to put the EXIT statement at
the end of the block or you will end up with a script that stays
in SQL*Plus forever. Secondly, don't forget to put the semi-colons
(;) at the end of every SQL statement, or each
statement will overwrite the previous one or just create one long
unprocessable mess. Thirdly, some internal database tables may
contain the dollar symbol, which is special to the shell,
so escape them with the back-slash (\) as shown on the
FROM line.
On the WHERE line there is a reference to a SQL*Plus
positional parameter '&1' which will pick
up its value from the variable $sql_arg_1 at run-time
as shown in the last line, just after the end of block flag.
Did I say this was a simple example? Well, at least you don't
have to worry about quoting when using this method. All quotes
find their way to the destination file unscathed. Now to do the
same thing using echo instead of cat, see the
example below.
echo "SET ECHO OFF" >> $sql0
echo "SET FEEDBACK OFF" >> $sql0
echo "SET HEADING OFF" >> $sql0
echo "SELECT my_package.my_function($column)" >> $sql0
echo " FROM v\$database" >> $sql0
echo " WHERE name LIKE '%&1%';" >> $sql0
echo "EXIT" >> $sql0
sqlplus -s $uid/$password@database @$sql0 $sql_arg_1 > $log0
So what's the point of all this extra typing? Well for one thing
it allows you to put special bits of code into the block which
will only be used at certain times, by hiding them in complex
command groups. This example shows how
this is done below.
echo "SET ECHO OFF" >> $sql0
echo "SET FEEDBACK OFF" >> $sql0
echo "SET HEADING OFF" >> $sql0
echo "SELECT my_package.my_function($column)" >> $sql0
echo " FROM v\$database" >> $sql0
if [ "$db_type" = "m" ]
then
echo " WHERE name = '$db_name';" >> $sql0
else
echo " WHERE name LIKE '%&1%';" >> $sql0
fi
echo "EXIT" >> $sql0
sqlplus -s $uid/$password@database @$sql0 $sql_arg_1 > $log0
This is basically the same block except the WHERE clause
has been hidden inside an if statement. Now, depending
on the Database Type in the $db_type variable, the WHERE
clause can take one of two forms. Conveniently, the additional
argument which is not required by SQL*Plus in the first form,
is ignored at execution time, even though it is still available
on the last line. This is common with all scripts, arguments are
only used if they are referenced from within the
script.
So there you have the first two ways of creating another file
from a script. The version using cat can only cope with
a single output form, the version using echo can output
a multitude of forms depending on the complex command forms you
use. The choice is yours. There are, however, other ways to create
output files. You can use direct generation as in the example
List to create a list of files. Or the
indirect method shown in the example Counted
List where lines are built inside a loop construct and then
appended to the file to create a menu file. Or in the example
Sorted List where a list of words is
sorted into alphabetic order, duplicates are removed, then the
rest stored in a file.
ls -1 *.log > $lst0
count=1
for file in `ls -1 *.log`
do
echo "$count: $file" >> $mnu0
count=`expr $count + 1`
done
echo $@ | tr ' ' '\n' | sort -u > $lst0
There are also the head and tail
commands which are useful when reading files as opposed to writing.
We have already seen one example of their use in a previous section.
Here are a few more. The following example Reverse
List and Count Words reverses the order of lines in a file and
adds a word count to the end of each line. Note the use of expr
to change the type of the counter variable $file_length before
the while command sees it.
file_length=`wc -l $input_file | cut -c1-8` # Count Lines
file_length=`expr $file_length + 0` # Change Type
while $file_length # Start Loop
do
line=`head -$file_length $input_file | tail -1` # Get Line
words=`s_count_args $line` # Count Words
echo "$line = $words words" >> $output_file # Write Line
file_length=`expr $file_length - 1` # Decrement
done
Don't forget that the tail command has a few more options
up its sleave than most users remember. As well as the minus,
there is the plus. This acts a bit like an inverse head
command in that the command tail +10 -10 will give you
lines 10 through to 10 from the end - in a 33 line file, that
is from 10 through 23. There are also the l, b,
and c options (lines, blocks, characters) which affect
the element being counted by the plus and minus
numbers. Then there is r which reverses the order of the
lines returned. Lastly there is f which is often used as
part of a monitor script. This option does not terminate, but
just keeps looking at the end of the named file waiting for something
else to be written. Here is an example of the
tail -f option in use in use, which is a small section
of a much longer script.
#----------------------------------------------------------------
# Launch FTP in background and collect return code on completion
#----------------------------------------------------------------
( ftp -nv < $ftp_commands > $ftp_log 2>&1 ; \
echo $? > $ftp_return_code ) &
job_1_number=$!
tail -f $ftp_log | grep $search > $done_log &
job_2_number=$!
#-------------------------------
# Check done_log until complete
#-------------------------------
while [ `grep -c $search $done_log` = 0 ]
do
sleep 1
done
#-----------------------------------------
# Tidy Up the jobs if hung and log result
#-----------------------------------------
if [ "`cat $ftp_return_code`" = "" ]
then
kill -9 $job_1_number $job_2_number
echo "FTP Complete at `s_date`" >> $process_log
else
echo "FTP returned a [`cat $ftp_return_code`]" >> $process_log
echo "Aborting $0 at `s_timestamp`"
echo "========================================="
cat $process_log
echo "========================================="
exit
fi
Note the use of $! to return the background job numbers
for later termination. Also see how the two commands on the first
line have been submitted to a subshell in the background
and yet the return code from inside the subshell, being redirected
to a file, is available for testing in the parent shell. The
tail -f command will stay looking at the end of the log
file for ever if left, hence the need for the tidy up later, but
the grep $search only passes a known string
on to the completion file $done_log. This can be checked
in a loop with sleeps until something appears.
Notice how flexible the test command is by allowing the
output of a cat command to be used as part of a string
comparison. Of course in a real script you might want to code
in some loop counters to stop a hung FTP process from hanging
the script. Say, count to 100 loops max or something. Its your
choice how long to wait.
|