"${1-}" vs "$1"
Asked Answered
U

4

15

The code for git bash completion, specifically the function __gitcomp, uses parameter expansions like "${1-}". This appears to be similar to "$1". What is the difference?

Also: where is this documented in the bash manual?

Underfeed answered 17/4, 2011 at 11:43 Comment(1)
Not sure which answer to accept here: the only one that fully answers my question is my own, and it does so mostly by just referencing the other answers. If one (or both) of those folks would like to edit their answer to make it more comprehensive, I'll accept one of them.Underfeed
A
19

First, recall that ${foo-bar} expands to the value of foo, like $foo or ${foo}, except that if foo is unset, ${foo-bar} expands to bar ($foo expands to the empty string if foo is unset). There is a more often-used variant of this syntax, ${foo:-bar}, which expands to bar if foo is unset or empty. (This is explained in the manual if you look closely enough: search for :-, and note the sentence “Omitting the colon results in a test only for a parameter that is unset.” above.)

For a positional parameter $1, ${1-bar} expands to bar if $1 is unset, that is, if the number of positional parameters is less than 1. Unless the positional parameters have been changed with set or shift, this means that the current function, or if not applicable the current script, has no parameter.

Now when bar is empty, ${1-} looks like a useless complication: the expansion is that of $1, except that when $1 is unset, the expansion is empty, which it would be anyway. The point of using ${1-} is that under set -u (a.k.a. set -o nounset), a plain $1 would result in an error if the parameter was unset, whereas ${1-} always successfully expands to the empty string if $1 is unset.

Alber answered 17/4, 2011 at 15:58 Comment(0)
L
3
echo "${foo-default}"

Prints $foo, if foo is defined, and 'default', if foo is undefined. So I conclude

"${1-}"

is empty, if the first argument to the script is not defined.

Lagos answered 17/4, 2011 at 11:50 Comment(2)
The documentation under parameter expansion says ${parameter:-word} does this, and doesn't mention the form without the colon, ${parameter-word}. But it appears both work just fine. I'd be curious to see documentation/explanation as well!Jimmy
@Jefromi: it's in case set -u was in effect.Shuttlecock
L
2

The Bash reference manual §3.5.3 Shell Parameter Expansion says:

When not performing substring expansion, using the form described below, Bash tests for a parameter that is unset or null. Omitting the colon results in a test only for a parameter that is unset. Put another way, if the colon is included, the operator tests for both parameter’s existence and that its value is not null; if the colon is omitted, the operator tests only for existence.

${parameter:-word}

If parameter is unset or null, the expansion of word is substituted. Otherwise, the value of parameter is substituted.

(Emphasis added.)

If the ${1-} appears inside double quotes in the shell script, it is really a not particularly useful way of writing "$1". If $1 is not defined, then both "${1-}" and "$1" expand to an empty argument; if $1 is defined but empty, they also both expand to an empty argument; and otherwise, even if $1 contains spaces, it appears as one argument to the called program.

If the ${1-} appears outside double quotes, then it still isn't useful: if $1 is undefined or empty, then the called program sees no argument (with either notation); if $1 is defined, then the called program sees one or more arguments based on the (split up) value of $1, or it sees no argument if $1 consists only of white space.

The notation really comes into its own when there is a value of some sort after the dash. For example:

localvar=${ENVVAR1:-${ENVVAR2:-/opt/software}}

This says 'if $ENVVAR1 is set to a non-empty value (including all blanks), use it; otherwise, look at $ENVVAR2 and if it is set to a non-empty value, use it; otherwise, use the value /opt/software'.

Latisha answered 17/4, 2011 at 15:45 Comment(0)
U
0

original answer

Managed to actually pay attention to the detail of the intro to EXPANSION -> Parameter expansion in the manual. The final sentence before the list of expansion cases (:-, :+, etc.) explains that "Omitting the colon results in a test only for a parameter that is unset." If the : is used, those tests will be for a parameter that is either unset or null.

So:

$ unset malkovich
$ echo "${malkovich:-John} ${malkovich-Malkovich}"
John Malkovich
$ malkovich=
$ echo "${malkovich:-John} ${malkovich-Malkovich}"
John
$ echo "$malkovich"

$

Moral of the story: don't just scan the manual, RTFM.

appendix

At first glance, this answer may seem irrelevant; confused readers are advised to consider the case of echo "${malkovich-}" and then the original form as used in echo "${1-}". This is an answer to my question in that it explains to myself, as well as others familiar with the :- form of default parameter expansion, that the the colon can be omitted.

As Gilles points out, "${1-}" is effectively the same as "$1" unless set -u is in effect: in that case, the provision of a default value is necessary to avoid an error in cases where the variable is unset. See Johnathan Lefler's answer for a thorough explanation of the context and syntax.

Underfeed answered 17/4, 2011 at 15:16 Comment(2)
this 'answer' should probably be added to the question, though your observation about the commentary in the Bash manual is correct (your answer arrived while I was typing in mine).Latisha
@Jonathan: Well, it answers the question for me at least: I was already familiar with the use of :- in parameter expansion but didn't realize that the colon could be omitted. Although it doesn't actually address the logic of using this form in the git completion function, that wasn't really my question. In any case, this answer as written is liable to be confusing to people who are not familiar with the :- form; I guess I'll edit it.Underfeed

© 2022 - 2024 — McMap. All rights reserved.