4

I've got a shell script (example) as follows (simplified):

#!/bin/sh
./somecommand -s ${DOMAIN_SUFFIX:=.example.com}

If I run it as ./example, it correctly runs ./somecommand -s .example.com.

If I run it as DOMAIN_SUFFIX=.stackexchange.com ./example, it correctly runs ./somecommand -s .stackexchange.com.

Question: how do I set DOMAIN_SUFFIX such that it gets passed (inside the script) as an explicit empty string?

That is: I need it to run ./somecommand -s ''.

I've tried DOMAIN_SUFFIX='' ./example, but that runs ./somecommand -s, which fails.

4 Answers4

1

Use:

"${DOMAIN_SUFFIX=.example.com}"

${parameter=default}, ${parameter:=default}

If parameter not set, set it to default.

Both forms nearly equivalent. The : makes a difference only when $parameter has been declared and is null.

http://www.tldp.org/LDP/abs/html/parameter-substitution.html

Note that you also need quotes "" to prevent empty value from being discarded, after interpolation (just noted your code was missing them, thanks @Gilles !).

Also note that = (or :=) will also assign the default value to DOMAIN_SUFFIX (if not set), so if you only need to get the value, you may want to use - instead.

zeppelin
  • 3,822
  • 10
  • 21
1

Note that after all expansions have been carried out by the shell, and end up in an empty field, then it gets dropped unless it was quoted. Since you did not quote the variable , if it resolves to an empty '' then it shall be dropped from the 'somecommands' arguments list. Not only that if DOMAIN_SUFFIX happened to have whitespace or widlcards then you potentially surpise the 'somecommand'.

Also the ${DOMAIN_SUFFIX:=.example.com} is a typo based on the results that you show for: DOMAIN_SUFFIX='' ./example as an empty DOMAIN_SUFFIX would get substituted for it's default value and which then gets placed on the somecommand's arguments list. Hence it never sees an 'empty'. ${DOMAIN=.example.com} will explain the results that you are getting.

0

I think this is the part of the bash manual you are looking for (POSIX has a similar wording of the same effect):

When not performing substring expansion, using the forms documented below (e.g., :-), bash tests for a parameter that is unset or null. Omitting the colon results in a test only for a parameter that is unset.

The difference between an unset variable and a variable with a null value is that an unset variable doesn't even exist.

So,

#!/bin/bash

bash -c 'echo "$#"; printf "%s\n" "$@"' -- "${DOMAIN_SUFFIX=.example.com}"

This will set DOMAIN_SUFFIX to .example.com if DOMAIN_SUFFIX is unset, but not if it is null (an empty string).

The command used here will simply spawn a new bash process that will print the number of command line argument passed in, followed by all positional parameters, one on each line:

bash-4.4$ bash script
1
.example.com
bash-4.4$ DOMAIN_SUFFIX=.hello.world bash script
1
.hello.world
bash-4.4$ DOMAIN_SUFFIX= bash script
1

bash-4.4$

In the last example, DOMAIN_SUFFIX= is identical to DOMAIN_SUFFIX=''.

Kusalananda
  • 333,661
0

TL,DR: Why does my shell script choke on whitespace or other special characters?. Oops, that's even longer… Well, real TL,DR: always put double quotes around variable and command substitutions. Plus see the end of this answer for a second issue with your script.

It's impossible with your script to make a variable expansion expand to an empty string. An unquoted variable expansion undergoes the “expand+split+glob” process:

  1. Take the value of the variable, which is a string.
  2. Split the string at whitespace (more generally, at characters that are present in the value of the IFS variable). This results in a list of strings (possibly empty if the original string contained nothing but whitespace).
  3. For each element of the list, if it contains at least one wildcard character *?\[ and the element is a pattern that matches at least one file, then the element is replaced by the list of matches.

It's possible for $DOMAIN_SUFFIX to expand to an empty list of strings, but not to a list containing an empty string. The wildcard matching step cannot produce empty elements since a file name is never the empty string. The splitting step cannot produce empty elements with the default value of IFS, since the separator is a sequence of whitespace and it takes at least one non-whitespace character to interrupt a sequence of whitespace. The splitting step can produce empty elements if IFS is changed to include non-whitespace characters, but that would require changing the script to modify IFS and there's no reason to do so.

You need to change the script and make it use a straightforward variable expansion instead of using the expand+split+glob operator: put double quotes around the variable expansion.

Additionally, ${DOMAIN_SUFFIX:=.example.com} expands to .example.com if the value of DOMAIN_SUFFIX is empty. So you'll never get an empty string for that. To preserve the empty value and only use .example.com if DOMAIN_SUFFIX is unset, change the := to =.

#!/bin/sh
./somecommand -s "${DOMAIN_SUFFIX=.example.com}"