3

I wrote small convenience functions for working with environment variables in C++ by wrapping std::getenv, setenv and environ. In addition to setting and getting the environment variables, I provided functionality to check if the current environment contains a certain variable.

#include <cstdlib>
#include <unistd.h>

template <typename VAR_TYPE>
void set(const std::string& variableName, VAR_TYPE varValue, bool overwrite = false) {
    if (!setenv(variableName.c_str(), std::string(variableValue).c_str(), overwrite)) {
      if (errno == ENOMEM) {
        throw std::bad_alloc();
      } else if (errno == EINVAL) {
        throw std::invalid_argument("Variable name invalid: "  + variableName);
      } else {
        throw std::runtime_error("Failed to set environment variable " + variableName);
      }
    }
  }

std::string load(const std::string& variableName, const std::string& defaultValue = "") {
    if (const char* envVariable = std::getenv(variableName)) {
      return std::string(envVariable);
    }
    return defaultValue;
  }

bool contains(const std::string& variableName) {
    for (char** currentEnviron = environ; *currentEnviron; currentEnviron++) {
      if (!std::strncmp(variableName.c_str(), *currentEnviron, variableName.length())) {
        return true;
      }
    }
    return false;
  }

However, by definition, this only allows to access environment variables that are in the form NAME=VALUE.

In bash, I can do the following:

$> export SOME_VAR
$> export -p | grep SOME_VAR
declare -x SOME_VAR

Apparently, SOME_VAR is defined somewhere, even if I dont assign a value to it. When I run printenv however, which uses the same methods I use in my wrapper, SOME_VAR is not listed. I have had a look at /proc/self/environ but this only lists variables with assigned value.

My questions are:

  1. What is the difference between an environment variable defined as SOME_VAR_WITH_VALUE=42 and SOME_VAR_WITHOUT_VALUE.
  2. Is there a possibility in C/C++ to access environment variables without values?
sfun
  • 198
  • 1
  • 11
  • Your check, `-z $SOME_VAR`, doesn't check if `SOME_VAR` is set, but if length of `$SOME_VAR` is 0. If `SOME_VAR` is not set or if it is set to empty string this will return true. – TCvD Dec 04 '20 at 08:59
  • Oh my, you are right. I edited my question – sfun Dec 04 '20 at 09:16

3 Answers3

3

Shell has a notion of internal and exported environment variables.

A shell command like ABC=xyz sets an internal variable (which can be seen in Bash using set).

To export an internal variable, there's the export command.

export ABC=xyz will create an internal variable and export it (exported ones can be seen using env).

export ABC by itself does not define any variable, it simple tags ABC to be exported, when it's defined.

Try this for example:

$ ABC=xyz
$ set | grep ABC
ABC=xyz
$ env | grep ABC

And now with export:

$ export ABC
$ set | grep ABC
$ env | grep ABC
$ ABC=xyz
$ set | grep ABC
ABC=xyz
$ env | grep ABC
ABC=xyz

Notice how the variable got exported the moment it was set. So export ABC is purely a shell feature, it does not modify the environment. There is no API for it.

To set an environment variable with an empty value, use export ABC="":

$ export ABC=""
$ env | grep ABC
ABC=
rustyx
  • 80,671
  • 25
  • 200
  • 267
  • Thanks for the great explanation! So the closest I can get to having "empty" variables (without jumping through hoops by for example parsing the output of `declare`) is by using `export ABC=""` – sfun Dec 04 '20 at 10:09
0

First try this:

export SOME_VAR
set | grep SOME_VAR

The second line will return nothing. That is because the first line doesn't define SOME_VAR.

As for your two questions:

  1. export VARIABLE has no effect. It does not create variable with no value, nor does it change value of an already set variable.
  2. Environment variable always has a value, but that value may be empty string. If you try to access nonexistent variable you will get NULL.
TCvD
  • 600
  • 2
  • 8
  • When I use `export SOME_VAR` and then run `export -p`, `SOME_VAR` is listed as `declare -x SOME_VAR`. So it seems that the `export` statement seems to have some effect – sfun Dec 04 '20 at 09:14
  • True, the name is exported, you are correct -- `export VAR` has effect. But it does not define `VAR`'s value. So `VAR` will be exported, but it's value will still not be defined. – TCvD Dec 04 '20 at 09:20
0

Ultimately, bash is just another Linux program. There's limited magic involved. It can alter the libc environ variables, but it can also have its own internal variables. If you don't see SOME_VAR_WITHOUT_VALUE in /proc/sef/environ, you can deduce that it's some kind of bash-internal variable.

Note that bash, like most Linux programs, can be run multiple times in parallel. Environments are separate between processes. So even in C you can't access any of the bash environment variables directly. You're taking advantage of the fact that bash copies its environment to your environment, when your process is started. But env -i your_process will stop even that.

MSalters
  • 173,980
  • 10
  • 155
  • 350