By TheDudeAbides


2019-02-05 17:18:18 8 Comments

I work in a relatively heterogeneous environment where I may be running different versions of Bash on different HPC nodes, VMs, or my personal workstation.

I like the default behavior of Bash <= 4.1 that expands cd $SOMEPATH into cd /the/actual/path when pressing the Tab key. This is just one example, though; complete_fullquote (though I don't know exactly what it does) may have also changed default behavior at v4.2.

However, the shopt option, direxpand which switches on this non-default behavior in Bash 4.2 and above is not recognized by earlier versions, and results in an error message being printed to the console every time I log in to a node with an older Bash:

-bash: shopt: direxpand: invalid shell option name

What I'd like to do is wrap a conditional around shop -s direxpand to enable that option on Bash > 4.1 in a robust way (i.e., not just redirecting the error output to /dev/null).

3 comments

@Luciano Andress Martini 2019-02-05 17:23:31

Check if direxpand is present in the output of shopt and enable it if it is:

shopt | grep -q '^direxpand\b' && shopt -s direxpand

@Gilles 2019-02-05 17:45:30

Better make that grep -q '^direxpand\b' in case some future version or fork of bash has an option that contains this as a substring and removes direxpand. Unlikely in this specific case, but it doesn't cost much to be robust.

@TheDudeAbides 2019-02-05 18:05:55

Thanks Luciano. I'd intended to answer my own question, but I"ll accept your answer after my edits go through peer review. Maybe you can approve them yourself?

@David Foerster 2019-02-05 21:47:53

Bash allows to query specific shell options, so one can use [ -z "$(shopt -po direxpand 2>&-)" ] || shopt -s direxpand. No more regex issues! :-)

@Rich 2019-02-06 16:47:28

@DavidFoerster I'd turn the logic around: [ -n "blah" ] && shopt blah The way you phrase it, you're saying "if direxpand is unsupported, then don't do this thing".

@David Foerster 2019-02-07 18:07:58

@Rich: Most of my shell scripts include set -e at the top, so I tend to use short-cut logic this way around.

@Luciano Andress Martini 2019-02-07 18:39:41

I fear more the people that think shell based solutions are not safe, than the problems caused by using them. I don't see any reason for you to fear 2>/dev/null or think it is not a robust solution, in fact it is the more robust solution as it does not depends on any external tools like grep, if your special devices are not working, you are already burning in fire.

@TheDudeAbides 2019-02-05 17:18:18

I found most of a solution in this SE thread.

When you know for sure that a specific shopt option is available at a certain major/minor release of Bash, here's one way to enable it conditionally:

if [[ ${BASH_VERSINFO[0]} -eq 4 && ${BASH_VERSINFO[1]} -gt 1 ]]; then
    shopt -s direxpand
fi

Obviously, the conditional expression gets a little more complicated when Bash reaches version 5. Note the braces around BASH_VERINFO[index], which are required, and the use of -eq and -gt, which do integer rather than (locale-dependent) lexical comparisons.

The $BASH_VERSINFO array contains all the information you'd see in the output of bash --version:

bash --version | head -1
# result:
# GNU bash, version 4.3.48(1)-release (x86_64-pc-linux-gnu)

declare -p BASH_VERSINFO
# result:
# declare -ar BASH_VERSINFO='([0]="4" [1]="3" [2]="48" [3]="1" [4]="release" [5]="x86_64-pc-linux-gnu")'

When it isn't clear from the documentation for shopt at which Bash version(s) became supported or changed their behavior, the method proposed by Luciano is probably fine:

# note the '-q' so that the matched pattern isn't actually printed
shopt | grep -q direxpand && shopt -s direxpand

References: 1, 2, 3
Related reading: Set and Shopt - Why Two?

@Gilles 2019-02-05 17:44:07

I don't see what's wrong with redirecting errors to /dev/null. If you want your code to be robust to set -e, use the common idiom … || true:

shopt -s direxpand 2>/dev/null || true

If you want to run some fallback code if the option does not exist, use the return status of shopt:

if shopt -s direxpand 2>/dev/null; then
  … # the direxpand option exists
else
  … # the direxpand option does not exist
fi

But if you really dislike redirecting the error away, you can use the completion mechanism to perform introspection. This assumes that you don't have antiquated machines with bash ≤ 2.03 that didn't have programmable completion.

shopt_exists () {
  compgen -A shopt -X \!"$1" "$1" >/dev/null
}
if shopt_exists direxpand; then
  shopt -s direxpand
fi

This method avoids forking, which is slow on some environments such as Cygwin. So does the straightforward 2>/dev/null, I don't think you can beat that on performance.

@TheDudeAbides 2019-02-05 18:09:31

That is not where my brain would've gone, but I like the compgen proposal. That's varsity level stuff right there! Avoiding redirection to /dev/null is just a personal preference. I like to ask for permission instead of forgiveness, if that makes sense? :)

@TheDudeAbides 2019-02-05 18:21:41

+1 for a totally unanticipated schooling in Bash programmable completion, which forced me to go to the manual to decipher what compgen -A shopt -X ... even meant.

@Gilles 2019-02-05 18:29:11

@TheDudeAbides I read about using compgen this way on Unix & Linux, I don't know who first proposed it. (I stopped using bash as my main shell before it had programmable completion.) In programming it's usually a bad idea to ask for permission because there's a risk that the permission check will not match what you're actually doing, either because of a coding error (where you aren't quite checking what you think you're checking) or because what you checked changed before you used it.

Related Questions

Sponsored Content

2 Answered Questions

[SOLVED] How can I prevent yum update from creating config files?

1 Answered Questions

5 Answered Questions

[SOLVED] How can I list bash'es options for the current shell?

  • 2015-06-17 07:04:38
  • the_velour_fog
  • 8675 View
  • 21 Score
  • 5 Answer
  • Tags:   bash shopt

3 Answered Questions

[SOLVED] How to prevent script from deleting itself

  • 2016-05-02 13:43:43
  • jthomp
  • 392 View
  • 3 Score
  • 3 Answer
  • Tags:   bash shell-script

2 Answered Questions

[SOLVED] Run script on non-full bash-login invoked as "sh"

  • 2015-04-06 21:31:12
  • Siguza
  • 1016 View
  • 0 Score
  • 2 Answer
  • Tags:   bash shell login su

1 Answered Questions

[SOLVED] How to prevent bash from transforming arguments?

2 Answered Questions

[SOLVED] How can I send multiple options to one variable

  • 2015-08-27 00:07:12
  • Xentrees
  • 284 View
  • 2 Score
  • 2 Answer
  • Tags:   bash shell-script

2 Answered Questions

[SOLVED] How to load .bashrc from "bash -c"

  • 2013-11-18 02:21:56
  • lairtech
  • 2454 View
  • 3 Score
  • 2 Answer
  • Tags:   bash

3 Answered Questions

[SOLVED] How can I make my .bashrc call to shopt -s autocd depend on the bash version?

  • 2014-04-12 18:16:06
  • Michael Durrant
  • 611 View
  • 6 Score
  • 3 Answer
  • Tags:   bash bashrc shopt

2 Answered Questions

[SOLVED] How do you get descriptions of the available `shopt` options?

  • 2013-12-28 05:56:53
  • bnjmn
  • 3190 View
  • 7 Score
  • 2 Answer
  • Tags:   bash shopt

Sponsored Content