amphibient
amphibient

Reputation: 31268

Is there a way to set an environment var in Perl and export it out of the scope of the script?

You can read an env var

$envVarVal = $ENV{'VAR_NAME'};

Setting it reversely does not work, i.e. you will not see the var once the script is done running:

$ENV{'VAR_NAME'} = 'some_val';

I also tried a system call:

`export VAR_NAME=some_val`;

The call succeeded within the Perl script but again the variable was not visible out of scope.

Is it possible to do it and, if not, why is it not possible to do it in Perl but is by sourcing a regular shell script, any shell, as far as I am aware?

Upvotes: 2

Views: 1833

Answers (3)

Schwern
Schwern

Reputation: 165207

Yes, use VMS.

This might not be the most practical advice, but it is complete. :)

VMS is... different. VMS and Unix probably more different than Windows and Unix. Perl is built around a Unix mindset and must stretch some of those Unix concepts to the breaking point in order to wrap them around VMS' way of doing things. One of those is what %ENV means.

VMS doesn't have environment variables in the sense that Unix does. The closest thing VMS has is logical names. They're a table of key/value pairs and serve a purpose similar to both environment variables and shell aliases. Unlike environment variables on Unix, and like Ogres, they have layers.

From The OpenVMS Consultant: Logical Names (Part 1):

Most processes are associated with at least four logical name tables (Process, Job, Group, and System). Names are generally translated by first searching in hierarchical sequence the Process, Job, Group and System tables. Advanced users can control the search and translation process. Additionally, some translations preclude logical names defined by end-users, for security and integrity reasons.

On Unix, the environment table is copied into a process and thus that process cannot have any effect on its parents. On VMS, the logical tables are a stack. VMS reads up that stack until it finds a value. For example, if you ask for the value of, say, SYS$OUTPUT (which is like STDOUT) it will first check the logical table of the process, then the job, then the group and finally the value for the whole system. In this way the operating system can define a default SYS$OUTPUT in the system table, but it can be overridden by you or a single process.

If this seems complicated, its actually a simplification of what really happens.

Logicals are a stack, so what happens if you set one?

Finally, to the point of this answer, $ENV{FOO} = "BAR" will look up the logical table stack and assign to the first FOO it finds. If there's one in the process table, it will assign to that. If there's one in the job table, it will assign to that. And so on until it reaches the limit of its permission to write. At that point it will create a new entry in its highest table, probably the job table. That means $ENV{FOO} = "BAR" is very likely to land in the job table and thus be seen by all your processes.

And that is how one sets an environment variable in Perl in one process and exports to another.

Upvotes: 13

qqx
qqx

Reputation: 19475

That isn't possible on most systems, not just in perl but in shells as well. Each process gets its own copy of the environment inherited from the parent, and any changes to the environment only affect that process and any child processes that it creates (after making that change to the environment).

Environment variables set in perl with $ENV{VAR_NAME} = 'some_val'; will be available to processes created by the perl script after that. Just as environment variables set by a shell script with export VAR_NAME=some_val would be reflected in any processes later created by that script.

A shell function or sourcing a shell script (using source scriptname or . scriptname) can make changes to the environment that outlive the function or sourced script. But that is only possible because no new process is created to run that code, it would be run within the same process from which it was called.

Using perl's backtick operator to run export is pointless. That will succeed because that causes perl to start a new shell process where the environment variable is set, but then that process would immediately exit without doing anything useful.

You can somewhat see this in action by using echo $$ to show the process ID. Try that before running, a script, within a shell script that you source, running the same script without sourcing it, and from within the shell code executed with perl's backtick operator. The process ID can also be printed from perl using print $$, "\n";.

Upvotes: 8

choroba
choroba

Reputation: 241988

It is not possible. But, you can tell Perl to create a script for you and then source it or eval it:

#!/bin/bash
eval $(perl -e 'print "VAR1=", rand 1')
perl -e 'print "VAR2=", rand 1' > var2.sh
. var2.sh
rm var2.sh
echo $VAR1 $VAR2

Upvotes: 6

Related Questions