43

Is there a way to set environment variables in Linux using C?

I tried setenv() and putenv(), but they don't seem to be working for me.

Xantium
  • 11,201
  • 10
  • 62
  • 89
iman453
  • 9,105
  • 16
  • 54
  • 70

5 Answers5

66

I'm going to make a wild guess here, but the normal reason that these functions appear to not work is not because they don't work, but because the user doesn't really understand how environment variables work. For example, if I have this program:

int main(int argc, char **argv)
{
  putenv("SomeVariable=SomeValue");
  return 0;
}

And then I run it from the shell, it won't modify the shell's environment - there's no way for a child process to do that. That's why the shell commands that modify the environment are builtins, and why you need to source a script that contains variable settings you want to add to your shell, rather than simply running it.

Carl Norum
  • 219,201
  • 40
  • 422
  • 469
  • The corollary to this is to get the "run program to set variables" behavior in the shell you 1) write a shell script to do the job and 2) don't run it the usual way, but rather `source` it. (BTW in most shells `. script` is the same as `source script` and a lot less typing.) – dmckee --- ex-moderator kitten Aug 05 '10 at 16:08
  • That makes a lot of sense, thank you :) Yeah, that was what I was doing. I'm new to linux, so pardon me if this is a stupid question, but is there no way at all for a child process to set the shells environment in C, and by not sourcing a script. I'm interning at this company and I've been asked to write a function to set the timezone of a device running linux, so I was trying to set the TZ environment of the shell. – iman453 Aug 05 '10 at 16:11
  • @iman: try `man -k tz` for some options. On my Mac OS X machine what shows up is `tzset (3)`... – dmckee --- ex-moderator kitten Aug 05 '10 at 16:15
  • @dmckee: wouldn't tzset still work only for the process I run the code and all its child processes? I sorta wanted a way such that all the processes get this information (don't know if that's even possible lol) – iman453 Aug 05 '10 at 16:18
  • 2
    @iman: Yes. That advice appears to have been worth every cent you paid for it. Sorry. – dmckee --- ex-moderator kitten Aug 05 '10 at 16:32
  • @iman453, you should be able to write a shell script to do what you'd like. – Carl Norum Aug 05 '10 at 16:58
13

Any unix program runs in a separate process from the process which starts it; this is a 'child' process.

When a program is started up -- be that at the command line or any other way -- the system creates a new process which is (more-or-less) a copy of the parent process. That copy includes the environment variables in the parent process, and this is the mechanism by which the child process 'inherits' the environment variables of its parent. (this is all largely what other answers here have said)

That is, a process only ever sets its own environment variables.

Others have mentioned sourcing a shell script, as a way of setting environment variables in the current process, but if you need to set variables in the current (shell) process programmatically, then there is a slightly indirect way that it's possible.

Consider this:

% cat envs.c
#include <stdio.h>
int main(int argc, char**argv)
{
    int i;
    for (i=1; i<argc; i++) {
        printf("ENV%d=%s\n", i, argv[i]);
    }
}
% echo $ENV1

% ./envs one two
ENV1=one
ENV2=two
% eval `./envs one two`
% echo $ENV1
one
% 

The built-in eval evaluates its argument as if that argument were typed at the shell prompt. This is a sh-style example; the csh-style variant is left as an exercise!

Norman Gray
  • 11,978
  • 2
  • 33
  • 56
  • I noticed this was down-voted. Any indication why? Did someone spot an error? – Norman Gray May 21 '11 at 14:06
  • Not sure why someone would downvote this. Weird. Looks like a good solution to me! – kodybrown May 27 '15 at 17:03
  • I suppose it's because the answer sets the environment variable using the built-in bash command 'eval', which are the chances that the programmer might not be able to use bash at all. If the programmer is allowed to use a shell script (which is not always the case, hence the question), there're other more obvious and better ways to set the environment variable other than the bash specific eval. (p.s. I didn't downvote) – neuro_sys Jun 30 '15 at 15:28
  • What are the other ways you're thinking of? It's true that the question is a little vague, but I took/take it to be asking how one would set environment variables in a shell, using a program. Since the only ways I can think of that the variables can be set _in that shell_ are using `source` or `eval`, there are few options (of course, if you want them to be set in the current or a child process, then you just use `setenv`). Note that `eval` isn't just bash – it's in [posix](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#eval); I don't know what the *csh equivalent is. – Norman Gray Jul 01 '15 at 12:29
8

The environment variable set by setenv()/putenv() will be set for the process that executed these functions and will be inherited by the processes launched by it. However, it will not be broadcasted into the shell that executed your program.

Why isn't my wrapper around setenv() working?

Community
  • 1
  • 1
karlphillip
  • 92,053
  • 36
  • 243
  • 426
1

The environment block is process-local, and copied to child processes. So if you change variables, the new value only affects your process and child processes spawned after the change. Assuredly it will not change the shell you launched from.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
0

Not an answer to this question, just wanna say that putenv is dangerous, use setenv instead.

putenv(char *string) is dangerous for the reason that all it does is simply append the address of your key-value pair string to the environ array. Therefore, if we subsequently modify the bytes pointed to by string, the change will affect the process environment.

#include <stdlib.h>

int main(void) {
    char new_env[] = "A=A";
    putenv(new_env);

    // modifying your `new_env` also modifies the environment
    // vairable
    new_env[0] = 'B';
    return EXIT_SUCCESS;
}

Since environ only stores the address of our string argument, string has to be static to prevent the dangling pointer.

#include <stdlib.h>

void foo();

int main(void) {
    foo();
    return EXIT_SUCCESS;
}

void foo() {
    char new_env[] = "A=B";
    putenv(new_env);
}

When the stack frame for foo function is deallocated, the bytes of new_env are gone, and the address stored in environ becomes a dangling pointer.

Steve Lau
  • 658
  • 7
  • 13