3

The following Bash (v 5.1.4) script

#!/usr/bin/env bash
echo $BASHOPTS
shopt extglob
x="123"
if [[ "$x" == +([[:digit:]]) ]]; then 
    echo "[[ Ok!" 
fi
case $x in
    +([[:digit:]])) echo "case Ok" ;;
    *) echo case "KO" ;;
esac

outputs

checkwinsize:cmdhist:complete_fullquote:extquote:force_fignore:globasciiranges:hostcomplete:interactive_comments:progcomp:promptvars:sourcepath
extglob         off
[[ Ok!
./extglob_test.sh: line 10: syntax error near unexpected token `('
./extglob_test.sh: line 10: `    +([[:digit:]])) echo "case Ok" ;;'

which raises several questions to me:

  1. shopt extglob says it's off, so case option in line 9 logically fails. But why code in line 5 succeeds then?

  2. The env variable BASHOPTS contains the inherited shopt options, and the BASH manual states that:

    BASHOPTS
      A  colon-separated  list of enabled shell options.  Each word in the list is a valid argument for the -s option to the shopt
      builtin command (see SHELL BUILTIN COMMANDS below).  The options appearing in BASHOPTS are those reported as  on  by  shopt.
      If this variable is in the environment when bash starts up, each shell option in the list will be enabled before reading any
      startup files.  This variable is read-only.
    

    So I'd expect to see extglob enabled in the script. And indeed, issuing an echo $BASHOPTS command at the shell prompt outputs:

    checkwinsize:cmdhist:complete_fullquote:expand_aliases:extglob:extquote:force_fignore:globasciiranges:histappend:interactive_comments:progcomp:promptvars:sourcepath
    

    However, we can see there are some differences, and notably the option extglob which is present here, but missing in the output from the script.

    Of course, if I enable extglob option with shopt -s extglob in line 3 of the script, I get:

    checkwinsize:cmdhist:complete_fullquote:extquote:force_fignore:globasciiranges:hostcomplete:interactive_comments:progcomp:promptvars:sourcepath
    [[ Ok!
    case Ok
    
  3. However, the linter (I use Ale in Vim) still signals a syntax error in line 9

Any ideas?

5
  • 3
    Inside the [[ ]] extglob is enabled by default, well at least according to my man pages, so it does not need the shopt to enable it. To jump directly at the relevant section of the man page try: PAGER="less +/^[[:space:]]*'\[\['" man bash , In the case statement the ( and ` )` is special to the shell so that is an expected error imo. Also I don't think the case statement can handle that pattern for extglob.
    – Jetchisel
    Feb 28 '21 at 18:46
  • "BASHOPTS contains the inherited shopt options [...] So I'd expect to see extglob enabled in the script." -- err, why? If BASHOPTS doesn't contain it at the start of the script, then the script didn't get it from the calling context, right? Also, BASHOPTS doesn't appear to be exported by default, so unless you explicitly did that, it wouldn't get inherited from the parent shell.
    – ilkkachu
    Feb 28 '21 at 19:27
  • 3. does the linter deal with extglob patterns in other contexts? Like ls -l +([[:digit:]]). Like you said, that case does work ok, and also e.g. shellcheck.net parses it correctly.
    – ilkkachu
    Feb 28 '21 at 19:31
  • One should always read carefully the doc ! Thanks all
    – hutou
    Feb 28 '21 at 21:23
  • The error in your linter probably appears because the case condition is normally incorrect (extglob is off by default). If you put it into a function in your Bash script, it will throw a syntax error, unless you turn on extglob before parsing the function. Thus, take care with functions! Compare a script with error to a script that succeeds. It's not intuitive. Mar 1 '21 at 9:43
3

There are 3 questions.

  1. As comments have said, inside [[ extglob is enabled by default.

  2. You don't have an environment variable BASHOPTS. You can see this by running export | grep BASHOPTS and not seeing anything. As you quote "If this variable is in the environment when bash starts up..." you need to run export BASHOPTS in your interactive shell.

  3. Get a better linter!.

1
  • 1
    or declare -p BASHOPTS and look for the missing -x.
    – ilkkachu
    Feb 28 '21 at 19:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.