#!/bin/bash # copyright 2007 Gilbert Ashley # minimal implementation similar to the 'cut' command show_usage() { echo echo ${0##/*}" Usage:" echo " Cut FIELDS from STRING using separator delimiter SEP" echo "${0##/} -[fFIELDS|f FIELDS|--fields=] -[dSEP|d SEP] STRING" echo "Using --output-delimiter= replaces the delimiter in the output." echo "Fields can be given using one of these forms: single field '2'" echo "open range '4-', closed range '2-5', or comma-separated list '2,3,4'" exit } case $1 in ""|"-h"|"--help") show_usage ;; esac # Minimum number of arguments needed by this program MINARGS=3 # count the number of arguments given COUNT=0 for ARGV in "$@" ; do (( COUNT++ )) ; done ARGC=$COUNT # check to make sure enough arguments were given if [[ $ARGC -lt $MINARGS ]] ; then echo "Too few arguments given (Minimum:$MINARGS)" echo show_usage fi # process command-line arguments for WORD in "$@" ; do case $WORD in -*) true ; case $WORD in --fields=*) [[ $DEBUG ]] && echo "Short range FIELD Option using '='" FIELDS=${WORD##*=} shift ;; -f?) [[ $DEBUG ]] && echo "Short single FIELD Option" FIELDS=${WORD##*f} shift ;; -f) [[ $DEBUG ]] && echo "Short split FIELD Option" if [[ ${2:0:1} != "-" ]] ; then FIELDS=$2 [[ $DEBUG ]] && echo FIELDS=$FIELDS shift 2 else echo "Missing argument" show_usage fi ;; -f*) [[ $DEBUG ]] && echo "Short FIELD Option range" FIELDS=${WORD##*f} [[ $DEBUG ]] && echo FIELDS=$FIELDS shift ;; -d?) [[ $DEBUG ]] && echo "Short single DEL Option" SEP=${WORD##*d} [[ $DEBUG ]] && echo FIELDS=$FIELDS shift ;; -d) [[ $DEBUG ]] && echo "Short split FIELD Option" if [[ ${2:0:1} != "-" ]] ; then SEP=$2 [[ $DEBUG ]] && echo FIELDS=$FIELDS shift 2 else echo "Missing argument" show_usage fi ;; -d*) [[ $DEBUG ]] && echo "Short-short DEL Option" SEP=${WORD##*d} [[ $DEBUG ]] && echo SEP=$SEP shift ;; --output-delimiter=*) [[ $DEBUG ]] && echo "Long NEW_SEP Option" NEW_SEP=${WORD##*=} [[ $DEBUG ]] && echo NEW_SEP=$NEW_SEP shift ;; esac ;; esac done # we've munched our way through the options arguments with shift # the remainder of the command-line is the INPUT_STREAM STRING="$@" # set the NEW_SEP to SEP unless --output-delimiter was given ! [[ $NEW_SEP ]] && NEW_SEP=$SEP # function _freq counts the number of matches # of PATTERN in PARSESTRING and returns FREQ # example usage: _freq $PATTERN $PARSESTRING function _freq() { FREQ=0 ! [[ $PATTERN ]] && PATTERN=$1 ! [[ $PARSESTRING ]] && PARSESTRING=$2 while [[ $PARSESTRING != "" ]] ; do case $PARSESTRING in *$PATTERN*) FREQ=$(( FREQ + 1 )) ; PARSESTRING=${PARSESTRING#*${PATTERN}} ;; *) PARSESTRING="" ;; esac done echo $FREQ } # _get_one_field returns the contents of a FIELD in STRING # fields are determined by a dividing separator SEP # STRING, FIELD and SEP must be supplied to this function function _get_one_field() { if [[ $FIELD = 1 ]] ; then # output the contents to the left of the first SEP OUTPUT=${STRING%%${SEP}*} else # Chop off the first match and cheat on the count STUB=${STRING#*$SEP} COUNT=1 # skip over any matches before the requested one while [[ $COUNT -lt $(( $FIELD - 1 )) ]] ; do # ROF Chop off leading FIELD and/or SEP STUB=${STUB#*$SEP} [[ $DEBUG ]] && echo $STUB (( COUNT++ )) done # LOF Chop off trailing SEP and/or rest of STUB OUTPUT=${STUB%%${SEP}*} fi echo $OUTPUT } # the command-line FIELDS variable takes one of these forms: # single field '2', open range '4-', closed range '2-5', or comma-separated list '2,3,4' # We examine the FIELDS variable with if's and convert everything to a LIST # the first character is always the START or HEAD HEAD=${FIELDS:0:1} # if the second character is a '-' then this is a range like: 2- or 3-5 if [[ ${FIELDS:1:1} = "-" ]] ; then # if the third character is null, range goes to end of STRING if [[ ${FIELDS:2:1} = "" ]] ; then # this is an open START to END range like: 2- #echo "START to END range" START=${FIELDS:0:1} # _freq counts the number of SEP's in STRING FINISH=$(_freq $SEP $STRING) # there are always one more TOTAL_FIELDS then SEP's FINISH=$(( FINISH + 1 )) else # this is a closed range like: 3-5 START=${FIELDS:0:1} FINISH=${FIELDS:2:1} fi # translate the RANGE values to a comma-separated LIST LIST="$START," COUNT=$START while [[ $COUNT -lt $(( $FINISH - 1 )) ]] ; do (( COUNT++ )) LIST=$LIST$COUNT"," done LIST=$LIST$FINISH elif [[ ${FIELDS:1:1} = "," ]] ; then # this is already a list # TODO? support a list like this:2,3,5- LIST=$FIELDS elif [[ ${FIELDS:1:1} = "" ]] ; then # single argument given LIST=${FIELDS:0:1} fi # read first character of FIELDS variable HEAD=${LIST:0:1} # increment the LIST by chopping the first 2 digits LIST=${LIST:2} # set the FIELD to the HEAD FIELD=$HEAD # read the contents of the START or HEAD FIELD OUTPUT="$(_get_one_field)" # start the OUTPUT_STACK with the first OUTPUT OUTPUT_STACK="$OUTPUT" # read the *rest* of the LIST of fields while [[ $LIST != "" ]] ; do # readfirst character FIELD=${LIST:0:1} # increment LIST LIST=${LIST:2} # the OUTPUT of the current FIELD OUTPUT="$(_get_one_field)" # add the OUTPUT to OUTPUT_STACK with a NEW_SEP between OUTPUT_STACK=$OUTPUT_STACK$NEW_SEP$OUTPUT done # this the final ouput FINAL_OUTPUT=$OUTPUT_STACK # official program FINAL_OUTPUT echo $FINAL_OUTPUT exit