sh man page ....

Ian Smith smithi at nimnet.asn.au
Sun Oct 12 10:34:22 UTC 2014


Re: freebsd-questions Digest, Vol 540, Issue 6, Message: 7
On Fri, 10 Oct 2014 10:41:49 -0500 "William A. Mahaffey III" <wam at hiwaay.net> wrote:
 > On 10/10/14 10:30, Michael Sierchio wrote:
 > > On Fri, Oct 10, 2014 at 8:30 AM, William A. Mahaffey III <wam at hiwaay.net> wrote:
 > >
 > >> .....I had a bunch of shell scripts written to use Linux
 > >> sh, which was in fact bash, which means it had a superset of the arithmetic
 > >> operators that traditional sh had. When I use these scripts under sh under
 > >> FBSD 9.3, they largely work, though there are some minor differences (empty
 > >> strings evaluate to zero (0) under bash, error under sh). The man page for
 > >> sh doesn't reflect some of these compatibilities/incompatibilities,
 > > Nor should it. The Bourne Shell is the Bourne Shell, is adequately
 > > documented by the man page, and warnings about incompatibility are the
 > > responsibility of those who foist off bash as sh.
 > >
 > > You're blaming your own bad habit on others. :-)

 > Well !!!! The sh man page is mute on the fact that an empty string is an 
 > error in arithmetic or logical evaluations, which is an omission 
 > irrespective of what bash does :-).

No, sh(1) sayeth regarding $((...)), removing some whitespace:

     The allowed expressions are a subset of C expressions, summarized below.
           Values     All values are of type intmax_t.
           Constants  Decimal, octal (starting with 0) and hexadecimal (start-
                      ing with 0x) integer constants.
           Variables  Shell variables can be read and written and contain
                      integer constants.
           Unary operators
                      ! ~ + -
           Binary operators
                      * / % + - << >> < <= > >= == != & ^ | && ||
           Assignment operators
                      = += -= *= /= %= <<= >>= &= ^= |=
           Conditional operator
                      ? :
     The result of the expression is substituted in decimal.

Now unary operations require a single value; binary operations, two.  

Undefined variables resolve to "", so expressions become invalid where 
one (for unary) or for binary, either|both arguments are null|undefined.

 > I presume that converting Linux users to FBSD users is an agenda item 
 > here (maybe my error),

Not really; sh(1) is the standard, and we're not chasing 'converts' like 
it were some kind of religion - clearly there's more than enough of that 
in the world!  bash claims to be sh compatible and I've always taken it 
at its word by using nothing but sh syntax in bash scripts on Debian :)

 > thus suitably complete man pages should be an important goal, I would 
 > think. I didn't think converting from Linux to FBSD was/is a bad 
 > habit ;-) ....

Noone would argue that completeness is a goal, and while mentioning that 
sh DOES NOT convert undefined (or null) variables to 0 - which is pretty 
weird - is therefore unnecessary, this brings up one thing that should 
be mentioned, and I'll do so below in response to RW's post.

bash(1) specifically states: "A shell variable that is null or unset 
evaluates to 0 when referenced by name without using the parameter 
expansion syntax." and later "A null value evaluates to 0", but it goes 
without saying - because it's not said - that in sh(1) that is not so.

I'll also point out that bash(1) is 5463 lines (on 9.3) and sh(1) is 
1631 lines, or just less than 30% the size.  From bash(1):

BUGS
       It's too big and too slow.

       There are some subtle differences between bash and traditional versions
       of sh, mostly because of the POSIX specification.

'subtle differences' hardly cuts it, and to reverse the onus, bash(1) 
doesn't point out ITS incompatible foibles - like this very 0 thing.

In Message: 12
On Fri, 10 Oct 2014 18:38:14 +0100 RW <rwmaillists at googlemail.com> wrote::
 > On Fri, 10 Oct 2014 10:30:19 -0500
 > William A. Mahaffey III wrote:
[..]
 > > I have a FBSD 9.3 desktop that supplanted a Linux FC14 desktop used
 > > for web access, some light development, & other day-to-day tasks
 > > (i.e. my daily driver, so to speak). I had a bunch of shell scripts
 > > written to use Linux sh, which was in fact bash, which means it had a
 > > superset of the arithmetic operators that traditional sh had. When I
 > > use these scripts under sh under FBSD 9.3, they largely work, though
 > > there are some minor differences (empty strings evaluate to zero (0)
 > > under bash, error under sh). 
 > 
 > Can you give an example?
 > 
 > $ sh
 > $ echo $((1+c))
 > 1

Now this is strange.  bash(1) points out that "Within an expression, 
shell variables may also be referenced by name without using the 
parameter expansion syntax".  That this works in sh(1) does appear to be 
undocumented, though I've quite often seen it used, and wondered.  And 
what's worse, when you DO use the full form ($variable), it fails!

% sh -c 'echo $(( 1+c ))'
1
% sh -c 'echo $(( 1-c ))'
1
% sh -c 'echo $(( 1/c ))'
arithmetic expression: division by zero: " 1/c "
% sh -c 'echo $(( 1|c ))'
1
% sh -c 'echo $(( 1&c ))'
0

But:

% sh -c 'echo $(( 1 & $c ))'
arithmetic expression: expecting primary: " 1 &  "
% sh -c 'echo $(( 1 ^ $c ))'
arithmetic expression: expecting primary: " 1 ^  "
% sh -c 'echo $(( 1 + $c ))'
arithmetic expression: expecting primary: " 1 +  "
% sh -c 'echo $(( 1 + c ))'
1

As far as I can tell, neither of these behaviours are documented, though 
I may have missed something; sh(1) is generally both terse and precise.

And further [just _sometimes_ quoting from digests can be useful :-]

 > Straight out of the script which is failing. Under linux, if I call the 
 > script w/ no '-s #' option, the variable 'slept' is not set, & linux (or 
 > more accurately linux bash) evaluates that to the value oif zero (0).
 > 
 > 
 > [wam at kabini1, ~, 7:07:22pm] 386 % sh
 > $ if [ 0 -lt $(($slept)) ] ; then echo -n "$cmd: sleeping $slept secs 
 > ...." ; sleep $(($slept)) ; echo " done." ; fi
 > arithmetic expression: expecting primary: ""
 > [wam at kabini1, ~, 7:07:45pm] 387 %

So just precede that and similar instances with:

  [ -z "$slept" ] && slept=0	# or [ ! "$slept" ] ...

Either way, quotes are required around potentially undef/null variables 
in test aka [, and never hurt.

And though it's only what I read in sh(1), you should be able to use the
${parameter:=word} form to assign a default value if unset or null:

	if [ 0 -lt ${slept:=0} ; ...	# no need for the $((..)) form

cheers, Ian


More information about the freebsd-questions mailing list