Search code examples
bashparameter-expansion

Bash param expansion "${var##pat}" behaves differently in script than in shell


I'm trying to use a pretty basic Bash parameter expansion in a script and it's not working; it works fine when I run it in my interactive shell, though. Here's the little test script:

<~> $ cat /tmp/foo
#!/usr/bin/env bash

foo="bar 1.2.3"
echo $foo
echo ${foo##*([^0-9])}
echo ${foo/a/-}

The first param expansion (${foo##*([^0-9])}) doesn't work when I run the script, but the second one (${foo/a/-}) does:

<~> $ /tmp/foo
bar 1.2.3
bar 1.2.3
b-r 1.2.3

If I source it instead (running it in my current shell), it works as expected:

<~> $ . /tmp/foo
bar 1.2.3
1.2.3
b-r 1.2.3

Suspecting a difference in actual shell being run, shell version, or settings, I expanded the script:

<~> $ cat /tmp/foo
#!/usr/bin/env bash

echo "SHELL: $SHELL"
echo "VERSION: $BASH_VERSION"
echo

set -o
echo

foo="bar 1.2.3"
echo $foo
echo ${foo##*([^0-9])}
echo ${foo/a/-}

And now I get this:

<~> $ /tmp/foo
SHELL: /opt/local/bin/bash
VERSION: 5.1.16(1)-release

allexport       off
braceexpand     on
emacs           off
errexit         off
errtrace        off
functrace       off
hashall         on
histexpand      off
history         off
ignoreeof       off
interactive-comments    on
keyword         off
monitor         off
noclobber       off
noexec          off
noglob          off
nolog           off
notify          off
nounset         off
onecmd          off
physical        off
pipefail        off
posix           off
privileged      off
verbose         off
vi              off
xtrace          off

bar 1.2.3
bar 1.2.3
b-r 1.2.3

Nothing looks out of place, and running the two and comparing the output shows nothing of significance:

<~> $ diff <(/tmp/foo) <(. /tmp/foo)
6c6
< emacs             off
---
> emacs             on
11,12c11,12
< histexpand        off
< history           off
---
> histexpand        on
> history           on
16c16
< monitor           off
---
> monitor           on
33c33
< bar 1.2.3
---
> 1.2.3

Any ideas on what I'm missing here (besides a clue, 'natch)? Thanks!

Addendum

Just to paint a complete picture derived from the accepted answer below, adding in shopt to the script output shows these additional differences between the script version and the interactive shell's version:

52c52
< expand_aliases    off
---
> expand_aliases    on
54c54
< extglob           off
---
> extglob           on
61c61
< histappend        off
---
> histappend        on
64c64
< hostcomplete      on
---
> hostcomplete      off
72c72
< login_shell       off
---
> login_shell       on

and indeed there is the missing option (extglob).


Solution

  • You need to add

    shopt -s extglob
    

    to the script to enable *(pattern-list) extended globbing. I suspect you have this enabled in interactive shells in your .bashrc.