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]