4

I'm writing a utility that needs to be able to set and use context. I'd like it to use environment variables in the shell so that it can remember what context it's currently in between invocations. Ideally, I'd like to set this environment variable from within the utility itself. Something like:

mytool set-context <context>
mytool do-stuff # Aware of <context>

I'd like this to behave like:

export MYTOOL_CONTEXT=<context>
mytool do-stuff # Aware of <context>

Now, it's not actually possible for a program to set environment variables in the environment of the calling shell (covered on SO here). However, that's the sort of behavior I want, and I'm looking for an approximation or workaround.

Some ideas I had in mind:

  • Output the proper string, and expect the user to set the variable (assuming that the variable isn't simply set to <context>, but instead to some string derived from it). That would look something like export MYTOOL_CONTEXT=$(mytool get-context-var <context>).
  • Output the full command for setting it. That would look something like $(mytool set-context <context>), where the output of the command is actually executed (and the output would be something like export MYTOOL_CONTEXT=<context>).
  • Save to a temporary file whose name is based on the PID of the shell. This would work in most cases, but removing the file wouldn't happen automatically, so it would probably just lie around until reboot (ie, possibly forever on many machines).

Any other ideas? Thanks!

Note: The above examples use BASH syntax, but there are equivalents in most shells.

Community
  • 1
  • 1
joshlf
  • 21,822
  • 11
  • 69
  • 96
  • Your question seems unclear but you seem to be looking for: `MY_VARIABLE=value somecommand` syntax. – devnull Jan 24 '14 at 06:11
  • Yeah, on second read, it's confusingly worded. I'll clarify. – joshlf Jan 24 '14 at 06:12
  • @devnull, is that more clear (I edited the first paragraph)? – joshlf Jan 24 '14 at 06:14
  • possible duplicate of [How to write a bash script to set global environment variable?](http://stackoverflow.com/questions/12351702/how-to-write-a-bash-script-to-set-global-environment-variable) – user123444555621 Jan 24 '14 at 06:19
  • The main objection I have to the proposed duplicate, @Pumbaa80, is that none of the answers describe or mention the `.` (aka `source`) command. The chosen answer describes setting the environment in files like `~/.bash_profile`, but those are not necessarily the best choice. And knowing the `.` command allows you to set new values later. – Jonathan Leffler Jan 24 '14 at 06:30
  • @JonathanLeffler You should post your answer to the other question, then. – user123444555621 Jan 24 '14 at 06:33
  • It had occurred to me... – Jonathan Leffler Jan 24 '14 at 06:36
  • 1
    The precedent by tools like `resize` and `ssh-agent` is that you invoke them like `eval $(resize)`. This matches what you have already described; the tool prints a snippet to be evaluated by the calling shell. `ssh-agent` even has an option to print the assignment in Csh syntax (bletch). – tripleee Jan 24 '14 at 06:56

1 Answers1

6

Before clarification

The POSIX shells (bash, ksh, etc) and the Bourne shell allow you to set environment variables on a command line for one command only:

MYTOOL_CONTEXT=<context> mytool do-stuff

This sets MYTOOL_CONTEXT as an environment variable for that invocation of mytool (only). Incidentally, most shells accept an option -k (which is not standardized by POSIX) that means all arguments that look like VAR=value are treated as environment variables, even when they appear after the command name. This is a curiosity with rather limited practical value, which is why it is not standardized.

The POSIX env command is designed to allow you to control the environment of an invoked command, though it is commonly used without any arguments to list the current environment. So, alternatively, you might use:

env MYTOOL_CONTEXT=<context> mytool do-stuff

The advantage of env is that you can do things like unset every environment variable before setting the ones specified in the command line (so you get total control over the environment).

After clarification

If the intention is to set the environment for subsequent use, then the . command is the one to use. You can create a file, context, which contains commands to be executed to set the environment. Then you can use:

. context

and the contents of the file will executed in the context of the current shell, so you can set environment variables, etc. Bash provides a synonym, source, for the . command. It was inspired by the C shell which does not provide the . command but does provide source as an equivalent. The file named as an argument to the . command will be searched for on $PATH but does not need to be executable (readable is sufficient). If you provide extra arguments, they become the positional parameters ($1, etc) for the duration of the . command. Note too that any variables or functions created by the dotted file remain in effect when the file is finished (unlike variables in a normal script which vanish when the script completes). If you aren't going to pollute the user's name space, you have to be careful.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • The problem is that I want this to persist in the shell's environment (for example, in bash, using `export`). I know how to set it as a user. What I'd like to figure out is a way to allow the invoked program to set the environment variables without much user intervention. – joshlf Jan 24 '14 at 06:18
  • No; it reads the file as if it is shell script. If it doesn't contain valid shell commands, all hell will break loose. Instead of starting a new process to execute the contents of the file (the normal technique for executing a shell script), the current process reads the file and executes the commands. – Jonathan Leffler Jan 24 '14 at 06:35
  • Is there any equivalent for binaries? – joshlf Jan 24 '14 at 06:36
  • Not really. The nearest equivalent would be `exec context` where `context` is now a binary that adjusts the environment and then executes — no `fork()` — an (interactive?) shell. The `exec` replaces the current shell with the program, then the program replaces itself with another shell. I don't like that as a technique, but it can be made to work. I've used it when I have to set the environment using a C shell script of a colleague. I exec the `csh`, `source the.env` and then `exec ksh` (or `exec bash`) to get back to a sane shell with their environment settings. – Jonathan Leffler Jan 24 '14 at 06:46
  • Hmmm... yeah, that's not too desirable. – joshlf Jan 24 '14 at 06:47
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/45978/discussion-between-jonathan-leffler-and-joshlf13) – Jonathan Leffler Jan 24 '14 at 06:47