Discussion:
[Help-bash] Arithmetic evaluation / expansion question
Bruce Hohl
2018-10-10 16:47:05 UTC
Permalink
An arithmetic evaluation / expansion question please: the man page
indicates that base 10 is the default, and null or unset variables evaluate
to zero.

Are strings also evaluated to zero within $(( ))?
(Except those beginning with 0 which are octals.)

$ pd=abc
$ echo $pd
abc
$ echo $((pd))
0
Andy Chu
2018-10-10 16:56:27 UTC
Permalink
In the statement

echo $(( pd ))

pd is parsed as a variable name, not a "string". So your question really
reduces to the first thing you mentioned -- undefined variables are coerced
to 0 in arithmetic contexts.

I am working on a bash compatible shell that warns you about this, and
provides the option for it to be a fatal error with 'set -o strict-arith'.
It's not done but it handles this case well, and I appreciate other bug
reports.

http://www.oilshell.org/

bash$ bin/osh
osh$ echo $(( undef ))
Line 1 of '<interactive>'
echo $(( undef ))
^~~~~
osh warning: Coercing undefined value to 0 in arithmetic context
0
osh$ echo $?
0

osh$ set -o strict-arith

osh$ echo $(( undef ))
Line 4 of '<interactive>'
echo $(( undef ))
^~~~~
Coercing undefined value to 0 in arithmetic context
osh$ echo $?
1
Post by Bruce Hohl
An arithmetic evaluation / expansion question please: the man page
indicates that base 10 is the default, and null or unset variables evaluate
to zero.
Are strings also evaluated to zero within $(( ))?
(Except those beginning with 0 which are octals.)
$ pd=abc
$ echo $pd
abc
$ echo $((pd))
0
Greg Wooledge
2018-10-10 17:07:46 UTC
Permalink
Post by Bruce Hohl
Are strings also evaluated to zero within $(( ))?
$ pd=abc
$ echo $pd
abc
$ echo $((pd))
0
As Andy said, things that can be parsed as variable names are treated
as variable names in arithmetic contexts. What he didn't mention was
that bash does this *recursively* until it gets to an integer, or to
something that can't be parsed as either an integer or a variable name.

wooledg:~$ a=b b=c c=d d=e e=f f=42; echo $((a))
42
Andy Chu
2018-10-10 17:25:27 UTC
Permalink
Right, I have seen this very odd recursive behavior. It doesn't appear to
be documented, but one of the very first bugs filed against OSH mentions it:

https://github.com/oilshell/oil/issues/3

In this case it's one the operands of [[ a -eq b ]]. Not only can 'a' be
a variable that contains a varaible name, etc. -- it can be an entire
arithmetic expression.

In other words, it can be thought of as an implicit recursive 'eval' as
well. Basically bash tries as hard as it can to make things integers in
arithmetic contexts. It's even more aggressive than awk or Make, which
also work with integers-as-strings.

(I chose not to copy this behavior since "important" shell scripts don't
appear to rely on it.)

Andy
Post by Greg Wooledge
Post by Bruce Hohl
Are strings also evaluated to zero within $(( ))?
$ pd=abc
$ echo $pd
abc
$ echo $((pd))
0
As Andy said, things that can be parsed as variable names are treated
as variable names in arithmetic contexts. What he didn't mention was
that bash does this *recursively* until it gets to an integer, or to
something that can't be parsed as either an integer or a variable name.
wooledg:~$ a=b b=c c=d d=e e=f f=42; echo $((a))
42
Andy Chu
2018-10-10 17:31:39 UTC
Permalink
To be clearer here is an example of the recursive eval (in $(( )) rather
than [[ ]], but it's the same thing in both places):

$ one=1
$ two=2
$ x='one + two'
$ code='4 > 2 ? x : y'
$ echo $(( code ))
3

But don't do that :) Or use eval explicitly.
Post by Andy Chu
Right, I have seen this very odd recursive behavior. It doesn't appear to
https://github.com/oilshell/oil/issues/3
In this case it's one the operands of [[ a -eq b ]]. Not only can 'a' be
a variable that contains a varaible name, etc. -- it can be an entire
arithmetic expression.
In other words, it can be thought of as an implicit recursive 'eval' as
well. Basically bash tries as hard as it can to make things integers in
arithmetic contexts. It's even more aggressive than awk or Make, which
also work with integers-as-strings.
(I chose not to copy this behavior since "important" shell scripts don't
appear to rely on it.)
Andy
Post by Greg Wooledge
Post by Bruce Hohl
Are strings also evaluated to zero within $(( ))?
$ pd=abc
$ echo $pd
abc
$ echo $((pd))
0
As Andy said, things that can be parsed as variable names are treated
as variable names in arithmetic contexts. What he didn't mention was
that bash does this *recursively* until it gets to an integer, or to
something that can't be parsed as either an integer or a variable name.
wooledg:~$ a=b b=c c=d d=e e=f f=42; echo $((a))
42
Bruce Hohl
2018-10-10 20:49:26 UTC
Permalink
Post by Greg Wooledge
As Andy said, things that can be parsed as variable names are treated
as variable names in arithmetic contexts.
I understand and noted that as described in the man page. I can see the
following from bash:

$ pd=abc; echo $pd
abc
$ echo $((pd))
0
$ echo $(($pd))
0

I noted in the man page that null or unset variables evaluate to zero.
Above, pd is a defined variable which holds "abc" (a string not beginning
with zero - non-octal)
pd is "arithmetically" evaluated to zero. Is this as intended? I.E. can
this behavior be relied upon?
Post by Greg Wooledge
What he didn't mention was
that bash does this *recursively* until it gets to an integer, or to
something that can't be parsed as either an integer or a variable name.
That is good to know !
Post by Greg Wooledge
To be clearer here is an example of the recursive eval (in $(( )) rather
$ one=1
$ two=2
$ x='one + two'
$ code='4 > 2 ? x : y'
$ echo $(( code ))
3
But don't do that :) Or use eval explicitly.
Post by Andy Chu
Right, I have seen this very odd recursive behavior. It doesn't appear
to
Post by Andy Chu
be documented, but one of the very first bugs filed against OSH mentions
https://github.com/oilshell/oil/issues/3
In this case it's one the operands of [[ a -eq b ]]. Not only can 'a'
be
Post by Andy Chu
a variable that contains a varaible name, etc. -- it can be an entire
arithmetic expression.
In other words, it can be thought of as an implicit recursive 'eval' as
well. Basically bash tries as hard as it can to make things integers in
arithmetic contexts. It's even more aggressive than awk or Make, which
also work with integers-as-strings.
(I chose not to copy this behavior since "important" shell scripts don't
appear to rely on it.)
Andy
Post by Greg Wooledge
Post by Bruce Hohl
Are strings also evaluated to zero within $(( ))?
$ pd=abc
$ echo $pd
abc
$ echo $((pd))
0
As Andy said, things that can be parsed as variable names are treated
as variable names in arithmetic contexts. What he didn't mention was
that bash does this *recursively* until it gets to an integer, or to
something that can't be parsed as either an integer or a variable name.
wooledg:~$ a=b b=c c=d d=e e=f f=42; echo $((a))
42
Andy Chu
2018-10-11 00:04:48 UTC
Permalink
Post by Bruce Hohl
I noted in the man page that null or unset variables evaluate to zero.
Above, pd is a defined variable which holds "abc" (a string not beginning
with zero - non-octal)
pd is "arithmetically" evaluated to zero. Is this as intended? I.E. can
this behavior be relied upon?
In an arithmetic context, pd is evaluated identically to $pd, so yes. Just
think of it as the $ being optional.

Andy
Bruce Hohl
2018-10-11 00:36:21 UTC
Permalink
In an arithmetic context, pd is evaluated identically to $pd ...
I do understand that as it is described in the man page. For arithmetic
evaluation, the man page also indicates that null or unset variables
evaluate to zero.

I have noted through "observation" (per the original example) that a
defined variable which holds a string not beginning with zero (non-octal)
also is "arithmetically" evaluated to zero. Is that behavior as intended?
I.E. can that behavior be relied upon? I admit I did not read the entire
man page. ;)
I noted in the man page that null or unset variables evaluate to zero.
Post by Bruce Hohl
Above, pd is a defined variable which holds "abc" (a string not beginning
with zero - non-octal)
pd is "arithmetically" evaluated to zero. Is this as intended? I.E. can
this behavior be relied upon?
In an arithmetic context, pd is evaluated identically to $pd, so yes.
Just think of it as the $ being optional.
Andy
Quentin L'Hours
2018-10-11 00:46:31 UTC
Permalink
Post by Bruce Hohl
In an arithmetic context, pd is evaluated identically to $pd ...
I do understand that as it is described in the man page. For arithmetic
evaluation, the man page also indicates that null or unset variables
evaluate to zero.
I have noted through "observation" (per the original example) that a
defined variable which holds a string not beginning with zero (non-octal)
also is "arithmetically" evaluated to zero. Is that behavior as intended?
I.E. can that behavior be relied upon? I admit I did not read the entire
man page. ;)
I think you're misunderstanding what is actually going on, it evaluates
to zero because of the recursive behavior described by Greg and Andy,
not because it contains something not beginning with zero:

When doing $((pd)) in your example, here's what happens:
- pd is evaluated to abc
- abc is unset so it is evaluated to 0

As simple as that.
if abc was set to 2, then $((pd)) would expand to 2.

That's also what is happening in Greg's example:
wooledg:~$ a=b b=c c=d d=e e=f f=42; echo $((a))
42

Hope this is a bit clearer.
Bruce Hohl
2018-10-11 01:23:04 UTC
Permalink
@Quentin
Light bulb! I understand now. Thanks!
Post by Bruce Hohl
Post by Bruce Hohl
In an arithmetic context, pd is evaluated identically to $pd ...
I do understand that as it is described in the man page. For arithmetic
evaluation, the man page also indicates that null or unset variables
evaluate to zero.
I have noted through "observation" (per the original example) that a
defined variable which holds a string not beginning with zero (non-octal)
also is "arithmetically" evaluated to zero. Is that behavior as
intended?
Post by Bruce Hohl
I.E. can that behavior be relied upon? I admit I did not read the entire
man page. ;)
I think you're misunderstanding what is actually going on, it evaluates
to zero because of the recursive behavior described by Greg and Andy,
- pd is evaluated to abc
- abc is unset so it is evaluated to 0
As simple as that.
if abc was set to 2, then $((pd)) would expand to 2.
wooledg:~$ a=b b=c c=d d=e e=f f=42; echo $((a))
42
Hope this is a bit clearer.
Chet Ramey
2018-10-11 14:39:32 UTC
Permalink
Post by Bruce Hohl
I have noted through "observation" (per the original example) that a
defined variable which holds a string not beginning with zero (non-octal)
also is "arithmetically" evaluated to zero.
Think of the variable's value being evaluated as an expression. If a
token that looks like a shell identifer is part of that value, the same
process occurs again.
--
``The lyf so short, the craft so long to lerne.'' - Chaucer
``Ars longa, vita brevis'' - Hippocrates
Chet Ramey, UTech, CWRU ***@case.edu http://tiswww.cwru.edu/~chet/
Chet Ramey
2018-10-11 14:36:34 UTC
Permalink
Post by Andy Chu
Right, I have seen this very odd recursive behavior. It doesn't appear to
"Within an expression, shell
variables may also be referenced by name without using the parameter
expansion syntax"

and

"The value of a variable is evaluated as an arithmetic expression when
it is referenced"
--
``The lyf so short, the craft so long to lerne.'' - Chaucer
``Ars longa, vita brevis'' - Hippocrates
Chet Ramey, UTech, CWRU ***@case.edu http://tiswww.cwru.edu/~chet/
Chet Ramey
2018-10-11 14:33:55 UTC
Permalink
Post by Greg Wooledge
Post by Bruce Hohl
Are strings also evaluated to zero within $(( ))?
$ pd=abc
$ echo $pd
abc
$ echo $((pd))
0
As Andy said, things that can be parsed as variable names are treated
as variable names in arithmetic contexts. What he didn't mention was
that bash does this *recursively* until it gets to an integer, or to
something that can't be parsed as either an integer or a variable name.
No, Greg. If the evaluator encounters a token that looks like a shell
identifier, its value is treated as an expression (if it's unset, that
expression evaluates to 0). This expression is run through the evaluator
again, as if it had been seen inline in parens. This is what makes

ten='2 * 5'
echo $(( ten ))
echo $(( $ten ))

both echo `10'. I'm surprised you said that, because this has come up a
number of times in the past, but your wiki has the same error.
--
``The lyf so short, the craft so long to lerne.'' - Chaucer
``Ars longa, vita brevis'' - Hippocrates
Chet Ramey, UTech, CWRU ***@case.edu http://tiswww.cwru.edu/~chet/
Greg Wooledge
2018-10-11 14:48:05 UTC
Permalink
Post by Chet Ramey
No, Greg. If the evaluator encounters a token that looks like a shell
identifier, its value is treated as an expression (if it's unset, that
expression evaluates to 0). This expression is run through the evaluator
again, as if it had been seen inline in parens. This is what makes
ten='2 * 5'
echo $(( ten ))
echo $(( $ten ))
both echo `10'. I'm surprised you said that, because this has come up a
number of times in the past, but your wiki has the same error.
OK, thanks. Corrected it on the wiki page.
https://mywiki.wooledge.org/ArithmeticExpression

Chet Ramey
2018-10-11 14:26:46 UTC
Permalink
Post by Bruce Hohl
An arithmetic evaluation / expansion question please: the man page
indicates that base 10 is the default, and null or unset variables evaluate
to zero.
Are strings also evaluated to zero within $(( ))?
Expressions that resolve to unset variables (since a token that looks
like a shell identifier is treated as a variable name) evaluate to 0.
--
``The lyf so short, the craft so long to lerne.'' - Chaucer
``Ars longa, vita brevis'' - Hippocrates
Chet Ramey, UTech, CWRU ***@case.edu http://tiswww.cwru.edu/~chet/
Loading...