Error message garbage when parameter expansion used inside (()) and variable unset

2018-04-02 Thread PRussell
Section 6.5 Shell Arithmetic says,

"Within an expression, shell variables may also be referenced by name without
using the parameter expansion syntax. A shell variable that is null or unset
evaluates to 0 when referenced by name without using the parameter expansion
syntax." - http://www.gnu.org/software/bash/manual/bash.html#Shell-Arithmetic

The above tells us what happens to an unset variable if not using parameter
expansion.

But if a shell variable uses parameter expansion and is null or unset, what
does it evaluate to inside (()) syntax?  

It evaluates to 0 inside [[]], but gives an error inside (()). The error 
message is different between bash 4.3.11 and 4.4.19. The later contains 
garbage. 

See test 3 and 4 below.


### Test 1:
# No parameter expansion. var2 not set. Should evaluate to 0 thus when compared
# to 0, 1A and 1B should echo yes. They do.

echo;echo 1A
  ( set -x;var=0;var1=var; [[ var1 -eq var2 ]] && echo yes || echo no )
echo 1B
  ( set -x;var=0;var1=var; (( var1 == var2 )) && echo yes || echo no )

### Test 2:
# No parameter expansion. var2 not set. Should evaluate to 0 thus when compared
# to 5, 2A and 2B should echo no. They do

   
echo;echo 2A
   
  ( set -x;var=5;var1=var; [[ var1 -eq var2 ]] && echo yes || echo no ) 
   
echo 2B 
   
  ( set -x;var=5;var1=var; (( var1 == var2 )) && echo yes || echo no )  
   

   
### Test 3: 
 
# Parameter expansion. var2 not set.
   

   
echo;echo 3A
   
  ( set -x;var=0;var1=var; [[ var1 -eq $var2 ]] && echo yes || echo no )
   
echo 3B 
   
  ( set -x;var=0;var1=var; (( var1 == $var2 )) && echo yes || echo no ) 
   

### Test 4:
echo;echo 4A
   
  ( set -x;var=5;var1=var; [[ var1 -eq $var2 ]] && echo yes || echo no )
   
echo 4B 
   
  ( set -x;var=5;var1=var; (( var1 == $var2 )) && echo yes || echo no ) 
   

   

   
It appears that 3A and 4A evaluate to 0 because of the arithmetic context. 
3A echo's yes; 4A echo's no.

The problem is what is happening with 3B and 4B.  I tested on bash 4.3.11 and
bash 4.4.19 and got a slightly different error message.

Bash version 4.3.11:

./tt: line 18: var1: var1 ==  : syntax error: operand expected (error token is 
"==  ")

Bash version 4.4.19 ( was garabage):

./tt: line 18: : var1 ==  : syntax error: operand expected (error token is 
"==  ")

Peggy Russell



Re: Error message garbage when parameter expansion used inside (()) and variable unset

2018-04-03 Thread PRussell

Chet, is the output on opensuse running bash 4.4.19, correct?

The specific output:

./t.sh: line 9: ���#V: var1 ==  : syntax error: operand expected (error 
token is "==  ")


archlinux has the same version of bash and I got the same results as on 
opensuse.


Below are the details of running ./t.sh &>t.log on multiple versions of 
bash.


When I run this script (t.sh),

..
#!/bin/bash

# system info
lsb_release -d
printf -- 'Bash Version: %s\n\n' "${BASH_VERSION}"

# test example
echo 3B
  ( set -x; var=0;var1=var; (( var1 == $var2 )) && echo yes || echo no )
..

I get,

..
Description:  openSUSE Tumbleweed
Bash Version: 4.4.19(1)-release

3B
+ t.sh line 9 : var=0
+ t.sh line 9 : var1=var
+ t.sh line 9 : ((  var1 ==   ))
./t.sh: line 9: ���#V: var1 ==  : syntax error: operand expected (error 
token is "==  ")

+ t.sh line 9 : echo no
no
..

..
Description:Linux Mint 17.3 Rosa
Bash Version: 4.3.11(1)-release

3B
+ t.sh line 9 : var=0
+ t.sh line 9 : var1=var
+ t.sh line 9 : ((  var1 ==   ))
./t.sh: line 9:  var1 ==  : var1 ==  : syntax error: operand expected 
(error token is "==  ")

+ t.sh line 9 : echo no
no
..

If I remove the "set -x" I get,

..
Description:  openSUSE Tumbleweed
Bash Version: 4.4.19(1)-release

3B
./t.sh: line 9: ((: var1 ==  : syntax error: operand expected (error 
token is "==  ")

no
..

..
Description:Linux Mint 17.3 Rosa
Bash Version: 4.3.11(1)-release

3B
./t.sh: line 9: ((: var1 ==  : syntax error: operand expected (error 
token is "==  ")

no
..

--
Peggy Russell

On 04/03/2018 08:49 AM, Chet Ramey wrote:

On 4/2/18 5:16 PM, PRussell wrote:

Section 6.5 Shell Arithmetic says,

"Within an expression, shell variables may also be referenced by name without
using the parameter expansion syntax. A shell variable that is null or unset
evaluates to 0 when referenced by name without using the parameter expansion
syntax." - http://www.gnu.org/software/bash/manual/bash.html#Shell-Arithmetic

The above tells us what happens to an unset variable if not using parameter
expansion.

But if a shell variable uses parameter expansion and is null or unset, what
does it evaluate to inside (()) syntax? 


Since ((...)) is equivalent to let "...", as noted in the description of
`((' in the man page and info doc, it expands to the same thing that it
would when you perform a double-quoted word expansion. If you expand a
shell variable that's null or unset, you get the usual: it disappears.



The problem is what is happening with 3B and 4B.  I tested on bash 4.3.11 and
bash 4.4.19 and got a slightly different error message.

Bash version 4.3.11:

./tt: line 18: var1: var1 ==  : syntax error: operand expected (error token is "==  
")

Bash version 4.4.19 ( was garabage):

./tt: line 18: : var1 ==  : syntax error: operand expected (error token is "==  
")


I don't get this. I get `((' as the command name in the error message for
both bash-4.3.46 and bash-4.4.19:

./x18: line 1: ((: var1 ==  : syntax error: operand expected (error token
is "==  ")







Re: Error message garbage when parameter expansion used inside (()) and variable unset

2018-04-03 Thread PRussell
Hi,

The error seems to be localized to the expansion of PS4 when "set -x" is active.

Please see sample script below.

I am aware of the unusual parameter expansion for FUNCNAME.  There might be a 
local historical reason. :-)

It does not happen outside of the PS4 expansion. It also behaves differently on 
4.3 vs 4.4. 

On 4.4, valgrind shows "Invalid Reads".
On 4.3, valgrind shows no errors.

I included below a small part of valgrind's output. If you need more let me 
know.

Sample script:
.
#!/bin/bash

function main() {
  # Shows different successful parameter expansions of FUNCNAME
  echo -n 'declare -p FUNCNAME:'
  declare -p FUNCNAME
  # parentheses surrounding expansion
  echo '(${FUNCNAME:+${FUNCNAME[0]##*/}})':"(${FUNCNAME:+${FUNCNAME[0]##*/}})"
  # parentheses inside expansion
  echo '${FUNCNAME:+(${FUNCNAME[0]##*/})}':"${FUNCNAME:+(${FUNCNAME[0]##*/})}"

  echo

  # PS4: Use FUNCNAME in PS4 - parentheses surrounding expansion *No ERROR*
  declare -x PS4='+ ${BASH_SOURCE[0]##*/} line ${LINENO} 
(${FUNCNAME:+${FUNCNAME[0]}}):'
  declare -p PS4
  echo "PS4=${PS4}"
  set -x; var=0;var1=var; (( var1 == $var2 )) && echo yes || echo no
  echo

  # PS4: Use FUNCNAME in PS4 - parentheses inside expansion **ERROR HERE**
  declare -x PS4='+ ${BASH_SOURCE[0]##*/} line ${LINENO} 
${FUNCNAME:+(${FUNCNAME[0]})}:'
  declare -p PS4
  echo "PS4=${PS4}"
  set -x; var=0;var1=var; (( var1 == $var2 )) && echo yes || echo no
}

main
.

Partial valgrind output:
.
==759== Memcheck, a memory error detector
==759== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==759== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==759== Command: ./x19
==759==
declare -p FUNCNAME:declare -a FUNCNAME=([0]="main" [1]="main")
${FUNCNAME:+(${FUNCNAME[0]##*/})}:(main)
(${FUNCNAME:+${FUNCNAME[0]##*/}}):(main)
${FUNCNAME[0]##*/}:(main)
${FUNCNAME[0]}:(main)
${FUNCNAME}:(main)

declare -x PS4="+ \${BASH_SOURCE[0]##*/} line \${LINENO} 
(\${FUNCNAME:+\${FUNCNAME[0]}}):"
PS4=+ ${BASH_SOURCE[0]##*/} line ${LINENO} (${FUNCNAME:+${FUNCNAME[0]}}):
==759== Invalid read of size 16
==759==at 0x533D488: __wcsnlen_sse4_1 (in /usr/lib/libc-2.26.so)
==759==by 0x532D5C2: wcsrtombs (in /usr/lib/libc-2.26.so)
==759==by 0x128D4C: ??? (in /usr/bin/bash)
==759==by 0x160243: ??? (in /usr/bin/bash)
==759==by 0x160B96: ??? (in /usr/bin/bash)
==759==by 0x161D0D: ??? (in /usr/bin/bash)
==759==by 0x1637FB: expand_prompt_string (in /usr/bin/bash)
==759==by 0x12D530: decode_prompt_string (in /usr/bin/bash)
==759==by 0x13BD92: indirection_level_string (in /usr/bin/bash)
==759==by 0x13C078: xtrace_print_assignment (in /usr/bin/bash)
==759==by 0x15CD12: ??? (in /usr/bin/bash)
==759==by 0x165835: ??? (in /usr/bin/bash)
==759==  Address 0x5935610 is 0 bytes after a block of size 16 alloc'd
==759==at 0x4C2CEDF: malloc (vg_replace_malloc.c:299)
==759==by 0x532C5EF: wcsdup (in /usr/lib/libc-2.26.so)
==759==by 0x128BB9: ??? (in /usr/bin/bash)
==759==by 0x160243: ??? (in /usr/bin/bash)
==759==by 0x160B96: ??? (in /usr/bin/bash)
==759==by 0x161D0D: ??? (in /usr/bin/bash)
==759==by 0x1637FB: expand_prompt_string (in /usr/bin/bash)
==759==by 0x12D530: decode_prompt_string (in /usr/bin/bash)
==759==by 0x13BD92: indirection_level_string (in /usr/bin/bash)
==759==by 0x13C078: xtrace_print_assignment (in /usr/bin/bash)
==759==by 0x15CD12: ??? (in /usr/bin/bash)
==759==by 0x165835: ??? (in /usr/bin/bash)
==759==
==759== Invalid read of size 16
==759==at 0x533D48D: __wcsnlen_sse4_1 (in /usr/lib/libc-2.26.so)
==759==by 0x532D5C2: wcsrtombs (in /usr/lib/libc-2.26.so)
.

--

Peggy Russell

On 04/03/2018 01:15 PM, Chet Ramey wrote:
> I don't see the same type of memory corruption.  I get:
> 
> chet-mail(1)$ lsb_release -d
> Description:  Red Hat Enterprise Linux Server release 6.9 (Santiago)
> chet-mail(1)$ cat ./x18
> ( set -x;var=0;var1=var; (( var1 == $var2 )) && echo yes || echo no )
> chet-mail(1)$ ./bash -c 'echo $BASH_VERSION'
> 4.4.19(4)-release
> chet-mail(1)$ ./bash ./x18
> + var=0
> + var1=var
> + ((  var1 ==   ))
> ./x18: line 1: ((: var1 ==  : syntax error: operand expected (error token
> is "==  ")
> + echo no
> no
> 
> But otherwise the results are correct.
> 
> If you'd like, take a look at running your version under valgrind or a
> similar tool to see if bash is touching freed memory. (I don't happen to
> see that running on RHEL, but your results may vary with a distribution-
> compiled version.)