Menu

Simulating Grep

2016-03-31
2018-04-24
  • Lowell Boggs, Jr.

    Here is a example brash script that simulates grep using the following builtins:
    * .ls
    * .regex

    The script following script defines two functions: one which is used to print grep's help message, and the other that actually implements most of grep's main functionality

    #!/bin/brash
    
    function .grepPrintHelp
    {
       echo ""
       echo ".grep  -- a simulation of grep"
       echo ""
       echo "   .grep [options] word ..."
       echo ""
       echo "Options:"
       echo "   --help  -- print help"
       echo "   -h      -- suppress file names"
       echo "   -n      -- include line numbers"
       echo "   -e word -- specify an additional search pattern"
       echo "   -r      -- recursively search directories"
       echo ""
       echo "Description"
       echo ""
       echo "  Search the specified files for a specified regular expression"
       echo "  print the results -- possibly including the file name and line"
       echo "  numbers based on the options."
       echo ""
       echo "Default behavior:"
       echo ""
       echo "   If -e option is not specified, then assume the first word on"
       echo "   the command line is the pattern to search for.  The rest of"
       echo "   the words are assumed to be file names -- or directory names"
       echo "   If the -e option is specified, only the parameters to -e will"
       echo "   be treated as search expressions"
       echo ""
       echo "   When searching directories, all files in the directory will be"
       echo "   searched -- but recursion will only occur with -r"
       echo ""
    }
    
    function .grep
    (
       ropt=0  # -r not specified
       nopt="" # -n not specified
    
       pattern=""     # -e not specified.  this is a list of -e values separated by \| (both)
       patternSet=0
    
       hopt="-f"      # -h not specified
    
       if [ "$1" = "--help" ]
       then
          .grepPrintHelp
          return 1
       fi
    
       #
       #  interpret the command line options:  r, e, h, and n.  Option e requires a parameter.
       #
    
       while getopts re:hn name
       do
           case $name in
             e) 
    
             if [ "$pattern" = "" ] 
             then 
                 pattern="$OPTARG" 
             else
                 pattern="$pattern\\|$OPTARG"
             fi
            patternSet=1 
            ;;
    
             h) hopt="";;  # turn off filename
    
             r) ropt=1 ;;  # turn on recursion
    
             n) nopt="-l" ;; # turn on line numbers
    
             *)echo "Invalid arg" 1>&2 ;;
           esac
       done
    
       #
       #  Remove the -options, if any
       #
    
       let shiftcount=OPTIND-1  
    
    
       if (( shiftcount >= 1 ))
       then
           shift $shiftcount
       fi
    
       #
       #  if no -e options specified, the consume the first parameter as the pattern
       #  to search for.
       #
    
       if (( patternSet == 0 ))
       then
          pattern="$1"
          shift
       fi
    
       if [ $# = 0 ]
       then
           # no parameters were supplied, so print the help message
           .grepPrintHelp
           return 1
       fi
    
       #
       #  if there is only 1 option, and if that option is not a directory
       #  then turn off the displaying of file names.  If you need to display the name
       #  and there is only 1 file to search, use nul: as a second file name to overcome
       #  this annoyance.  (I know, but this is how the real grep works too).
       #
    
       if [ $# = 1 -a ! -d "$1" ]
       then
          hopt=""
       fi
    
       #
       #  Iterate over each file name option and handle it being a directory or not.
       #
    
       for f in "$@"
       do
    
           #
           #  ignore file names begining with .  (unless they are in a subdirectory search)
           #
    
           case "$f" in
           .*)
               continue
               ;;
           esac
    
    
           if [ -d "$f" ]
           then
    
          #
          #   If the current filename is a directory, then based on whether -r
          #   was specified or not do either of 2 things:
          #
          #     A.  iterate over all members of the directory and just grep
          #         them -- if -r is not specified.
          #
          #     B.  recursively visit the whole directory tree and search each
          #         file -- ignoring subdirectories as they will be handled at
          #         the top level
          #
    
    
    
          if [ "$ropt" != "0" ]
          then
              .ls -R "$f" | 
              while read g
              do
              if [ ! -d "$g" -a -r "$g" ]
              then
                  eval ".regex $hopt $nopt -m '$pattern' '$g'"
              fi
              done
          else
    
              for g in "$f"/*
              do
              if [ ! -d "$g" -a -r "$g" ]
              then
                  eval ".regex $hopt $nopt -m '$pattern' '$g'"
              fi
    
              done
    
          fi
    
    
    
           else
          eval ".regex $hopt $nopt -m '$pattern' '$f'"
           fi
    
       done
    
    
    
    )
    

    Note that due to the possibility of spaces in file names and directory names, it is advisable to always use double quotes around variable expansions.

     

    Last edit: Lowell Boggs, Jr. 2018-04-24
  • Lowell Boggs, Jr.

    I posted an update to the above example which changed \" to ' in the calls to eval which runs the .regex function on files in subdirectories. This corrected a problem where the pathnames containing backslashes were being misinterpreted as escape sequences. Sigh.

     

Anonymous
Anonymous

Add attachments
Cancel