2

I'm trying to figure out how a certain line of code from the top of BASH man page tab completion script works:

[[ $OSTYPE == *@(darwin|freebsd|solaris|cygwin|openbsd)* ]] || _userland GNU \
    || return 1

I believe it's a guard; if the BASH special variable $OSTYPE does not contain one of the strings in the (basic regular?) expression contained within parentheses, or if the userland is GNU then it'll discontinue execution of the script. But, I can't understand how the syntax works or what it means and I don't know what the flow of control is.

You can find the definition of _userland here:

# Check if we're running on the given userland
# @param $1 userland to check for
_userland()
{
    local userland=$( uname -s )
    [[ $userland == @(Linux|GNU/*) ]] && userland=GNU
    [[ $userland == $1 ]]
}

How does this function work? Does it return a value?

If you could provide references to relevant documentation or articles, that would be helpful. Thank you.

nc.
  • 7,179
  • 5
  • 28
  • 38

1 Answers1

1

In a chain of c1 || c2 || c3 || ... each command will be tried until one is successful. After a successful command, the remaining commands in the chain will not be executed.

So in this chain of commands:

[[ $OSTYPE == *@(darwin|freebsd|solaris|cygwin|openbsd)* ]] \
|| _userland GNU \
|| return 1

If OSTYPE is one of darwin, freebsd, etc, then the chain stops. You can read more about the pattern used there in man bash, search for Pattern Matching. In this example, the rule that applies is:

@(pattern-list)
    Matches one of the given patterns

The patterns are separated by |. The * around the @(...) means that these patterns can occur anywhere within OSTYPE. Only @(...) would mean exact match on the given patterns, @(...)* would mean starting with any of the given patterns, *@(...) would mean ending with the any of the given patterns.

If OSTYPE didn't match, we try the next item in the chain: _userland GNU. If successful, then the chain stops. Otherwise we try the next item in the chain, which is return 1, so we exit the function.

The _userland function does:

  • local userland=$( uname -s ) : store the output of uname -s command in a local variable called userland
  • [[ $userland == @(Linux|GNU/*) ]] && userland=GNU : if the value is Linux or starts with GNU/, then set userland=GNU
  • [[ $userland == $1 ]] : compare the value to the parameter we received. The exit code of this comparison is the return value of the function. If the value matches, that means success.
janos
  • 120,954
  • 29
  • 226
  • 236
  • Thank you for breaking it down for me! – nc. Mar 05 '16 at 07:26
  • Just for completeness sake: On my Mac OS, when I run `echo $OSTYPE` I get the output `darwin15`. And the **Pattern Matching** section of bash's man page says if the `extglob` `shopt` option is set (it is, in the `bash_completion` script, the same one that defines `_userland`), then `*(pattern-list)` "[m]atches zero or more occurrences of the given patterns" where "a pattern-list is a list of one or more patterns separated by a `|`". This looks like a full solution to me! – nc. Mar 05 '16 at 08:02
  • Hmm, actually I don't quite understand the pattern matching, I don't get the trailing `*` (is that just "Matches any string, including the null string"? does that serve any purpose?) or why they don't use a `+` to precede the open paren. I thought that'd mean that it had to include one of the patterns in the list. – nc. Mar 05 '16 at 08:06
  • 1
    I added more explanation about the pattern, see my updated post. – janos Mar 05 '16 at 08:10