#!/bin/bash # copyright 2007 Gilbert Ashley # BashTrix grepis an implementation of the 'grep' command # written in pure shell. Very few grep options are supported. VERSION=0.1 ERROR=0 # show program usage show_usage() { echo "Usage: ${0##/*} [OPTION]... [PATTERN] [FILE]..." echo "Try '${0##/*} --help' for more information." exit $ERROR } # show program help show_help() { echo echo "Usage: ${0##/*} [OPTION]... [PATTERN] [FILE]..." echo "${0##/} -[v] PATTERN FILE" echo " or: (cat|echo) | ${0##/*} [OPTION]... [PATTERN]" echo "Print lines of each FILE to standard output which contain matches of PATTERN." echo "With more than one FILE, precede each with a header giving the file name." echo "With no FILE, read standard input." echo " -q, --quiet, --silent never print headers giving file names" echo " -v, --reverse Reverse the match" echo " --verbose always print headers giving file names" echo " --help display this help and exit" echo " --version output version information and exit" exit $ERROR } show_version() { echo ${0##/*}" (BashTrix) $VERSION" echo "Copyright 2007 Gilbert Ashley " echo "This is free software written in pure shell." exit $ERROR } # show usage if '-h' or '--help' is the first argument or no argument is given case $1 in "") show_usage ;; "-h"|"--help") show_help ;; "--version") show_version ;; esac # get the - options for WORD in "$@" ; do case $WORD in -*) true ; case $WORD in -v) REVERSE_MATCH=1 ; shift ;; -n) PRINT_LINE_NUMBERS=1 ; shift ;; -H) PRINT_FILE_NAMES=1 ; shift ;; --help) show_usage ;; --version) show_version ;; -) READ_STDIN=1 ; shift ;; esac ;; esac done # get the search PATTERN and advance # these patterns are processed by a case statement which can't deal with # regular expresions. We identify regex anchors (beginning '^' and ending '$' characters, # which denote line begins with or line ends with), and strip off the character # so case can deal with it directly. case handles bracket-enclosed expressions # mostly the same. However this routine does not deal with '[^sometext]' or '[sometext$]' -yet. PATTERN=$1 if [[ "${PATTERN:0:1}" = "^" ]] ; then MATCH_BEGINNING=1 PATTERN=${PATTERN:1} #PATTERN=${PATTERN/^/} else OFFSET=$(( ${#PATTERN} - 1 )) if [[ "${PATTERN:$OFFSET:1}" = "$" ]] ; then MATCH_ENDING=1 PATTERN=${PATTERN:0:$OFFSET} fi fi if [[ "${PATTERN:0:1}" = "[" ]] ; then if [[ "${PATTERN:1:1}" = "^" ]] ; then REVERSE_MATCH=1 PATTERN="[${PATTERN:2}" else OFFSET=$(( ${#PATTERN} - 2 )) if [[ "${PATTERN:$OFFSET:1}" = "$" ]] ; then MATCH_ENDING=1 PATTERN="${PATTERN:0:$OFFSET}]" fi fi fi echo $PATTERN # move to the next argument in the command line shift if [[ $# -gt 0 ]] ; then while [[ $# -gt 0 ]] ; do FILE_NAME="$1" if [ ! -r "$1" ] ; then echo "Cannot find file $1" 1>&2 ERROR=1 break else # write the header output if requested if [[ $SHOW_HEADERS ]] ; then echo "==> $FILE_NAME <==" fi # reset to the start and print the requested lines FILE_LINE_COUNT=0 LINE= IFS= while read LINE ; do # add the curent line to the line counter (( FILE_LINE_COUNT++ )) STRING=$LINE if [[ $PRINT_LINE_NUMBERS ]] ; then OUTPUT_HEADER="$FILE_LINE_COUNT:" fi if [[ $PRINT_FILE_NAMES ]] ; then OUTPUT_HEADER="$FILE_NAME:$OUTPUT_HEADER" fi case $STRING in $PATTERN*) if [[ ! $REVERSE_MATCH ]] ; then echo $OUTPUT_HEADER$STRING fi ;; *$PATTERN) if [[ ! $REVERSE_MATCH ]] && [[ ! $MATCH_BEGINNING ]] ; then echo $OUTPUT_HEADER$STRING fi ;; *$PATTERN*) if [[ ! $REVERSE_MATCH ]] ; then if [[ ! $MATCH_BEGINNING ]] && [[ ! $MATCH_ENDING ]] ; then echo $OUTPUT_HEADER$STRING fi fi ;; *) if [[ $REVERSE_MATCH ]] ; then if [[ "$STRING" != "" ]] ; then echo $OUTPUT_HEADER$STRING fi fi ;; esac # go to next LINE done <"$1" fi # go to next FILE in $@ shift done else # piped input is presumed to be separated into lines already FILE_NAME="STDIN" # write the header output if requested if [[ $SHOW_HEADERS ]] ; then echo "==> $FILE_NAME <==" fi # output the requested lines in the file FILE_LINE_COUNT=0 LINE= IFS= while read LINE ; do # add the curent line to the line counter (( FILE_LINE_COUNT++ )) STRING=$LINE case $STRING in $PATTERN*) if [[ $MATCH_BEGINNING ]] && [[ ! $REVERSE_MATCH ]] ; then echo $STRING fi #break ;; *$PATTERN) if [[ $MATCH_ENDING ]] && [[ ! $REVERSE_MATCH ]] ; then echo $STRING fi ;; *$PATTERN*) if [[ ! $REVERSE_MATCH ]] ; then if [[ ! $MATCH_BEGINNING ]] && [[ ! $MATCH_ENDING ]] ; then echo $STRING fi fi ;; *) if [[ $REVERSE_MATCH ]] ; then if [[ "$STRING" != "" ]] ; then echo $STRING fi fi ;; esac # go to next LINE shift done fi exit $ERROR