(M)  s i s t e m a   o p e r a c i o n a l   m a g n u x   l i n u x ~/ · documentação · suporte · sobre

 

Appendix A. Contributed Scripts

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.