Perl: Sourcing a profile or bashrc or other shell script SOLVED

Everyone has worked at Perla place where they do things slightly different than what you’re used to. In this case we need to source a shell script file that houses the environment variables that we need to import. Unfortunately, the shell script file may or may not call other scripts/programs or it may use string manipulation to populate the environment variables. This means you can’t just read a the file in perl with simple key/value pairings.

In the Unix/Linux shell scripting world, if you export an environment variable it will be available in any child process.

# Here we export the variable so it will show up in Perl's %ENV hash:
export MYVAR="woohoo"

If we don’t explicitly export the environment variable, it will not be available to a child process.

# We don't export the variable so it will not show up in Perl's %ENV hash:
NOTEXPORTED_VAR="too bad"

So how do we handle the non-exported environment variables so Perl can use them? Each shell that is POSIX compliant in one way or another will have the set builtin command that will produce output of the environment variables regardless of whether they’ve been exported. Fortunately for us, it is in key/value pairs with an equals sign “=” as the delimiter. Be warned, you will get everything.

In the example code below we’re going to use the BASH shell to source the /somedir/.env file. You can replace it with the shell of your choice. Setting an environment variable with Perl’s %ENV hash will automatically export it making it available for any child processes of the Perl process.

 BEGIN {
     # you will need to include the "&& set" *IF* you have an shell file
     #  that doesn't export the variables.  
     if ( -f '/somedir/.env' && -x '/somedir/.env') {
         open(my $PS, 'bash -c ". /somedir/.env && set" |') or die 'Cannot execute bash built-in set');

         while (< $PS>) {
             # we need to strip extended ASCII characters
             #  and any lines without an "="
             if (/=/ && /[^\x20-\x7F]/) {
                 chomp;
                 my ($key, $value) = split /=/;
                 $ENV{$key} = $value;
             }
         }

         close $PS;
     }
Share Button

4 Replies to “Perl: Sourcing a profile or bashrc or other shell script SOLVED”

  1. Note that environment variables may contain newlines, in which case this code will do the wrong thing. When I did the same thing, I ended up using perl instead of set:

    %ENV = do {
        open my $pipe, '-|', bash => -c => q{source /somedir/.env; exec perl -MData::Dump=pp -e 'print pp %ENV'}
            or die "Couldn't fork bash: $!n";
        my $env = do { local $/; <$pipe> };
        eval $env;
    };

  2. There are several problems with your solution:

    It would be better to be using the three-argument open. open(my $PS, '-|', 'bash -c ". /somedir/.env && set") || die "..."

    Wouldn’t the newline in each line of input match /[^x20-x7f]/? You should put the chomp before the match, and you probably do want to include all printable characters and exclude only control characters: !/[[:cntrl:]]/

    You should skip any lines that don’t start properly: next unless /^w+=/;

    There are several lines that would be misunderstood by your script:

    BASH_VERSINFO=([0]="3" [1]="2" [2]="53" [3]="1" [4]="release" [5]="x86_64-apple-darwin13")
    BASH_VERSION='3.2.53(1)-release'
    IFS=$' tn'
    PS2='> '

    Try something like the following:


    while () {
    chomp;
    next unless /^w+=/;
    next if /[[:cntrl:]]/;
    my ( $key, $value ) = split /=/, $_, 2;
    if ( $value =~ /^(/ ) {
    # Multi-valued, skip it.
    next
    } eslif ( $value =~ /^$'(.*)'$/ ) {
    # Code to translate backslash escapes
    # into the character equivalent
    } elsif ( $value =~ /^'(.*)'$/ ) {
    # Code to translate bash strings
    # into the real strings
    }
    $ENV{$key} = $value;
    }

    There are probably other errors too.

Leave a Reply

Your email address will not be published. Required fields are marked *