|
These scripts, while not fitting into the text of this document, do
illustrate some interesting shell programming techniques. They are useful,
too. Have fun analyzing and running them. Example A-1. manview: A script for viewing formatted man pages
#!/bin/bash
# Formats the source of a man page for viewing in a user directory.
# This is useful when writing man page source and you want to
# look at the intermediate results on the fly while working on it.
if [ -z $1 ]
then
echo "Usage: `basename $0` [filename]"
exit 1
fi
groff -Tascii -man $1 | less
# From the man page for groff.
exit 0 |
Example A-2. rn: A simple-minded file rename utility This script is a modification of Example 3-58. #! /bin/bash
#
# Very simpleminded filename "rename" utility.
# Based on "lowercase.sh".
if [ $# -ne 2 ]
then
echo "Usage: `basename $0` old-pattern new-pattern"
# As in "rn gif jpg", which renames all gif files in working directory to jpg.
exit 1
fi
number=0 # Keeps track of how many files actually renamed.
for filename in *$1* #Traverse all matching files in directory.
do
if [ -f $filename ] # If finds match...
then
fname=`basename $filename` # Strip off path.
n=`echo $fname | sed -e "s/$1/$2/"` # Substitute new for old in filename.
mv $fname $n # Rename.
let "number += 1"
fi
done
if [ $number -eq 1 ] # For correct grammar.
then
echo "$number file renamed."
else
echo "$number files renamed."
fi
exit 0
# Exercise for reader:
# What type of files will this not work on?
# How to fix this? |
Example A-3. encryptedpw: A script for uploading to an ftp site,
using a locally encrypted password
#!/bin/bash
# Example 3-71 modified to use encrypted password.
if [ -z $1 ]
then
echo "Usage: `basename $0` filename"
exit 1
fi
Username=bozo
# Change to suit.
Filename=`basename $1`
# Strips pathname out of file name
Server="XXX"
Directory="YYY"
# Change above to actual server name & directory.
password=`cruft <pword`
# "pword" is the file containing encrypted password.
# Uses the author's own "cruft" file encryption package,
# based on onetime pad algorithm,
# and obtainable from:
# Primary-site: ftp://metalab.unc.edu /pub/Linux/utils/file
# cruft-0.2.tar.gz [16k]
ftp -n $Server <<End-Of-Session
# -n option disables auto-logon
user $Username $Password
binary
bell
# Ring 'bell' after each file transfer
cd $Directory
put $Filename
bye
End-Of-Session
exit 0 |
+ The following two scripts are by Mark Moraes of the University
of Toronto. See the enclosed file "Moraes-COPYRIGHT"
for permissions and restrictions. Example A-4. behead: A script for removing mail and news message headers
#! /bin/sh
# Strips off the header from a mail/News message i.e. till the first
# empty line
# Mark Moraes, University of Toronto
# --> These comments added by author of HOWTO.
if [ $# -eq 0 ]; then
# --> If no command line args present, then works on file redirected to stdin.
sed -e '1,/^$/d' -e '/^[ ]*$/d'
# --> Delete empty lines and all lines until
# --> first one beginning with white space.
else
# --> If command line args present, then work on files named.
for i do
sed -e '1,/^$/d' -e '/^[ ]*$/d' $i
# --> Ditto, as above.
done
fi
# --> Exercise for the reader: Add error checking and other options.
# -->
# --> Note that the small sed script repeats, except for the arg passed.
# --> Does it make sense to embed it in a function? Why or why not? |
Example A-5. ftpget: A script for downloading files via ftp
#! /bin/sh
# $Id: ftpget,v 1.2 91/05/07 21:15:43 moraes Exp $
# Script to perform batch anonymous ftp. Essentially converts a list of
# of command line arguments into input to ftp.
# Simple, and quick - written as a companion to ftplist
# -h specifies the remote host (default prep.ai.mit.edu)
# -d specifies the remote directory to cd to - you can provide a sequence
# of -d options - they will be cd'ed to in turn. If the paths are relative,
# make sure you get the sequence right. Be careful with relative paths -
# there are far too many symlinks nowadays.
# (default is the ftp login directory)
# -v turns on the verbose option of ftp, and shows all responses from the
# ftp server.
# -f remotefile[:localfile] gets the remote file into localfile
# -m pattern does an mget with the specified pattern. Remember to quote
# shell characters.
# -c does a local cd to the specified directory
# For example,
# ftpget -h expo.lcs.mit.edu -d contrib -f xplaces.shar:xplaces.sh \
# -d ../pub/R3/fixes -c ~/fixes -m 'fix*'
# will get xplaces.shar from ~ftp/contrib on expo.lcs.mit.edu, and put it in
# xplaces.sh in the current working directory, and get all fixes from
# ~ftp/pub/R3/fixes and put them in the ~/fixes directory.
# Obviously, the sequence of the options is important, since the equivalent
# commands are executed by ftp in corresponding order
#
# Mark Moraes (moraes@csri.toronto.edu), Feb 1, 1989
# --> Angle brackets changed to parens, so Docbook won't get indigestion.
#
# --> These comments added by author of HOWTO.
# PATH=/local/bin:/usr/ucb:/usr/bin:/bin
# export PATH
# --> Above 2 lines from original script probably superfluous.
TMPFILE=/tmp/ftp.$$
# --> Creates temp file, using process id of script ($$)
# --> to construct filename.
SITE=`domainname`.toronto.edu
# --> 'domainname' similar to 'hostname'
# --> May rewrite this to parameterize this for general use.
usage="Usage: $0 [-h remotehost] [-d remotedirectory]... [-f remfile:localfile]... \
[-c localdirectory] [-m filepattern] [-v]"
ftpflags="-i -n"
verbflag=
set -f # So we can use globbing in -m
set x `getopt vh:d:c:m:f: $*`
if [ $? != 0 ]; then
echo $usage
exit 1
fi
shift
trap 'rm -f ${TMPFILE} ; exit' 0 1 2 3 15
echo "user anonymous ${USER-gnu}@${SITE} > ${TMPFILE}"
# --> Added quotes (recommended in complex echoes).
echo binary >> ${TMPFILE}
for i in $*
# --> Parse command line args.
do
case $i in
-v) verbflag=-v; echo hash >> ${TMPFILE}; shift;;
-h) remhost=$2; shift 2;;
-d) echo cd $2 >> ${TMPFILE};
if [ x${verbflag} != x ]; then
echo pwd >> ${TMPFILE};
fi;
shift 2;;
-c) echo lcd $2 >> ${TMPFILE}; shift 2;;
-m) echo mget "$2" >> ${TMPFILE}; shift 2;;
-f) f1=`expr "$2" : "\([^:]*\).*"`; f2=`expr "$2" : "[^:]*:\(.*\)"`;
echo get ${f1} ${f2} >> ${TMPFILE}; shift 2;;
--) shift; break;;
esac
done
if [ $# -ne 0 ]; then
echo $usage
exit 2
fi
if [ x${verbflag} != x ]; then
ftpflags="${ftpflags} -v"
fi
if [ x${remhost} = x ]; then
remhost=prep.ai.mit.edu
# --> Rewrite to match your favorite ftp site.
fi
echo quit >> ${TMPFILE}
# --> All commands saved in tempfile.
ftp ${ftpflags} ${remhost} < ${TMPFILE}
# --> Now, tempfile batch processed by ftp.
rm -f ${TMPFILE}
# --> Finally, tempfile deleted (you may wish to copy it to a logfile).
# --> Exercises for reader:
# --> 1) Add error checking.
# --> 2) Add bells & whistles. |
+ Antek Sawicki contributed the following script, which makes very
clever use of the parameter substitution operators discussed in
Section 3.3.1. Example A-6. password: A script for generating random
8-character passwords #!/bin/bash
# May need to be invoked with #!/bin/bash2 on some machines.
#
# Random password generator for bash 2.x by Antek Sawicki <tenox@tenox.tc>,
# who generously gave permission to the HOWTO author to use it here.
#
# ==> Comments added by HOWTO author ==>
MATRIX="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
LENGTH="8"
# ==> May change 'LENGTH' for longer password, of course.
while [ ${n:=1} -le $LENGTH ]
# ==> Recall that := is "default substitution" operator.
# ==> So, if 'n' has not been initialized, set it to 1.
do
PASS="$PASS${MATRIX:$(($RANDOM%${#MATRIX})):1}"
# ==> Very clever, but tricky.
# ==> Starting from the innermost nesting...
# ==> ${#MATRIX} returns length of array MATRIX.
# ==> $RANDOM%${#MATRIX} returns random number between 1 and length of MATRIX.
# ==> ${MATRIX:$(($RANDOM%${#MATRIX})):1}
# ==> returns expansion of MATRIX at random position, by length 1.
# ==> See {var:pos:len} parameter substitution in Section 3.3.1 and following examples.
# ==> PASS=... simply pastes this result onto previous PASS (concatenation).
# ==> To visualize this more clearly, uncomment the following line
# ==> echo "$PASS"
# ==> to see PASS being built up, one character at a time, each iteration of the loop.
let n+=1
# ==> Increment 'n' for next pass.
done
echo "$PASS"
#== Or, redirect to file, as desired. |
+ James R. Van Zandt contributed this script, which uses named pipes
and, in his words, "really exercises quoting and escaping".
Example A-7. fifo: A script for making daily backups, using
named pipes #!/bin/bash
# ==> Script by James R. Van Zandt, and used here with his permission.
# ==> Comments added by author of HOWTO.
HERE=`uname -n`
# ==> hostname
THERE=bilbo
echo "starting remote backup to $THERE at `date +%r`"
# ==> `date +%r` returns time in 12-hour format, i.e. "08:08:34 PM".
# make sure /pipe really is a pipe and not a plain file
rm -rf /pipe
mkfifo /pipe
# ==> Create a "named pipe", named "/pipe".
# ==> 'su xyz' runs commands as user "xyz".
# ==> 'ssh' invokes secure shell (remote login client).
su xyz -c "ssh $THERE \"cat >/home/xyz/backup/${HERE}-daily.tar.gz\" < /pipe"&
cd /
tar -czf - bin boot dev etc home info lib man root sbin share usr var >/pipe
# ==> Uses named pipe, /pipe, to communicate between processes:
# ==> 'tar/gzip' writes to /pipe and 'ssh' reads from /pipe.
# ==> The end result is this backs up the main directories, from / on down.
# ==> What are the advantages of a "named pipe" in this situation,
# ==> as opposed to an "anonymous pipe", with |?
# ==> Will an anonymous pipe even work here?
exit 0 |
+ Jordi Sanfeliu gave permission to use his elegant
tree script. Example A-8. tree: A script for displaying a directory
tree #!/bin/sh
# @(#) tree 1.1 30/11/95 by Jordi Sanfeliu
# email: mikaku@arrakis.es
#
# Initial version: 1.0 30/11/95
# Next version : 1.1 24/02/97 Now, with symbolic links
# Patch by : Ian Kjos, to support unsearchable dirs
# email: beth13@mail.utexas.edu
#
# Tree is a tool for view the directory tree (obvious :-) )
#
# ==> 'Tree' script used here with the permission of its author, Jordi Sanfeliu.
# ==> Comments added by HOWTO author.
search () {
for dir in `echo *`
# ==> `echo *` lists all the files in current working directory, without line breaks.
# ==> Same effect as for dir in *
do
if [ -d $dir ] ; then # ==> If it is a directory (-d)...
zz=0 # ==> Temp variable, keeping track of directory level.
while [ $zz != $deep ] # Keep track of inner nested loop.
do
echo -n "| " # ==> Display vertical connector symbol,
# ==> with 2 spaces & no line feed in order to indent.
zz=`expr $zz + 1` # ==> Increment zz.
done
if [ -L $dir ] ; then # ==> If directory is a symbolic link...
echo "+---$dir" `ls -l $dir | sed 's/^.*'$dir' //'`
# ==> Display horiz. connector and list directory name, but...
# ==> delete date/time part of long listing.
else
echo "+---$dir" # ==> Display horizontal connector symbol...
# ==> and print directory name.
if cd $dir ; then # ==> If can move to subdirectory...
deep=`expr $deep + 1` # ==> Increment depth.
search # with recursivity ;-)
# ==> Function calls itself.
numdirs=`expr $numdirs + 1` # ==> Increment directory count.
fi
fi
fi
done
cd .. # ==> Up one directory level.
if [ $deep ] ; then # ==> If depth = 0 (returns TRUE)...
swfi=1 # ==> set flag showing that search is done.
fi
deep=`expr $deep - 1` # ==> Decrement depth.
}
# - Main -
if [ $# = 0 ] ; then
cd `pwd` # ==> No args to script, then use current working directory.
else
cd $1 # ==> Otherwise, move to indicated directory.
fi
echo "Initial directory = `pwd`"
swfi=0 # ==> Search finished flag.
deep=0 # ==> Depth of listing.
numdirs=0
zz=0
while [ $swfi != 1 ] # While flag not set...
do
search # ==> Call function after initializing variables.
done
echo "Total directories = $numdirs"
# ==> Challenge to reader: try to figure out exactly how this script works. |
|