NAME

Env::Modify - affect Perl %ENV from subshell

VERSION

0.01

SYNOPSIS

use Env::Modify 'system', ':readpipe';

$ENV{FOO}="bar";
system('echo $FOO');   #  "bar"
system('FOO=baz');
print $ENV{FOO};       #  "baz"

# on Perl <=v5.8.8, say "Env::Modify::readpipe" instead
$out = qx(BAR=123; export BAR; echo hello);
print $ENV{BAR};       #  "123"
print $out;            #  "hello";

###

use Env::Modify 'source', ':bash', ':chdir';
open ENV, '>my_env.sh';
print ENV "export MEANING_OF_LIFE=42\n";
print ENV "cd \$HOME\n";
print ENV "cd .cpan\n";
close ENV;
my $status = source("my_env.sh");
print $ENV{MEANING_OF_LIFE};               # "42"
print "Current dir: ",Cwd::getcwd(),"\n";  # "/home/mob/.cpan"

DESCRIPTION

New Perl programmers are often confused about how the system call interacts with the environment, and they wonder why this code:

system('export foo=bar');
system('echo $foo');

behaves differently from

system('export foo=bar; echo $foo');

or why when they run this code

system("chdir \$HOME/scripts");
system("source my.env");
system("./my_script.sh");

all the environment variables that they carefully set in my.env are ignored by ./my_script.sh.

The reason these codes do not meet the new user's expectations is that subshells, such as those launched by system, receive their own copy of the operating system environment and can only make changes to that local environment.

This module seeks to overcome that limitation, allowing system calls (and qx()/readpipe/backticks) calls to affect the local environment. It uses the clever mechanism in Diab Jerius's Shell::GetEnv module to copy the environment of the subshell back to the calling environment.

FUNCTIONS

system

EXIT_CODE = system LIST

Acts like the builtin "system" in perlfunc command, but any changes made to the subshell environment are preserved and copied to the calling environment.

Like the builtin call, the return value is the exit status of the command as returned by the "wait" in perlfunc call.

When you import the system command into your calling package (with use Env::Modify ':system'), Env::Modify installs the Env::Modify::system function to the CORE::GLOBAL namespace (see "Overriding Build-in Functions" in perlsub), making it available anywhere that your program makes a call to system.

readpipe

readpipe(EXPR)

Env::Modify::qx(EXPR), &qx(EXPR)

backticks(EXPR)

Executes a system command and returns the standard output of the command. In scalar context, the output comes back as a single (potentially multi-line) string. In list context, returns a list of lines (however lines are defined with "$/" in perlvar or "$INPUT_RECORD_SEPARATOR" in perlvar).

Unlike the builtin readpipe command, any changes made by the system command to the subshell environment are preserved and copied back to the calling environment.

When any of the functions readpipe, qx, or backticks or any of the tags :readpipe, :qx, :backticks, or :all are imported into the calling namespace, this module installs the Env::Modify::readpipe function to the CORE::GLOBAL namespace. As described in "Overriding Built-in Functions" in perlsub, an override for the readpipe function also overrides the operators `` and qx{}. Note that readpipe was not supported as an overridable function for the CORE::GLOBAL package until Perl v5.8.9. If your version of perl is older than that, you will need to use function names and not qx[] or backticks notation to get this module to modify your environment.

See the "RT115330" section for another important caveat about the readpipe set of functions, and how to structure your use Env::Modify ... statement to make best use of this module.

readpipe_list LIST

backticks_list LIST

qx_list LIST

Convenience functions to accommodate external commands with shell metacharacters. Like the "readpipe" function, but may take a list of arguments the way that Perl's system LIST function does. Compare:

$output = readpipe("ls -l \"My Documents\" Videos*");
$output = readpipe_list("ls","-l","My Documents","Videos*");

(See also: https://metacpan.org/pod/distribution/perl/Porting/todo.pod#readpipe-LIST.)

source FILE LIST

Like the shell built-in command of the same name (also called . in some shells), executes the shell commands in a file and incorporates any modifications to the subshell's environment into the calling environment.

That is, if my_env.sh contains

FOO=123
NUM_HOME_FILES=$(ls $HOME | wc -l)
export FOO NUM_HOME_FILES

then you could run

use Env::Modify 'source';
source("my_env.sh");
print "FOO is $ENV{FOO}\n";  # expect: "123"
print "There are $ENV{NUM_HOME_FILES} in the home dir\n";

RT115330

"perlbug RT#115330" described an issue with Perl code that sets the *CORE::GLOBAL::readpipe function, like this module does. This bug was fixed in Perl v5.20.0 and if you are not using an older version of Perl than that, you are not affected by this bug and you may stop reading.

The short version is that the arguments received by the CORE::GLOBAL::readpipe function are correct when you use the keyword readpipe to call the function, and they need to be interpolated an extra time when you use backticks or the qx// construction.

The form of the use statement should resemble the way that you usually invoke the readpipe command. If your code literally calls the readpipe function, then you should load this module with

use Env::Modify ':readpipe';

If your code usually uses the qx!! construct or backticks to invoke readpipe, then you should load the module with either

use Env::Modify ':qx';
use Env::Modify ':backticks';

(these two calls are equivalent). This will have the effect of interpolating the input one last time before the input is passed to the shell.

If your code uses both readpipe and qx{}/backticks, you can always workaround this bug using fully-qualified function names like Env::Modify::readpipe(), Env::Modify::qx(), or Env::Modify::backticks(). All of these function calls will receive correctly interpolated input.

MODULE VARIABLES

$SHELL

The shell that the Shell::GetEnv module will use to run an external command. This must be a value supported by Shell::GetEnv, namely one of bash, csh, dash, ksh, sh, tcsh, or zsh. Defaults to sh.

The value of $SHELL can also be set by specifing a tag with the shell name when this module is imported. For example, to specify bash as the shell for this module to use, load this module like

use Env::Modify ':bash';

$CHDIR

If $Env::Modify::CHDIR is set to a true value, then any change of the current working directory in a subshell will also affect the working directory in the calling (Perl) environment. That is, you can say

chdir "/some/path";
$f = -f "bar";   # -f  /some/path/bar
system("cd foo");
$g = -f "bar";   # -f  /some/path/foo/bar

You can also enable this feature at import time with the :chdir tag.

%CMDOPT

A set of options that are passed to the Shell::GetEnv constructor. See the new method in "METHODS" in Shell::GetEnv.

# don't load startup files (.profile, .bashrc, etc.)
# when system() runs below
local $Env::Modify::CMDOPT{startup} = 0;
Env::Modify::system("FOO=bar; export FOO");

%ENVSOPT

A set of options that are passed to the import_envs method of Shell::GetEnv. See the import_envs method in "METHODS" in Shell::GetEnv.

# don't remove entries from Perl environment
local $Env::Modify::ENVSOPT{ZapDeleted} = 0;
source("./script_that_erases_PATH_var.sh");
print "PATH is still $ENV{PATH}";  # not erased

EXPORT

This module has four functions that can be exported into the calling namespace: system, readpipe, qx, and backticks. As qx is a Perl language construction and not just a keyword, if you import the qx function you would either have to use a fully qualified function name or a sigil to use it:

package My::Pkg;
use Env::Modify 'qx';
...
$out1 = qx($cmd1);          # calls Perl built-in, not Env::Modify::qx !
$out2 = &qx($cmd2);         # calls Env::Modify::qx
$out3 = My::Pkg::qx($cmd3); # calls Env::Modify::qx

The tag :system exports the system function into the calling namespace and also sets the CORE::GLOBAL::system function, so that all system calls in any package in any part of your script will use the Env::Modify::system function.

The tags :readpipe, :qx, or :backticks export the readpipe, qx, and backticks functions into the calling namespace, and also set the CORE::GLOBAL::readpipe function, so that all readpipe calls, qx// constructions, or backticks expressions in any package and in any part of your script will use the Env::Modify::readpipe function. If you are vulnerable to "RT115330" (see above), then you should use :readpipe if your script generally uses readpipe() to capture output from external programs and use :qx or :backticks if your script generally uses qx!! or backticks.

The :all tag behaves like :system + :backticks.

You may also specify the :chdir tag to enable the "chdir" feature (see $CHDIR under "MODULE VARIABLES"), or a tag with the name of a shell like :sh, :bash, etc. to specify the default shell for this module to run external commands (see $SHELL under "MODULE VARIABLES").

LIMITATIONS

Portability

Env::Modify can only work on systems where Shell::GetEnv will work, namely systems where POSIX-y type shells are installed.

Buffering

With a regular system or readpipe/qx/backticks call, lines from the standard error stream of the external command (and from the standard output stream in the case of system) are written to the terminal as the external program produces them. Because of the nature of how this module recovers and transfers the environment of the subshell, Env::Modify functions will hold onto external program output, and not publish it to your Perl script's terminal until the command has completed. This may cause suffering from buffering, and for that, the author of this module apologizes.

Interlaced standard output and standard error

In a regular system call that writes to both standard output and standard error, lines from the output stream and error stream will often be interleaved on your terminal. Because of the nature of how this module recovers and transfers the environment of the subshell, Env::Modify::system calls will not interleave error and output this way. All of the standard error output, if any, will be written to the terminal (file descriptor 2, which is usually but not necessarily STDERR), followed by all standard output being written to the terminal (file descriptor 1).

DEPENDENCIES

Shell::GetEnv provides the mechanism for copying a subshell's environment back into the calling environment.

SUPPORT

You can find documentation for this module with the perldoc command.

perldoc Env::Modify

You can also look for information at:

AUTHOR

Marty O'Brien, <mob at cpan.org>

COPYRIGHT AND LICENSE

Copyright (c) 2016, Marty O'Brien

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.8 or, at your option, any later version of Perl 5 you may have available.

See http://dev.perl.org/licenses/ for more information.