How do I import environment settings into my Perl program? - linux

I have a script whose content simply exports a variable in linux.
export LD_LIBRARY_PATH=....
I want to run this script in my Perl script so whoever is running my Perl script will have their LD_LIBRARY_PATH set. Can i just do this in the beginning of my Perl script:
#!/usr/bin/perl -w
system(". /myfolder1/myfolder2/myScript.sh");

#!/bin/sh
. /myfolder1/myfolder2/myScript.sh
exec perl -wxS "$0" "$#"
#!/usr/bin/perl -w
# .. the rest of your script as normal
When you run this, it will first be executed by /bin/sh, which is capable of loading myScript.sh into the local environment. sh then execs Perl, which is told to continue from the following line.

This won't work. To change the environment inside your Perl script (and to change the environment that will be passed on to commands run from inside your Perl script), change the %ENV variable.
$ENV{"LD_LIBRARY_PATH"} = ... ;

This won't work. There is no way for a subshell to manipulate the environment of the parent process.
But you could make your script echo the string you want to set as LD_LIBRARY_PATH and then from within your Perl script you could do something like that:
$ENV{LD_LIBRARY_PATH} = `path/to/your/script.sh`;
Of course, a bit of error checking might also be a good idea.

No. Your environment changes made in a child cannot affect the parent. This means running a script will not affect perl. Also perl will not affect the shell from which it was called. You can edit the environment inside perl by changing the special variable %ENV. If there's some kind of unreproducible calculation done in that script, maybe the script should just echo the setting and perl can pick that up on STDOUT and use it.
I {changed directory, modified my environment} in a perl script. How come the change disappeared when I exited the script? How do I get my changes to be visible?
Unix In the strictest sense, it can't be done -- the script executes
as a different process from the shell
it was started from. Changes to a
process are not reflected in its
parent, only in its own children
created after the change.

I had a similar problem a few years ago and whipped up a little module, Env::Sourced, that should do the trick.
use Env::Sourced qw(/myfolder1/myfolder2/myScript.sh);
...

Another option (other than making the changes directly in Perl's %ENV) is to make the changes you want a Perl module, so that you can say:
use MyEnvironment;
and have it modify your environment in all your scripts. It would make it simple to make changes after the fact that will not require editing every script.
The module itself will be simple, something like this:
package MyEnvironment;
$ENV{LD_LIBRARY_PATH} .= ":/some/path/you/want/appended";
# Any other changes you want here.
1;

That won't work. An (unpleasant) alternative might be to replace /usr/bin/perl with a shell script that first executes your script and then executes the perl executable.

This can't be done in the way you're trying to do this.
It either needs a wrapper shell script that sets LD_LIBRARY_PATH and then calls your perl script, or any user executing the script needs to have LD_LIBRARY_PATH set correctly in the first place.
If doing the latter, then this can be managed globally by editing /etc/profile and /etc/cshrc (for ksh, sh, bash, csh and tcsh) shells. You can then test for the value of LD_LIBRARY_PATH in your script and if not set/set incorrectly then print a friendly message to the user. Alternatively individual users can set this in their local .profile/.cshrc files.
Note: you haven't given any information about the environment or useres that might run this, so there's also the possibility that users may set LD_LIBRARY_PATH to something they need. If you do check LD_LIBRARY_PATH for a "good" value in your script, then keep in mind that several paths may have been specified, so you will need to parse this environment variable properly.

If you can find the right place in your perl script, this works as in my example:
$ENV{"LD_LIBRARY_PATH"} = "/oracle/product/10g/lib";
And it didn't require me to call another script to set the env var.

The Env::Modify module addresses this issue, at least for POSIX-y platforms:
use Env::Modify 'source';
source("/myfolder1/myfolder2/myScript.sh");
... environment settings from myScript.sh are now available to Perl ...

Related

Perl script, how to source an environment variables from .bash_profile

I need some quick advice on perl script. I created a script that basically calls other perl scripts and many other shell scripts within those. The problem I'm facing, is simply trying to make this run on a universal level by setting one environment variable.
This is on linux RHEL/CentOS by the way...
so I add my variables to .bash_profile and it works without an issue if I MANUALLY source the file first, then run my perl script! This is, OK, but I would like the script to automate this part instead of needing the extra, manual sourcing step.
So my script looks like this... in short
#!/usr/bin/perl
use warnings;
use strict;
`/bin/bash ~/.bash_profile`;
blah blah blah more code etc;
When launching the main script (where this part of the code is) it works no problem. It's all the subsequent calls made to other scripts that are failing...as if it is not passing the variable on to the rest of the scripts.
Any ideas??
Thanks,
The easiest way would be to set the environment variables within perl with $ENV{"name"}=.... These variables then will be propagated automatically to any programs started from within the perl script, no matter if perl or shell scripts
alternatively you could open the .bash_profile, parse it within perl, extract the variables and then set them again within perl with $ENV. This is error prone because there might be several ways to declare the variables.
or you could spawn a shell, which reads the .bash_profile and calls env afterwards. Then you read and parse the output from this shell. This is similar to the previous proposal, but the output you have to parse is more clearly defined.
or you could use a shell script which sources .bash_profile and then spawns the perl script.
Environment variables are inherited by child processes from their parent, so you simply need to launch perl from a shell that sourced ~/.bash_profile.
bash login shells source that file automatically, so it's just a question of setting bash as your login shell. You could also use bash -l.
bash -lc 'exec script.pl args'
If you can't setup the correct environment before launching Perl, you can bootstrap a bash login shell.
if (#ARGV && $ARGV[0] eq 'setup') {
shift(#ARGV);
} else {
exec('bash', '-lc', 'exec "$#"', '-', $^X, $0, 'setup', #ARGV) or die $!;
}
In all three cases, the variable is accessed using
$ENV{VAR_NAME}
Backticks (and system, and open, etc.) spawns a separate process that inherits the environment from your Perl program, but can only affect its own environment, not propogate changes back to your Perl script.
Shell::GetEnv has a few tricks that will help you incorporate those changes to the child environment, but often times you will find it is easier to parse .bash_profile yourself.
The more recent Env::Modify module can handle this task, leveraging the tricks in Shell::GetEnv.
use Env::Modify 'source';
source("$ENV{HOME}/.bash_profile");
using $ENV we can get any value from .profile/.bash_profile
Example:
suppose any variable is store in your .profile/.bash_profile
export CONNECT="Connection"
Retrieve the same variable in your perl scripts from .profile/.bash_profile as:
my $DB_CONNECT = $ENV{CONNECT};

How to source file from bash script

I'm trying to source a file with an environment variable from my bash script, but it doesn't work.
This is the content of my script (test.sh), which is located in ~/scripts/test.sh.
#!/bin/bash
FILE_NAME=/tmp/source_file
touch $FILE_NAME
echo "export TEST=\"test\"" > $FILE_NAME
source $FILE_NAME
Then I use alias in my ~/.bashrc.
alias testScript=~/scripts/test.sh
But when I use my script testScript, it didn't set the environment variable.
You need to use:
alias testScript=". ~/scripts/test.sh"
to source the file. Or you can use source in place of ., but I don't much like C shells so I don't use C shell notations such as source.
Environment variables only flow downstream in the process tree.
When you type testScript to a bash process, it creates a child process and execs /bin/bash or whatever is set by #!
Any environment variables set there remain only with the child process. Export causes the variables to be copied to additional grandchildren (children of that child) that might be spawned from that child.
Nothing can copy back to a parent. You need to use source instead of running the file. See Jonathan's answer.
You could try editing the files ~/.bashrc or ~/.login to set enviornment variables you need frequently.
See also https://superuser.com/q/153371 and https://superuser.com/questions/18988/difference-between-a-b-and-export-a-b-in-bash for more explanation of export in bash.
None of the other methods worked for me [source /path/to/file vs . ./path/to/file, alias, etc...], until, thanks to this tutorial I found that using the:
#!/usr/bin/env bash shebang
instead of the simpler #!/usr/bin/env one lets arguments pass on to the interpreter, which I think is the key here – see this document for more info.
In any event, if source commands in any form aren't working for you, try checking your shebang, that might be the problem :)

Setting an environment variable through a Perl script [duplicate]

This question already has answers here:
How to Share ENV Variables Among Perl Scripts
(2 answers)
Closed 5 years ago.
I am trying to set an environment variable, LD_LIBRARY_PATH, through a Perl script in the following way:
I have created .profile under /root
.profile has an export command say:
export LD_LIBRARY_PATH=/
My Perl script is test.pl and it has:
#!/usr/bin/perl
system(". /root/.profile");
When I execute ./test.pl, LD_LIBRARY_PATH doesn't change.
What am I doing wrong?
Your current script doesn't even change an environment variable in the Perl script itself. Rather, it invokes a shell as a subprocess; that shell process executes . /root/.profile, which updates $LD_LIBRARY_PATH only in that shell process.
You can change an environment variable in a Perl script (more precisely, in the process running the Perl script) by updating %ENV:
$ENV{LD_LIBRARY_PATH} = '/'; # or some more reasonable value
As perldoc -v %ENV says:
%ENV The hash %ENV contains your current environment. Setting a value in "ENV" changes the environment for any child processes you subsequently "fork()" off.
But that probably still won't do what you want; it won't (and can't) affect the environment of the process that invokes the Perl script (your interactive shell), only the Perl process itself and anything it invokes.
I'll assume you want to update $LD_LIBRARY_PATH in your current interactive shell process. To do that, you can have you Perl script print a shell command that will update $LD_LIBRARY_PATH. Then, rather than simply running your Perl script, you can execute it and then evaluate its output. For example:
$ cat env.pl
#!/usr/bin/perl
use strict;
use warnings;
print "export LD_LIBRARY_PATH=/\n";
$ ./env.pl # just prints the command without executing it
export LD_LIBRARY_PATH=/
$ eval $(./env.pl) # executes the command in the current shell
$ echo $LD_LIBRARY_PATH
/
$
This assumes that your current shell is bash or something similar.
Another option: After modifying %ENV, your Perl script can invoke another command, even a new interactive shell. The new process will inherit its environment from the Perl script. This can be a bit cumbersome, though; for example, if the new process is an interactive shell, it won't inherit unexported variables or history from the parent shell.
(One note, not directly related to your question: The fact that you're messing with /root/.profile implies that you're doing things as root (superuser). This can be dangerous. Use the root account (either by logging into it or via sudo only for things that actually need root privileges. For anything else, use a personal user account.
To change the environment in a Perl script, assign to the %ENV hash:
$ENV{'LD_LIBRARY_PATH'} = '/';
If you want to write a program that's used by a shell to change its environment, the way this is generally done is to have the script write shell commands to stdout. The shell then executes this with command substitution and uses eval to execute the resulting commands:
Perl script:
#!/usr/bin/perl
print 'LD_LIBRARY_PATH=\n';
Shell script:
eval "$(/path/to/perlscript)"
For examples of commands that work like this, see tset and ssh-agent.
system starts a new process, and changing the environment there won't affect the environment in the process of your script (usually—there are generally os-dependent means of changing other processes' environments).
The environment in a perl program is associated with %ENV, which is kind of like (it isn't actually) a tied hash to the environment: changing it will change the environment. Thus:
$ENV{LD_LIBRARY_PATH} = '/';
This can now be done with the Env::Modify module:
use Env::Modify 'source';
source("/root/.profile");
... env settings of .profile are now available to Perl ...
You can't do it.
This is from the Perl FAQ:
In the strictest sense, it can't be done--the script executes as a different process from the shell it was started from. Changes to a process are not reflected in its parent--only in any children created after the change. There is shell magic that may allow you to fake it by eval()ing the script's output in your shell; check out the comp.unix.questions FAQ for details.

How to export a library path from a child shell script and use the path in the parent shell?

I have the following files
build.sh
source exportpath.sh
exportpath.sh
export LD_LIBRARY_PATH=/usr/local/lib/
But,when I am executing this build.sh and then running the echo $LD_LIBRARY_PATH command I am not able to get the LD_LIBRARY_PATH value. How to set this value to the current shell.
I found that we have to source it in the current shell. But I want it done by a shell script.
When I am using source exportpath.sh in current shell, then my LD_LIBRARY_PATH is working but I want this should be done by a shell script.
How can I do this?
There is no magical way to have an environment variable set in the child shell be propagated to the parent shell. See this reply
You could implement some convention, for instance by having exportpath.sh taking a filename, that build.sh would later source (or use eval).
You may want to have your own shell function to wrap both.
But you should usually not do such weird tricks. For example, you could wrap some of your programs into a shell script setting the LD_LIBRARY_PATH (mozilla or firefox is often doing such things).
echo $LD_LIBRARY_PATH
Note the Use of "$" to reference the variable value.

shell export variable not come into effect

I (on mac osx) often use
export http_proxy=http://192.168.0.205:1099
to proxy http connection to get a highed download speed. To make things easy, I wrote a shell file named proxy.sh to do this:
#!/bin/sh
export http_proxy=http://192.168.0.205:1099
Before I downlaod, I execute proxy.sh shell command, but I found it did't not come into effect.It lost http_proxy variable in current commnad window(terminal). I must type export command in current terminal,it will come into effect.
So I want to know what's reason for this and a solution? thanks.
Running a shell script "normally" (with proxy.sh for example) results in that running in a sub-process so that it cannot affect the environment of the parent process.
Using . or source will run the shell script in the context of the current shell, so it will be able to affect the environment, using one of the following:
. proxy.sh
source proxy.sh
Another possibility (if you're using bash at least) is to create an alias to do the work for you. You can use something like:
alias faster='export http_proxy=http://192.168.0.205:1099'
so that you can then simply type faster on the command line and it will export that variable (in the context of the current shell).
You could also allow for one-shot settings such as:
alias faster='http_proxy=http://192.168.0.205:1099'
and then use:
faster your_program
which would translate into:
http_proxy=http://192.168.0.205:1099 your_program
That's a bash way to set a variable for just the one invocation of a command.
The export variable will only apply to the script -- if you want it to apply to the shell, you need to use source, and execute the script like so:
. ./proxy.sh
or:
source ./proxy.sh
Note the "." in the first example -- the dot follow by space means the script will apply to the shell.
The reason why your script does not work has been explained by Drakosha & how to make your script work has been explained by Anothony. But with the export in the script you need to source your script each time you open a new terminal. A better solution will be to add the export in .bash_profile or .bashrc
Hope this helps!
When executing a shell script a new shell is launched, the script is executed, and the shell dies. That's why you don't see the variable defined in your shell.
I suggest using an alias for the same purpose.

Resources