Sunday, June 8, 2014

Unix Shell Example: Path Searching(1)

./script_1:
 #! /bin/bash  

 usage()  
 {  
   echo "script [-all] [-?] [-help] [-version] envvar pattern(s)"  
 }  

 usage_and_exit()  
 {  
   usage  
   exit 1  
 }  

 version()  
 {  
   echo $PROGRAM has version: $VERSION  
 }  

 # We output the current parameters user put in  
 # to compare those parameters with correct way to use  
 error()  
 {  
   echo "$@" 1>&2  
   usage_and_exit 1  
 }  

 # We use EXITCODE to track the number of warnings script  
 # has produced. EXITCODE is increased by one every time   
 # in this function. expr's role is doing the arithmetic   
 # expression  
 warning()  
 {  
   echo "$@" 1>&2  
   EXITCODE=`expr $EXITCODE + 1`  
 }  

 # We setup the IFS by ourvelves in case any security attack  
 # Attacking way:   
 # Change the field separator IFS to change the way of script  
 # interpreting the input  
 IFS="  
  "  

 # Another attacking way:  
 # Fool the script to run intended command at provided path.  
 # We change the PATH to simplest version and export it to avoid this attack.  
 # Exporting PATH can make sure all inherited process from this  
 # script get the this PATH variable.  
 OPATH="$PATH"  
 PATH=/bin:/usr/bin  
 export PATH  
 all=no  
 envvar=  
 EXITCODE=0  
 PROGRAM=`basename $0`  
 #echo $0: ./script_1  
 #echo $PRGORAM: script_1  
 #basename is the command used to strip off leading path  
 VERSION=1.0  

 while [ $# -gt 0 ]  
 do  
   case $1 in  
   -all | -al | -a)   
     all=yes  
     ;;  
   '-?' | -help | -hel | -he | -h)  
     usage_and_exit  
     ;;  
   -version | -ver | -v)  
     version  
     exit 0  
     ;;  
   -*)  
     error "Unrecognized option:" $1  
     ;;  
   *) # We have to provide this option here, otherwise envvar option will be ignored  
     break  
     ;;  
   esac  
   shift  
 done  

 # Assume the parameter after the option is the "envvar"  
 # and *) option above, make script get out of while loop, and  
 # come over here to process the envvar option  
 # We assign that value to envvar, and then if there is still  
 # more parameters, we use "shift" to get rid of it.  
 # If user doesn't specify the environment variable, then we use  
 # default PATH pattern, which is assigned and exported above  
 if [ $# -le 0 ]  
 then  
   envvar=PATH  
 else  
   envvar=$1  
   test $# -gt 0 && shift  
 fi  

 # If user provide "PATH" as the envvar, then we use the saved  
 # PATH vriable content as the environment path variable  
 test "x$envvar" = "xPATH" && envvar=OPATH  

 # dirpath is expanded from the envvar, then get used to search  
 # files from there.  
 # $envvar will firstly be expanded into "OPATH", and then it becomes  
 # dirpath=`eval echo ${OPATH} | tr : ' '`  
 # Then it will look up variable OPATH in the environment, expand it  
 # lastly transform all ":" to space.  
 # The role of "eval" is making sure the following command is with  
 # the correct shell parsing order  
 dirpath=`eval echo '${'"$envvar"'}' | tr : ' '`  

 # For the PATH we defined above, output:  
 # /bin /user/bin  
 # A few checks:  
 # 1. What if $envvar is empty  
 # 2. What if $envvar is not expanded  
 # 3. What if $envvar is expanded but empty  
 # 4. If all above 3 get passed, then what if there are no  
 #  files specified to be found  
 if test -z "$envvar"  
 then  
   error missing environment variable  
 elif test "x$dirpath" = "x$envvar"  
 then  
   error current shell can not expand envvar  
 elif test -z "$dirpath"   
 # we have to add double quote here to make it be ONE ARGUMENT,   
 # otherwise $dirpath will be replaced by multiple paths separated by space,   
 # making command test complain too many arguments  
 then  
   error empty directory search path  
 elif test $# -eq 0  
 then  
   exit 0  
 fi  

 # For each user provided file pattern, script will search them  
 # in each expanded directory. If the file is found (test -f),   
 # then script will output the path and file name to standard  
 # output. Per variable $all, script will decide whether to move  
 # on to look for all occurances of file, or just "break 2" to look  
 # for next pattern  
 for filepattern in "$@"  
 # We have to use double quote on "$@", otherwise the script won't   
 # accept names with spaces. For example "name with spaces", with  
 # double quote on "$@", it will just interpret as ONE NAME, instead of  
 # three names "name" "with" and "spaces"  
 do  
   result=  
   for searchPath in $dirpath  
   do  
     for file in $searchPath/$filepattern  
     do   
       if test -f $file  
       then  
         result=$file  
         echo $result  
         test $all = "no" && break 2  
       fi  
     done  
   done  
   # If result is empty, it means that the user provided pattern is not  
   # found at the given path. warning function will increase variable  
   # EXITCODE to indicate the number of unfound file pattern.  
   test -z $result && warning "$filepattern not found"  
 done  

 # Per Unix convention, return code has to be 0~127, since 126 and 127 are  
 # already reserved by unix system, we setup the 125 as the upper limit here.  
 # 0: success  
 # 126: found the command but not having permission  
 # 127: failed to find the command  
 test $EXITCODE -gt 125 && EXITCODE=125  

 exit $EXITCODE  

terminal:

No comments:

Post a Comment