Package: bash
Version: 2.05a-11


I've been doing some basic programming with bash and found some things that
I believe to be inconsistancies. There are three (3) similar concerns I have





--- #1 -------------------

The first matter has to do with the distinction between the functions of "="
and "==". There is abiguity in the way bash parses two different
applications of the operators.

In one case bash demands the == operator for numeric statements such as:

        # This format is required
        if $((num1 == num2)); then

        # This format fails
        if $((num1 = num2)); then

However in regards to strings the = or == is equally acceptable:

        # This format is acceptable
        if [ $str1 == $str2 ]; then

        # This format is acceptable
        if [ $str1 = $str2 ]; then


It seems to me that the format "=" should be restricted to assignment
operations only. In my opinion, the test for equality should always demand
the "==" operator. This would be consistent with the rules of C generally,
and since Unix/Linux is (as I understand it) built upon the C/C++ syntax,
then "=" SHOULD be restricted to assignment. It is also consistent with the
application of the "!=" operator as well to demand that equality be tested
ONLY using "==".

Thus

        if [ $str1 = $str2 ]; then

should be an assignment and generate a parser error, whereas

        if [ $str1 == $str2 ]; then

should parse acceptably.



Why hasn't this been addressed, and if so, what is the reasoning to retain
it?





--- #2 -------------------

The next issue has to do with the actual [ test ] function in regards to
variables, particularly strings.

I found through hours of debugging the statement

        str1=""
        if [ -n $str1 ]; then

will return true when perhaps it should return false.



Consider the code I have written below and compare to the output results:



# TEST CODE

echo
echo check other true/false tests
tsVal=y
echo Where tsVal=$tsVal
echo
if [ $tsVal ]; then echo [ \$tsVal ] is true; else echo [ \$tsVal ] is
false; fi
if [ ${tsVal} ]; then echo [ \${tsVal} ] is true; else echo [ \${tsVal} ] is
false; fi

echo
if [ -z $tsVal ]; then echo [ -z \$tsVal ] is true; else echo [ -z \$tsVal ]
is false; fi
if [ -z ${tsVal} ]; then echo [ -z \${tsVal} ] is true; else echo [ -z
\${tsVal} ] is false; fi

echo
if [ -n $tsVal ]; then echo [ -n \$tsVal ] is true; else echo [ -n \$tsVal ]
is false; fi
if [ -n ${tsVal} ]; then echo [ -n \${tsVal} ] is true; else echo [ -n
\${tsVal} ] is false; fi



echo
tsVal=
echo Where tsVal=$tsVal
echo
if [ $tsVal ]; then echo [ \$tsVal ] is true; else echo [ \$tsVal ] is
false; fi
if [ ${tsVal} ]; then echo [ \${tsVal} ] is true; else echo [ \${tsVal} ] is
false; fi

echo
if [ -z $tsVal ]; then echo [ -z \$tsVal ] is true; else echo [ -z \$tsVal ]
is false; fi
if [ -z ${tsVal} ]; then echo [ -z \${tsVal} ] is true; else echo [ -z
\${tsVal} ] is false; fi

echo
if [ -n $tsVal ]; then echo [ -n \$tsVal ] is true; else echo [ -n \$tsVal ]
is false; fi
if [ -n ${tsVal} ]; then echo [ -n \${tsVal} ] is true; else echo [ -n
\${tsVal} ] is false; fi





# OUTPUT RESULTS

check other true/false tests
Where tsVal=y
[ $tsVal ] is true
[ ${tsVal} ] is true

[ -z $tsVal ] is false
[ -z ${tsVal} ] is false

[ -n $tsVal ] is true
[ -n ${tsVal} ] is true

Where tsVal=

[ $tsVal ] is false
[ ${tsVal} ] is false

[ -z $tsVal ] is true
[ -z ${tsVal} ] is true

[ -n $tsVal ] is true
[ -n ${tsVal} ] is true



The problem is that when "" is tested for non-zero string status, it returns
true. This seems odd since bash parses using whitespace seperation, which is
no different than whitespace delimiting. Granted a string of " " is not
really null, but then it raises the question:

        When does tsVal=  result in a zero string?



This is particularly odd when you consider that the tests using -z or -n
test for the LENGTH of the string. So I tested using:

        tsVal=
        echo length is ${#tsVal}
        if [ -n $tsVal ]; then echo length is non-zero; else echo length is 
zero;
fi

and the output is:

        length is 0
        length is non-zero

which is wrong!!! In other words ALL VARIABLES HAVE LENGTH since they are
stored as strings in the environment. This means that a variable's value can
never not exist!



But this is contrary to valid implementation, which is how I became aware of
it. I was having problems testing for a string match when tsVal resulted in
"".


        if [ $tsVal == $tsTest ]; then


resulted in a parsing error

        [: == : unary operator expected


How can this be if all string variables have length?



The only reliable solution becomes:

        if [ $tsVal ]; then

                if [ $tsTest ]; then

                        if [ $tsVal == $tsTest ]; then


But this demands a level of nesting for each variable that can end up as ""
to prevent errors.



The alternative is to use:

        if [ "$tsVal" == "$tsTest" ]; then

which will work, but doesn't make a whole lot of sense since the result is:

        if [ "" == "" ]; then

which is theoretically no different than:

        tsVal=""
        tsTest=""
        if [ $tsVal == $tsTest ]; then

which generates an error.



It seems to me that if either of $tsVal or $tsTest contains "" or " " then
[test] should be able to test for non-whitespace existance, and return false
instead of causing a parser error. Logically, bash should be able to
evaluate expressions involving strings as:


        if [ $tsVal defined and not whitespace ] && [ $tsTest defined and not
whitespace ]; then

                if [ $tsVal == $tsTest ]; then

                        return(1)

                else

                        return(0)

                fi

        else

                return(0)

        fi



It also seems to me that this should be fairly simple to implement since
bash needs to look through the existing environment to find a variable and
if it doesn't exist return a false condition.





--- #3 -------------------

The last issue is in regards to adding a built in function to bash to parse
the contents of variables.

I was having problems generating a valid PATH variable depending upon a
number of factors. I wanted a function that would meet the test


        export PATH=dir1:dir2:dirN

        if [ -d ~/bash ]; then

                export PATH=fnVarAdd($PATH, ~/bash, ":")

        fi

        echo $PATH


such that the rules for fnVarAdd(VarName, ValStr, DeLim) would be:

        fnVarAdd would ensure unique values within itself matching ValStr
        ValStr could allow for values to be added or removed from VarName
        DeLim could specify a delimeter different from a default ":"



The result was the function SFVarAdd used as demonstrated

        tsVal="sox:mets:cardinals:yankees"
        tsVal=SFVarAdd($tsVal, -:mets:+:bluejays:yankees, :)
        echo tsVal is $tsVal


When executed results in:

        tsVal is sox:cardinals:yankees:bluejays:


So, all instances of mets is removed, bluejays is added, and all other
values are ensured to occur only once



The result is below, but does not allow for \: or \DeLim escape sequences.


#---------------------------------------------------------------------------
---
# NAME:         SFVarAdd
# AUTHOR:       Christopher Buckley
# DATE:         January-2005
# COPYRIGHT:    GNU for U (as per gnu public license)
# DEBUGGING:    Use $DEBUG_CALLS for entry & exit debugging
#               Use $DEBUG_FLOW for flow control detection
#               Use $DEBUG_SYS for debugging system scripts
#               Use $DEBUG_USR for debugging user scripts
# DESCRIPTION:  Use this file as:
#
#                               set VarName=`SFVarAdd $VarName Values Delimiter`
#
# INPUTS:       $1      = contents of variable
#               $2      = add or delete string, format as (default is add):
#                               +:a1:a2:a(x):-:d1:d2:d(x)
#                           where   default is add
#                                   delimter is colon
#                                   + means add following
#                                   - means del following
#               [$3]    = Optional delimiter to use (default is colon)
# OUTPUTS:      $1 with/without added/deleted
#---------------------------------------------------------------------------
---



# Interactive shells preceeded with "-"
if [ -n "$DEBUG_CALLS" ]; then
    if [ "$0" == "-bash" ]; then
        echo ENTER: SFVarAdd
    else
        echo ENTER: $0
    fi
fi
#---------------------------------------------------------------------------
---
# ARGUMENTS:
#---------------------------------------------------------------------------
---



tsArgs=$2
tsLim=":"
tsMode="+"
tsVar=$1
tiCntA=$((${#tsArgs}-1))
tiNdxA=-1
if [ $3 ]; then
    $tsLim=$3
fi





#---------------------------------------------------------------------------
---
# MAIN:
#---------------------------------------------------------------------------
---



# Make sure we got input and not breaking
while ((tiCntA > tiNdxA)) && [ ! $tbBreak ]; do


    # Next argument character
    tbBreak=
    tsMatch=
    tiNdxA=$((tiNdxA + 1))
    tsChar=${tsArgs:tiNdxA:1}


    if ((tiCntA == tiNdxA)); then

        tbBreak=y
    fi


    # Check for evaluation change +: -:
    if ((${#tsWord} == 1)) && ([ $tsChar == $tsLim ] || [ $tbBreak ]); then

        if [ $tsWord == "+" ]; then
            tsMatch=y
        fi

        if [ $tsWord == "-" ]; then
            tsMatch=y
        fi

        if [ $tsMatch ]; then
            tsMatch=
            tsChar=
            tsMode=$tsWord
            tsWord=
        fi

    fi


    # Check for substring in argument
    if [ $tsChar ]; then

        if ([ $tsChar != $tsLim ] || [ $tbBreak ]); then

            tsWord=$tsWord$tsChar

        fi

        if ([ $tsChar == $tsLim ] || [ $tbBreak ]); then

            # Reset match parameters
            tsMatch=y

        fi

    fi


    # Look for match in variable
    if [ $tsMatch ]; then

        tiCntV=$((${#tsVar}-1))
        tiNdxV=-1
        tiStart=
        while ((tiCntV > tiNdxV)); do

            # Scan to delimiter - empty path ignored
            tsStrPrefix=
            tsStrSuffix=
            while ((tiCntV > tiNdxV)); do

                # Next path character
                tiNdxV=$((tiNdxV + 1))
                tsChar=${tsVar:tiNdxV:1}


                # Check for valid character
                if [ $tsChar != $tsLim ]; then

                    tsStrPrefix=$tsStrPrefix$tsChar

                fi

                # Check for terminator
                if [ $tsChar == $tsLim ]; then

                    break

                fi

            done


            # Match only on argument found in path
            if [ $tsStrPrefix ]; then

                if [ $tsStrPrefix != $tsWord ]; then

                    # Set new match point for next delete
                    tiStart=$tiNdxV

                fi


                if [ $tsStrPrefix == $tsWord ]; then

                    # Check for duplicate appends
                    # Check for delete mode and continue
                    if [ $tsMatch == "x" ] || [ $tsMode == "-" ]; then

                        # Delete every entry
                        tsStrPrefix=${tsVar:0:tiStart}
                        tsStrSuffix=${tsVar:tiNdxV:$((tiCntV-tiNdxV+1))}
                        tsVar=$tsStrPrefix$tsStrSuffix

                        # Adjust the count
                        tiCntV=$((${#tsVar}-1))

                        # Check for restart
                        if [ $tsMatch != "x" ]; then
                            tiNdxV=-1
                            tiStart=0
                        fi

                    else

                        # Retained but no more
                        tsMatch="x"
                        tiStart=$tiNdxV

                    fi

                fi

            fi


        #END tiCntV > tiNdxV
        done


        # Check if not found in append mode
        if [ $tsMatch == "y" ] && [ $tsMode == "+" ]; then

            # Check for previous variable
            if [ $tsVar ]; then
                tsVar=${tsVar}$tsLim$tsWord
            else
                tsVar=$tsWord
            fi

        fi


        # Reset loop parameters
        tsChar=
        tsWord=


    #END tsMatch
    fi


#END tiCntA > tiNdxA
done





#---------------------------------------------------------------------------
---
# FLUSH LOCALS - Use unset command:
#---------------------------------------------------------------------------
---



unset tbBreak
unset tiStart
unset tiCntA
unset tiCntV
unset tiNdxA
unset tiNdxV
unset tsArgs
unset tsChar
unset tsMatch
unset tsMode
unset tsStrPrefix
unset tsStrSuffix
unset tsWord






#---------------------------------------------------------------------------
---
# LEAVE:
#---------------------------------------------------------------------------
---
# That's all folks!
if [ -n "$DEBUG_CALLS" ]; then
    if [ "$0" == "-bash" ]; then
        echo LEAVE: SFVarAdd
    else
        echo LEAVE: $0
    fi
fi





#---------------------------------------------------------------------------
---
# RETURN RESULTS:
#---------------------------------------------------------------------------
---

echo $tsVar









-- 
To UNSUBSCRIBE, email to [EMAIL PROTECTED]
with a subject of "unsubscribe". Trouble? Contact [EMAIL PROTECTED]

Reply via email to