Using Bash environment variables from within a Perl script?
There are two queries here, on use of Bash variables and on running external commands.
There is the %ENV
hash in Perl, with environment variables
perl -wE'say $ENV{PWD}'
However, you are often better off getting the equivalent within the script, as things may have a subtly different meaning for the script or change as the script runs.
More importantly, using shell commands exposes you to all kinds of potential problems with quoting, shell injection, and interpretation. For instance, the command you show is dangerous, as outlined in Charles Duffy comment. It is in principle better to use Perl's rich functionality. See for example
Executing system commands safely while coding in Perl and
Using system commands in Perl instead of built in
libraries/functions
for a sober, and detailed, account of advantages.
In case you do need to run external commands, it is best to avoid the shell altogether, for example by using the multi-argument form of system. If you need the output of the command as well there are various modules in Perl that provide that. See links below.
If you also need to use the shell's capabilities, instead of quoting everything just right in order for the shell to receive what it needs better use a ready tool like String::ShellQuote.
Some examples:
How to use both pipes and prevent shell expansion in perl system function?
Perl is respecting '<' as a regular character rather an output redirection
How to pipe the content of a variable as STDIN in a qx{} statement in Perl?
Perl system command with multiple parameters output to file.
Note that qx operator (backticks) uses /bin/sh
, which may or may not get relegated to Bash. So if you want Bash you'll need system('/bin/bash', '-c', @cmd)
. See the links with examples.
Here is a full example related to the objective behind the question.
Your program's working directory may be other than expected depending on how it's started. For one, it changes after chdir
. I don't know your exact intent with PWD
, but in Perl there are core Cwd::cwd
and FindBin with $RealBin
, for the current working directory and for the directory where the script resides (generally different things).
To create a symbolic link to $path
, with the relative path following the current working directory
use warnings;
use strict;
use Cwd qw(cwd);
my $cwd = cwd;
my $path = '/first/path';
symlink($path, "$cwd/second/path") or die "Can't make a symlink: $!";
If the path is meant to be the script's location use $RealBin
from FindBin
instead of cwd
.
Note that with symlink you cannot pass a directory instead of a link name. See this page.
Perl set and get env in different bash script
The child process can inherit the parent's environment but cannot make any changes. Similarly the parent cannot have access to the child's environment as well. Hence to catch environment of the child in parent the child should print the values as shown in the bellow code. The below code will set already existing environment variables as well, but this can be optimized
# perlscript.pl
my $env_val = `. setnameenv.sh; env`;
my @env_list = split "\n", $env_str;
foreach (@env_list)
{
/([\w_]+)=(.*)/;
$ENV{$1} = $2;
}
print `. getnameenv.sh`;
find the actual explanation in this SO answer
Perl script, how to source an environment variables from .bash_profile
- 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.
How could I access shell variable from Perl script?
From bash manual:
When a program is invoked it is given an array of strings called the
environment. [...] The shell provides several ways to manipulate the
environment. On invocation, the shell scans its own environment and
creates a parameter for each name found, automatically marking it for
export to child processes. Executed commands inherit the environment.
Theexport
anddeclare -x
commands allow parameters and functions to
be added to and deleted from the environment.
So (assuming a Bash shell) using:
export LINES
will make the variable $LINES
available from within a Perl script startet from the Shell (using $ENV{LINES}
from the Perl script).
How to reload Bash environment variables inside Perl script?
#! /usr/bin/env perl
use strict;
use warnings;
BEGIN {
if (!exists( $ENV{PERL5LIB} )) {
if (!@ARGV || $ARGV[0] ne '--no-restart') {
exec('bash', '-lc', '"$@"', $0, $^X, $0, '--no-restart', @ARGV);
}
}
if (@ARGV && $ARGV[0] eq '--no-restart') {
shift(@ARGV);
}
}
...
(Obviously, using Getopt::Long or some-such would be better.)
This not only meets your spec, but provides a bypass if needed.
That said, the whole concept is flawed. Most people shouldn't be using PERL5LIB, and it's not your job to fix badly configured systems (those that need PERL5LIB but don't have it set when it should be set). Instead, fix your environment.
using environment variable in the perl script
$GATE
in a double quoted string will be considered a Perl variable. If you want to use an environment variable, you can use the %ENV
hash:
system ("bsub xyz +OPTIONS_GATE=$ENV{GATE}")
Alternatively, you can escape the dollar sign so Perl does not treat $GATE
as a Perl variable:
system ("bsub xyz +OPTIONS_GATE=\$GATE")
Or use a single quoted string, which does not interpolate variables:
system ('bsub xyz +OPTIONS_GATE=$GATE')
Note that if you had used
use strict;
use warnings;
It would have told you about this error. strict
would have said:
Global symbol "$GATE" requires explicit package name
Execution of script.pl aborted due to compilation errors.
And warnings
would have said:
Name "main::GATE" used only once: possible typo at script.pl line 12.
Use of uninitialized value $GATE in string at script.pl line 12.
When you do not use use strict; use warnings;
your errors are not removed, they are only hidden from you, so that they are harder to find. Therefore, always use these two pragmas.
how to source a shell script [environment variables] in perl script without forking a subshell?
Child environments cannot change parent environments. Your best bet is to parse env.sh
from inside the Perl code and set the variables in %ENV
:
#!/usr/bin/perl
use strict;
use warnings;
sub source {
my $name = shift;
open my $fh, "<", $name
or die "could not open $name: $!";
while (<$fh>) {
chomp;
my ($k, $v) = split /=/, $_, 2;
$v =~ s/^(['"])(.*)\1/$2/; #' fix highlighter
$v =~ s/\$([a-zA-Z]\w*)/$ENV{$1}/g;
$v =~ s/`(.*?)`/`$1`/ge; #dangerous
$ENV{$k} = $v;
}
}
source "env.sh";
for my $k (qw/foo bar baz quux/) {
print "$k => $ENV{$k}\n";
}
Given
foo=5
bar=10
baz="$foo$bar"
quux=`date +%Y%m%d`
it prints
foo => 5
bar => 10
baz => 510
quux => 20110726
The code can only handle simple files (for instance, it doesn't handle if
statements or foo=$(date)
). If you need something more complex, then writing a wrapper for your Perl script that sources env.sh
first is the right way to go (it is also probably the right way to go in the first place).
Another reason to source env.sh
before executing the Perl script is that setting the environment variables in Perl may happen too late for modules that are expecting to see them.
In the file foo
:
#!/bin/bash
source env.sh
exec foo.real
where foo.real is your Perl script.
Related Topics
How to Use Code Completion into Eclipse with Opencv
How to Solve "Server Terminated Early with Status 127" When Running Node.Js on Linux
Using a Glob Expression Passed as a Bash Script Argument
Stty Serial Port Settings for Parity Not Persistent
How to List One Filename Per Output Line in Linux
Merge Multiple Jpgs into Single PDF in Linux
How Does Ngrok Work Behind a Firewall
Append Text to File from Command Line Without Using Io Redirection
Difference Between Two .Tar.Gz File Lists on Linux
How to Automate the Installation of Eclipse Plugins with Command Line
Qimage to Cv::Mat Convertion Strange Behaviour
Python Socket.Error: [Errno 13] Permission Denied
Have One Folder with Files That Have the Same Name But Different File
How to Parse Command Line Options in Bash Shell
Systemd with Multiple Execstart
What's the Difference Between "Env" and "Set" (On MAC Os X or Linux)