tags:

views:

131

answers:

5

Hi,

Is there anyway to set environment variables in linux using C? I tried setenv() and putenv(), but they don't seem to be working for me.

Thanks in advance.

+3  A: 

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.

http://stackoverflow.com/questions/662466/why-isnt-my-wrapper-around-setenv-working/662486#662486

karlphillip
+1  A: 

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
+7  A: 

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
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
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
@iman: try `man -k tz` for some options. On my Mac OS X machine what shows up is `tzset (3)`...
dmckee
@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
@iman: Yes. That advice appears to have been worth every cent you paid for it. Sorry.
dmckee
@iman453, you should be able to write a shell script to do what you'd like.
Carl Norum
A: 

I got it from my "Advanced Programming in the UNIX Environment" Book

The environment list the array of pointers to the actual name=value strings and the environment strings are typically stored at the top of a process's memory space, above the stack. Deleting a string is simple; we simply find the pointer in the environment list and move all subsequent pointers down one. But adding a string or modifying an existing string is more difficult. The space at the top of the stack cannot be expanded, because it is often at the top of the address space of the process and so can't expand upward; it can't be expanded downward, because all the stack frames below it can't be moved.

  1. If we're modifying an existing name:

    a. If the size of the new value is less than or equal to the size of the existing value, we can just copy the new string over the old string.

    b. If the size of the new value is larger than the old one, however, we must malloc to obtain room for the new string, copy the new string to this area, and then replace the old pointer in the environment list for name with the pointer to this allocated area.

  2. If we're adding a new name, it's more complicated. First, we have to call malloc to allocate room for the name=value string and copy the string to this area.

    a. Then, if it's the first time we've added a new name, we have to call malloc to obtain room for a new list of pointers. We copy the old environment list to this new area and store a pointer to the name=value string at the end of this list of pointers. We also store a null pointer at the end of this list, of course. Finally, we set environ to point to this new list of pointers. if the original environment list was contained above the top of the stack, as is common, then we have moved this list of pointers to the heap. But most of the pointers in this list still point to name=value strings above the top of the stack.

    b. If this isn't the first time we've added new strings to the environment list, then we know that we've already allocated room for the list on the heap, so we just call realloc to allocate room for one more pointer. The pointer to the new name=value string is stored at the end of the list (on top of the previous null pointer), followed by a null pointer.

All the best.

Hemant
I'm not sure that you have understood the OP's question...
dmckee
You are correct :-). My understanding was he is facing some in his executable after setting env variables.
Hemant
+1  A: 

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