#!/bin/bash # copyright 2007 Gilbert Ashley # BashTrix tr is an implementation of the 'tr' command # written in pure shell. VERSION=0.3 # show program usage show_usage() { echo echo "Usage: ${0##/*} SET1 SET2 [FILE]..." echo " or: (cat|echo) FILE | ${0##/*} SET1 SET2" echo "Translate or delete characters." echo "Translate, squeeze, and/or delete characters in a line." echo "With no FILE specified, standard input is read." echo " -d delete characters in SET1, do not translate" echo " -s --squeeze-repeats replace characters in SET1" echo " which occur repeatedly with a single character" echo " Note: -d and -s cannot be used together." echo " --help display this help and exit" echo " --version output version information and exit" exit } # show version information show_version() { echo ${0##/*}" (BashTrix) $VERSION" echo "Copyright 2007 Gilbert Ashley " echo "This is free software written in pure shell." exit } for WORD in "$@" ; do case $WORD in -d) DELETE_CHARS=1 ; shift ;; -s) SQUEEZE_REPEATS=1 ; shift ;; "-h"|"--help") show_usage ;; "--version") show_version ;; esac done if [[ $DELETE_CHARS ]] && [[ $SQUEEZE_REPEATS ]] ; then show_usage fi if [[ ! $1 ]] ; then show_usage else if [[ $DELETE_CHARS ]] ; then PATTERN_1=$1 PATTERN_2="" shift 1 elif [[ $SQUEEZE_REPEATS ]] ; then PATTERN_1=$1 PATTERN_2=$PATTERN_1 shift 1 else PATTERN_1=$1 PATTERN_2=$2 shift 2 fi fi #echo $PATTERN_1 # look-up table for uppercase conversion _upper_case() { ## a look-up table is the fastest method case $1 in a*) _UPR=A ;; b*) _UPR=B ;; c*) _UPR=C ;; d*) _UPR=D ;; e*) _UPR=E ;; f*) _UPR=F ;; g*) _UPR=G ;; h*) _UPR=H ;; i*) _UPR=I ;; j*) _UPR=J ;; k*) _UPR=K ;; l*) _UPR=L ;; m*) _UPR=M ;; n*) _UPR=N ;; o*) _UPR=O ;; p*) _UPR=P ;; q*) _UPR=Q ;; r*) _UPR=R ;; s*) _UPR=S ;; t*) _UPR=T ;; u*) _UPR=U ;; v*) _UPR=V ;; w*) _UPR=W ;; x*) _UPR=X ;; y*) _UPR=Y ;; z*) _UPR=Z ;; *) _UPR=${1%${1#?}} ;; esac echo $_UPR } # look-up table for lowercase conversion _lower_case() { _LWR= case $1 in A*) _LWR=a ;; B*) _LWR=b ;; C*) _LWR=c ;; D*) _LWR=d ;; E*) _LWR=e ;; F*) _LWR=f ;; G*) _LWR=g ;; H*) _LWR=h ;; I*) _LWR=i ;; J*) _LWR=j ;; K*) _LWR=k ;; L*) _LWR=l ;; M*) _LWR=m ;; N*) _LWR=n ;; O*) _LWR=o ;; P*) _LWR=p ;; Q*) _LWR=q ;; R*) _LWR=r ;; S*) _LWR=s ;; T*) _LWR=t ;; U*) _LWR=u ;; V*) _LWR=v ;; W*) _LWR=w ;; X*) _LWR=x ;; Y*) _LWR=y ;; Z*) _LWR=z ;; *) _LWR=${1%${1#?}} ;; esac echo $_LWR } if [[ $# -gt 0 ]] ; then while [[ $# -gt 0 ]] ; do FILE_NAME="$1" if [ ! -r "$1" ] ; then echo "Cannot find file $1" 1>&2 exit 1 else FILE_LINE_COUNT=0 LINE= IFS= while read LINE ; do OUTPUT_STRING= STRING=$LINE case $STRING in *$PATTERN_1*) if [[ $DELETE_CHARS ]] ; then # replace PATTERN_1 with nothing OUTPUT_STRING=${STRING//$PATTERN_1/} elif [[ $SQUEEZE_REPEATS ]] ; then # replace repeated PATTERN_1 characters OUTPUT_STRING=${STRING//$PATTERN_1$PATTERN_1/$PATTERN_1} else if [[ "${PATTERN_1}" = "[a-z]" ]] && [[ "${PATTERN_2}" = "[A-Z]" ]] ; then for CHAR in a b c d e f g h i j k l m n o p q r s t u v w x y z ; do REPLACEMENT_CHAR=$(_upper_case $CHAR) case $STRING in *$CHAR*) STRING=${STRING//$CHAR/$REPLACEMENT_CHAR} ;; esac done OUTPUT_STRING=$STRING #find and print the ascii char number # printf "%d\n" "'A" elif [[ "${PATTERN_1}" = "[A-Z]" ]] && [[ "${PATTERN_2}" = "[a-z]" ]] ; then for CHAR in A B C D E F G H I J K L M N O P Q R S T U V W X Y Z ; do REPLACEMENT_CHAR=$(_lower_case $CHAR) case $STRING in *$CHAR*) STRING=${STRING//$CHAR/$REPLACEMENT_CHAR} ;; esac done OUTPUT_STRING=$STRING else # replace PATTERN_1 with PATTERN_2 OUTPUT_STRING=${STRING//$PATTERN_1/$PATTERN_2} fi fi ;; # non-matching lines are output as-is *) OUTPUT_STRING=$STRING ;; esac echo $OUTPUT_STRING done <"$1" fi # go to next FILE in $@ shift done else # piped input is presumed to be separated into lines already LINE= IFS= while read LINE ; do OUTPUT_STRING= STRING=$LINE case $STRING in *$PATTERN_1*) if [[ $DELETE_CHARS ]] ; then # replace PATTERN_1 with nothing OUTPUT_STRING=${STRING//$PATTERN_1/} elif [[ $SQUEEZE_REPEATS ]] ; then # replace repeated PATTERN_1 characters OUTPUT_STRING=${STRING//$PATTERN_1$PATTERN_1/$PATTERN_1} else # replace PATTERN_1 with PATTERN_2 OUTPUT_STRING=${STRING//$PATTERN_1/$PATTERN_2} fi ;; # non-matching lines are output as-is *) OUTPUT_STRING=$STRING ;; esac echo $OUTPUT_STRING # go to next line in stdin shift done fi