Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Slightly off topic, but I don't get environment variables. To me they look like implicit options that are based on what shell session you're currently in. Stuff that hangs around waiting to kill you when you're not looking, like a cecum.

Why would anyone want environment variables when you can have config files or explicit options instead?



They have their advantages over config files and command line options. They're better than config files because you can ask for different behavior in different invocations, and they're better than command line options because it allows you to control a program even when you don't invoke it directly.

Consider, for example, wanting different options for less between when it's called by you, or by man, or by git, or by journalctl, or any other utility.

    alias man='LESS="X$LESS" man'
    alias journalctl='LESS="S$LESS" journalctl'
It's also useful when you want all processes that follow from an invocation to share a certain configuration, and when they follow from another invocation to have another. An example of this is $DISPLAY. I startx on my tty1 and that invokes the Xorg server, exports DISPLAY with the display of the server, typically :0, and executes my window manager. When I press certain shortcut keys, applications will be called, and they, in turn, might call other applications, and they all need to know that when they open up a window it should be on display :0. If I login remotely through ssh, forwarding my X11 service, the applications that I call on the same machine where other processes of the same applications opened their windows on :0, should now open their windows on localhost:10, so I get to see them on the laptop I'm logging in from. If someone else wants to use their account on the same computer, they might switch to tty2 and startx themselves, and all the applications they open should open on their Xorg server process with display :1, not mixing with the other windows I opened locally or on the laptop.

This is also why $LANG, $HOME, $USER, and $TERM are useful. They're stuff that should shared by process trees. Their purpose can't be fulfilled by configuration files or command line options.


> you can ask for different behavior in different invocations,

Pretty much every software supports several config files (system wide, invocation specific) for this reason.

It's not harder to write a line to a file than to set an environment variable.

Environment variables have opaque size limitations. Any non-trivial data is likely to be quoted or encoded in some way, which is going to vary between your applications, and there's going to be quirks in parsing.

It is much easier to programmatically reason about state of standard file formats on disk, instead of resorting to parsing environments of running processes.

Environment variables is inherited to child processes (which is the point of using them). That's going to have security implications for you. Especially if those processes also read their configuration from environment.

And yes, given the choice, I think you will find that most people uses .gitconfig instead of setting their git configuration via environment variables, for these very reasons.


Not every foot fits into the same shoe. There are things that are appropriate for environment variables and things that are not, just like there are things that are appropriate for config files or command line options and things that are not.

Your post is weird to me, because you seem to have interpreted that I somehow implied that we shouldn't use configuration files and that everything should be done through environment variables. That's not at all the case.

On the other hand, your post seems to imply that environment variables should never be used. Is that so? Because if it is, I'd like to know how you'd think that $DISPLAY or $TERM could be better substituted by another solution. I'm not even going to limit you to config files and command line options. I've already painted a concrete scenario of the use of $DISPLAY in my previous post, can you paint that same scenario with another solution?

Now to paint a scenario of the use of $TERM. $TERM controls how terminal applications communicate with the terminal to use its features like clearing the screen, moving the cursor around, etc. Imagine one server and multiple people connecting to it through ssh in each of their computers. Each person uses a different terminal in their machines. The server terminal applications use $TERM to determine how to use the different features of the terminals that are connecting to it. These are not only the applications that the user launches through the shell, but also the ones that are called indirectly via other programs that the user has invoked. Also keep in mind that the server might simply not recognize the type of terminal you have (it doesn't have the appropriate terminfo file installed), but you might know another terminal that is similar to yours and that the server might know. In that case, you'll want to inform all programs you launch and the ones they launch to treat your terminal as if it were that other terminal (this is when you'd set $TERM in the shell session to something else). Can you rethink this so the terminal applications can use something other than $TERM to determine the type of terminal that's in use and still allow the user to override it?

> Pretty much every software supports several config files (system wide, invocation specific) for this reason.

How would the program know what configuration file to use for invocation specific configuration? If it's not through an environment variable, then I guess you specify it by command line option, and, like I said, that's not going to help when you don't control the options that are passed to the program when it's another program and not yourself that makes the call.


It's harder to accidentally commit a running process's environment to version control ;)


> Why would anyone want environment variables when you can have config files or explicit options instead?

If you have an underlying .so used by an application that hasn't implemented optioning at init of said library it can then let you control things in something that has no runtime configuration source. I've written several such libraries. Also good for controlling things you override with LD_PRELOAD


Config files set per user/system options whether they are set locally or globally, while env variables set per options per shell.

Global options > user options > env variables > command line parameters


Isn't it the other way around? Command line params are more than env vars and so on?


I think those are arrows, not greater-than-signs (i.e. load config from global options, then overwrite that with user options, etc.)


Oh, me. Sorry, you're totally correct.


I am very pro environment vars and consider files an anti pattern. Files are like globals, sibling processes end up sharing state, plus they survive longer than their use case.

Env variables cascade, which is why they have wider uses than program args. You can organize a system as a group of processes and envs are a good way to share state and abstract out the details like prod vs staging without affecting the program implementation.


I'd say it's really about size. A few dozen configs in environmental vars, sure. But once you expand beyond that, need to manage variants of them, need to compose them from fragments you start to see benefits from something more structured like xml, toml, json, or yaml etc.


I just start loading env variables from files, e.g. source blah.env

This way, my bash environment mirrors the task I am working on


I'd say it's really about size.

My wife assures me that size doesn't matter, it's actually the way you use your configs to manage your environmental vars.

(sorry, I just couldn't help myself...)


Envs are also globals. Dynamically scoped globals. They are evil and allow all kind of spooky action at distance, but sometimes they are a necessary evil.


Explicit options require the cooperation of every intermediate program. If program D is called by C, which is called by B, which is called by A, and D gets a new option that we need to use, we have to modify C to pass it to D, then B to pass it to C, and A to pass it to B. Or we could just set an environment variable.

Configuration files don't let us override individual variables easily, in different invocations of the program. We have to generate a custom version of the configuration file for that job and pass that file's name to it.

Environment variables are only visible to children, so they are inherently secure; we don't worry whether permissions are too loose on an environment variable so that another user could see it or modify it. There is no such thing.


Environment variables are visible with 'ps -E', to anyone with the same UID or root.

In Linux, they're visible in /proc/<pid>/environ, which is permission 400 and owned by the effective user of the process.


In addition to the other advantages mentioned here, environment variables serve as a common language that all programs can understand, in theory. They're also less complex to access than reading and parsing a file (less important for, say, Node.js than C++, but).


> Why would anyone want environment variables

> when you can have config files

You still need a way to tell your program where the config file is.

Environment variables are just like a configuration file that's automatically opened by the OS when a process start and automatically parsed as a set of named strings by the libc. In the many cases where one does not need more complex data structures than that, then to introduce a config file is just making things harder.

That being said, I wish envvars had namespaces of some sort.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: