4

AFAICT, neither GNU Bash, nor any other relevant package (e.g. GNU coreutils) that is commonly available in GNU/Linux distros, has a ready-made way to define an environment variable such that the attempt will fail, and indicate failure, if the environment variable already exists. Essentially, a "no-clobber" option for variable assignment, with a non-zero exit status on failure.

Shell parameter expansion (aka "parameter substitution") approaches avoid clobbering, but do not report failure:

unset MY_VARIABLE
: ${MY_VARIABLE:='my_value'}
: ${MY_VARIABLE:='this_will_fail_but_will_nevertheless_have_exit_code_0'}
echo $?
0
echo $MY_VARIABLE
my_value

The best workaround I have come up with is to create a function ("safedef") for that purpose:

die() {
    echo -e >&2 "$@"
    exit 1
}

safedef() {
    if [ -v $1 ]; then
        die 'Name conflict. Variable already defined. Will not overwrite it.'
    else
        # Create and assign environment variable
        export $1="${*:2}"
    fi
}

safedef MY_VARIABLE 'my_value'
safedef MY_OTHER_VARIABLE 'my_other_value'
# Etc, etc...

Questions:

  1. Have I overlooked a ready-made way to achieve this, provided by a free software package that is available in some or all mainstream GNU/Linux distros?
  2. Have I overlooked some kind of bug in safedef that means it could well unexpectedly fail to work as intended? (If so, what is the bug, and what might be a better approach?)
  • why not mark things-not-to-be-changed as readonly ? – thrig May 7 '18 at 15:24
  • Do you only need to consider scalar variables that have been exported to the environment (or where already in the environment when the shell was invoked)? Or any variable scalar or not (array, hash...), exported or not? – Stéphane Chazelas May 7 '18 at 15:26
  • @thrig, the problem with marking existing variables as read-only is twofold. 1. the problem of detecting their existence at the relevant moment (which seems more fraught with potential race conditions and other complications than my safedef approach above); and 2. because if the existing variable was defined by some other process, and that process needs to be able to overwrite it, then my marking it as read-only might cause that other process to fail (and indeed to do so in a subtle and headache-inducing manner). – sampablokuper May 7 '18 at 16:21
  • @StéphaneChazelas, "Do you only need to consider scalar variables that have been exported to the environment (or where already in the environment when the shell was invoked)?" At the moment, yes, that's my interest, and I would be happy to mark as correct an answer providing this, or confirming that safedef achieves it and can't readily be bettered. However, ... "Or any variable scalar or not (array, hash...), exported or not?" ... it would certainly be nice to have a general solution :) – sampablokuper May 7 '18 at 16:55
  • 2
    @sampablokuper "because if the existing variable was defined by some other process, and that process needs to be able to overwrite it." Please note that each process has its own copy of the environment, changing the value of a variable does not change the value in the parent process. Also, the readonly attribute on an environment variable is not inherited by the child process, the inherited environment variables start as read-write. – Johan Myréen May 7 '18 at 19:17
2

In bash I found the following:

unset MY_VARIABLE
: ${MY_VARIABLE='my_value'}
MY_VARIABLE=${MY_VARIABLE:+$( false )}${MY_VARIABLE:-'this_will_fail_with_exit_1'}
echo $?
echo $MY_VARIABLE

This prints:

1
my_value
  • On the face of it, this (i.e. the third line in your code block) seems to be a pretty elegant solution, making smart use of the parameter expansion options that GNU Bash provides. Thanks :) A nitpick is that it would be nice if it were DRY, i.e. if MY_VARIABLE only had to be written once for each invocation of that solution. But this is a nice starting point and a great answer, thanks again :) – sampablokuper May 9 '18 at 2:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.