Skip to main content
Fixed up solutions to avoid my mistaken understanding that double-quoted variables would bypass the double expansion
Source Link
Chris Davies
  • 128.4k
  • 16
  • 179
  • 324

What's happened here is (for you) an unexpected evaluation. For reference here's your original expression

[[ $option -ge 1 && option -le ${#array[@]} ]]

First, $option is evaluated. In this case it's d. This is then substituted into the [[ ... ]] expression and youto get this:

if [[ d -ge 1 && option -le 12 ]]; then]]

This is evaluated. BUT because [[ … ]] uses bash's expression evaluation the d and option are assumed to betreated as names of variables so there's another level of expansion (being aware that implicitly d=0):

if [[ 0 -gt 1 && d -le 12 ]]; then]]

The first part fails, so the second (erroneous) expression is skipped.

Solution? Double quote your variables when you use them.

if [[ "$option" -ge 1 && "$option" -le "${#array[@]}" ]]; then

But that won't catch the "non-numeric" error when $option isn't numeric. ForThere are a couple of solutions here that you need something like thiscan consider:

if [[ "$option" =~ ^(0|([1-9][0-9]*))$ ]] && [[ "$option" -ge 1 && "$option" -le "${#array[@]}" ]]; then
  1. Instead of using [[ … ]] use the older but acceptable [ … ]. This doesn't perform this double expansion. But that won't catch the "non-numeric" error when $option isn't numeric, so you'll still get an error

    [ "$option" -ge 1 ] && [ "$option" -le ${#array[@]} ]
    
  2. Protect the expression comparison by validating that the characters in the string are only numeric.

    [[ "$option" =~ ^(0|([1-9][0-9]*))$ && "$option" -ge 1 && "$option" -le "${#array[@]}" ]]
    

What's happened here is (for you) an unexpected evaluation.

First $option is evaluated. In this case it's d. This is then substituted into the [[ ... ]] expression and you get this:

if [[ d -ge 1 && option -le 12 ]]; then

This is evaluated. BUT the d and option are assumed to be variables so there's another level of expansion:

if [[ 0 -gt 1 && d -le 12 ]]; then

The first part fails, so the second (erroneous) expression is skipped.

Solution? Double quote your variables when you use them.

if [[ "$option" -ge 1 && "$option" -le "${#array[@]}" ]]; then

But that won't catch the "non-numeric" error when $option isn't numeric. For that you need something like this:

if [[ "$option" =~ ^(0|([1-9][0-9]*))$ ]] && [[ "$option" -ge 1 && "$option" -le "${#array[@]}" ]]; then

What's happened here is (for you) an unexpected evaluation. For reference here's your original expression

[[ $option -ge 1 && option -le ${#array[@]} ]]

First, $option is evaluated. In this case it's d. This is then substituted into the [[ ]] expression to get this:

[[ d -ge 1 && option -le 12 ]]

This is evaluated. BUT because [[ … ]] uses bash's expression evaluation the d and option are treated as names of variables so there's another level of expansion (being aware that implicitly d=0):

[[ 0 -gt 1 && d -le 12 ]]

The first part fails, so the second (erroneous) expression is skipped.

There are a couple of solutions here that you can consider:

  1. Instead of using [[ … ]] use the older but acceptable [ … ]. This doesn't perform this double expansion. But that won't catch the "non-numeric" error when $option isn't numeric, so you'll still get an error

    [ "$option" -ge 1 ] && [ "$option" -le ${#array[@]} ]
    
  2. Protect the expression comparison by validating that the characters in the string are only numeric.

    [[ "$option" =~ ^(0|([1-9][0-9]*))$ && "$option" -ge 1 && "$option" -le "${#array[@]}" ]]
    
added 1 character in body
Source Link
Chris Davies
  • 128.4k
  • 16
  • 179
  • 324

What's happened here is (for you) an unexpected evaluation.

First $option is evaluated. In this case it's d. This is then substituted into the [[ ... ]] expression and you get this:

if [[ d -ge 1 && option -le 12 ]]; then

This is evaluated. BUT the d and option are assumed to be variables so there's another level of expansion:

if [[ 0 -gt 1 && d -le 12 ]]; then

The first part fails, so the second (erroneous) expression is skipped.

Solution? Double quote your variables when you use them.

if [[ "$option" -ge 1 && "$option" -le "${#array[@]}" ]]; then

But that won't catch the "non-numeric" error when $option isn't numeric. For that you need something like this:

if [[ "$option"$option" =~ ^(0|([1-9][0-9]*))$ ]] && [[ "$option" -ge 1 && "$option" -le "${#array[@]}" ]]; then

What's happened here is (for you) an unexpected evaluation.

First $option is evaluated. In this case it's d. This is then substituted into the [[ ... ]] expression and you get this:

if [[ d -ge 1 && option -le 12 ]]; then

This is evaluated. BUT the d and option are assumed to be variables so there's another level of expansion:

if [[ 0 -gt 1 && d -le 12 ]]; then

The first part fails, so the second (erroneous) expression is skipped.

Solution? Double quote your variables when you use them.

if [[ "$option" -ge 1 && "$option" -le "${#array[@]}" ]]; then

But that won't catch the "non-numeric" error when $option isn't numeric. For that you need something like this:

if [[ "$option =~ ^(0|([1-9][0-9]*))$ ]] && [[ "$option" -ge 1 && "$option" -le "${#array[@]}" ]]; then

What's happened here is (for you) an unexpected evaluation.

First $option is evaluated. In this case it's d. This is then substituted into the [[ ... ]] expression and you get this:

if [[ d -ge 1 && option -le 12 ]]; then

This is evaluated. BUT the d and option are assumed to be variables so there's another level of expansion:

if [[ 0 -gt 1 && d -le 12 ]]; then

The first part fails, so the second (erroneous) expression is skipped.

Solution? Double quote your variables when you use them.

if [[ "$option" -ge 1 && "$option" -le "${#array[@]}" ]]; then

But that won't catch the "non-numeric" error when $option isn't numeric. For that you need something like this:

if [[ "$option" =~ ^(0|([1-9][0-9]*))$ ]] && [[ "$option" -ge 1 && "$option" -le "${#array[@]}" ]]; then
Source Link
Chris Davies
  • 128.4k
  • 16
  • 179
  • 324

What's happened here is (for you) an unexpected evaluation.

First $option is evaluated. In this case it's d. This is then substituted into the [[ ... ]] expression and you get this:

if [[ d -ge 1 && option -le 12 ]]; then

This is evaluated. BUT the d and option are assumed to be variables so there's another level of expansion:

if [[ 0 -gt 1 && d -le 12 ]]; then

The first part fails, so the second (erroneous) expression is skipped.

Solution? Double quote your variables when you use them.

if [[ "$option" -ge 1 && "$option" -le "${#array[@]}" ]]; then

But that won't catch the "non-numeric" error when $option isn't numeric. For that you need something like this:

if [[ "$option =~ ^(0|([1-9][0-9]*))$ ]] && [[ "$option" -ge 1 && "$option" -le "${#array[@]}" ]]; then