File Coverage

blib/lib/Env/Modify.pm
Criterion Covered Total %
statement 141 162 87.0
branch 41 62 66.1
condition 10 18 55.5
subroutine 22 24 91.6
pod 8 8 100.0
total 222 274 81.0


line stmt bran cond sub pod time code
1             package Env::Modify;
2 7     7   113587 use strict;
  7         12  
  7         196  
3 7     7   25 use warnings;
  7         10  
  7         183  
4 7     7   23 use base 'Exporter';
  7         12  
  7         637  
5 7     7   3449 use Shell::GetEnv '0.09';
  7         180162  
  7         236  
6             #use String::ShellQuote; # nah, just borrow from String::ShellQuote
7 7     7   3572 use File::Spec::Functions 'catfile';
  7         4358  
  7         416  
8 7     7   38 use File::Temp;
  7         11  
  7         2655  
9              
10             *DEBUG = *DEVNULL; open DEVNULL,'>/dev/null';
11             our $DEBUG && (*DEBUG = *STDERR);
12             our $SHELL = 'sh';
13             our $CHDIR = 0;
14              
15             our $VERSION = '0.02';
16             our @EXPORT_OK = qw(system readpipe qx backticks source
17             readpipe_list qx_list backticks_list);
18             my @readpipe_exp = qw(readpipe readpipe_list qx_list backticks_list);
19             our %EXPORT_TAGS = ('system' => ['system'],
20             'readpipe' => [@readpipe_exp],
21             'qx' => ['qx','backticks', @readpipe_exp],
22             'backticks' => ['qx','backticks', @readpipe_exp],
23             'all' => [ 'system', 'qx','backticks', 'source',
24             @readpipe_exp],
25             'chdir' => [],
26             'sh' => [], 'bash' => [], 'csh' => [], 'dash' => [],
27             'ksh' => [], 'tcsh' => [], 'zsh' => [] );
28              
29              
30             # default attributes that go to all Shell::GetEnv constructors
31             our %CMDOPT = (
32             startup => 0,
33             login => 0,
34             interactive => 0,
35             verbose => 0,
36             );
37              
38             our %ENVSOPT = (
39             ZapDeleted => 1
40             );
41              
42             our $TEMPDIR = File::Temp::tempdir( CLEANUP => 1 );
43              
44             my $calls = 0;
45              
46             # RT115330 describes a bug with overloaded readpipe function.
47             # It was fixed in Perl v5.20.0
48             our $_RT115330 = $] < 5.020000;
49              
50             sub import {
51             # Handle the CORE::GLOBAL::readpipe bug (RT#115330) differently depending
52             # on whether the caller primarily uses qx(), `backticks`, or readpipe.
53             # The bug was fixed in Perl v5.20.0
54 7     7   87 my %tags = map {; $_ => 1 } grep /^:/, @_;
  6         19  
55 7         11 my $cgreadpipe = 0;
56 7 100 66     67 if ($tags{":qx"} || $tags{":backticks"} || $tags{":all"}) {
    100 66        
57             *CORE::GLOBAL::readpipe = sub {
58 6     6   4871 local $_RT115330 = $] < 5.020000;
59 6         24 return _readpipe_mod_env(@_);
60 2         7 };
61 2         4 $cgreadpipe = 1;
62             } elsif ($tags{":readpipe"}) {
63             # NB: CORE::GLOBAL::readpipe will not be respected for Perl <=v5.8.9
64             *CORE::GLOBAL::readpipe = sub {
65 7     7   2603 local $_RT115330 = 0;
66 7         27 return _readpipe_mod_env(@_);
67 1         6 };
68 1         2 $cgreadpipe = 1;
69             }
70              
71 7 100 66     31 if ($tags{":system"} || $tags{":all"}) {
72 7     7   33 no warnings 'once';
  7         10  
  7         892  
73 2         6 *CORE::GLOBAL::system = \&Env::Modify::system;
74             }
75 7 100       14 if ($tags{":chdir"}) {
76 1         1 $CHDIR = 1;
77             }
78 7         14 for my $shell ('zsh','tcsh','ksh','dash','csh','bash','sh') {
79 49 50       87 if ($tags{":$shell"}) {
80 0         0 $SHELL = $shell;
81             }
82             }
83 7 100       15 if ($cgreadpipe) {
84 3         374 __PACKAGE__->export_to_level(1, __PACKAGE__, @_[1..$#_]);
85 3         7 my $callpkg = caller;
86 7     7   29 no strict 'refs';
  7         20  
  7         220  
87 7     7   27 no warnings 'redefine';
  7         10  
  7         5306  
88 3         4 *{$callpkg . "::readpipe"} = \&CORE::GLOBAL::readpipe;
  3         5830  
89             } else {
90 4         322 goto &Exporter::import;
91             }
92             }
93              
94 4     4 1 1831 sub backticks {goto &readpipe }
95 3     3 1 1406 sub qx { goto &readpipe }
96              
97 0     0 1 0 sub backticks_list { goto &readpipe_list }
98 0     0 1 0 sub qx_list { goto &readpipe_list }
99              
100             sub readpipe {
101 7     7 1 16 local $_RT115330 = 0;
102 7         41 return _readpipe_mod_env(@_);
103             }
104              
105             sub readpipe_list {
106 1     1 1 9 local $_RT115330 = 0;
107 1 50       11 my $cmd = @_ > 1 ? _sh_quote(@_) : $_[0];
108 1         4 return _readpipe_mod_env($cmd);
109             }
110              
111             sub system {
112 35     35 1 45319 my @cmd = @_;
113 35 100       151 my $cmd = @cmd > 1 ? _sh_quote(@cmd) : $cmd[0];
114 35         99 $calls++;
115 35         149 my $stub = "$calls.$$";
116 35         263 my $stdout = catfile( $TEMPDIR, $stub . "-stdout");
117 35         151 my $stderr = catfile( $TEMPDIR, $stub . "-stderr");
118 35         425 my $getenv = Shell::GetEnv->new( $SHELL, $cmd,
119             { %CMDOPT, echo => 0,
120             stdout => $stdout, stderr => $stderr } );
121 26         538832 $getenv->import_envs( %ENVSOPT );
122              
123 26 100       4202 if (-s $stderr) {
124 6         114 open my $err, '>&2'; # or warn "print to unopened filehandle?"
125 6         114 open my $fh, '<', $stderr;
126 6         11 print {$err} <$fh>;
  6         116  
127 6         42 close $fh;
128 6         338 close $err;
129             }
130 26 100       301 if (-s $stdout) {
131 4         60 open my $out, '>&1'; # or warn "print to unopened filehandle?"
132 4         84 open my $fh, '<', $stdout;
133 4         7 print {$out} <$fh>;
  4         71  
134 4         23 close $fh;
135 4         592 close $out;
136             }
137 26         1791 unlink $stdout, $stderr;
138 26         119 my $status = _status($getenv->status);
139 26 100       132 $CHDIR && chdir $ENV{PWD};
140 26         606 return $? = $status;
141             }
142              
143             sub source {
144 3     3 1 3185 my ($file,@args) = @_;
145 3 50       23 if ($SHELL eq 'tcsh') {
    50          
    50          
146 0         0 $file = _sh_quote($file,@args);
147 0         0 return Env::Modify::system( "source $file" );
148             } elsif ($SHELL eq 'csh') {
149 0         0 $file = _sh_quote($file,@args);
150 0         0 return Env::Modify::system("source $file");
151             } elsif ($SHELL eq 'ksh') {
152             # . $file is supposed to work, but it doesn't
153 0         0 $file = _sh_quote($file,@args);
154 0         0 return Env::Modify::system("eval \$(cat $file)");
155             } else {
156 3         16 return Env::Modify::system( ". " . _sh_quote($file,@args) );
157             }
158             }
159              
160             sub _readpipe_mod_env {
161 21     21   42 my ($cmd) = @_;
162              
163 21 50       57 if ($_RT115330) {
164 0 0       0 if ($cmd !~ /"/) {
165 0         0 $cmd = eval qq["$cmd"];
166             } else {
167 0         0 for my $delim ('#',',', qw(! ' ; " @ $ % ^ & * - = +
168             : . / ? ~ ` 00)) {
169 0 0       0 if ($cmd eq '00') {
    0          
170 0         0 warn __PACKAGE__,"::_readpipe_mod_env: ",
171             "reinterpolation of $cmd neglected";
172             } elsif ($cmd !~ /\Q$delim/) {
173 0         0 $cmd = eval qq[qq$delim$cmd$delim];
174 0         0 last;
175             }
176             }
177             }
178             }
179 21         30 $calls++;
180 21         96 my $stub = "$calls.$$";
181 21         166 my $stdout = catfile( $TEMPDIR, $stub . "-stdout");
182 21         109 my $stderr = catfile( $TEMPDIR, $stub . "-stderr");
183 21         263 my $getenv = Shell::GetEnv->new( $SHELL, $cmd,
184             { %CMDOPT, echo => 0,
185             stdout => $stdout, stderr => $stderr } );
186 21         407106 $getenv->import_envs( %ENVSOPT );
187 21         3431 my @out;
188             {
189 21 50       46 local $/ = wantarray ? $/ : undef;
  21         152  
190 7     7   80 no warnings 'io'; # in case open $fh touches fd 1 or 2
  7         13  
  7         4217  
191 21         674 open my $fh, '<', $stdout;
192 21         433 @out = <$fh>;
193 21         121 close $fh;
194 21 100       317 if (-s $stderr) {
195 9         104 open my $err, '>&=2'; # or warn "print to unopened filehandle?"
196 9         161 open my $fh, '<', $stderr;
197 9         111 my @err = <$fh>;
198 9         23 print {$err} @err;
  9         69  
199 9         305 close $err;
200 9         83 close $fh;
201             }
202             }
203 21         1840 unlink $stdout, $stderr;
204 21 100       103 $CHDIR && chdir $ENV{PWD};
205 21         130 $? = _status($getenv->status);
206 21 50       547 return wantarray ? @out : $out[0];
207             }
208              
209             sub _status {
210 47     47   359 my $ge_status = shift;
211              
212             # http://tldp.org/LDP/abs/html/exitcodes.html
213             # treat exit code 128+n as command terminated by signal ?
214             # may not be portable to non-POSIX OS or other shells
215 47         85 my $status = $ge_status;
216 47 100       195 if ($status > 128) {
217 1         4 $status = $status - 128;
218             } else {
219 46         132 $status = $status << 8;
220             }
221             }
222              
223             sub _sh_quote { # borrowed heavily from String::ShellQuote 1.04
224 12     12   3261 my @in = @_;
225 12 50       47 return '' unless @in;
226 12         35 my ($ret, $saw_non_equal, @err) = ('', 0);
227 12         33 foreach (@in) {
228 37 50 33     179 if (!defined $_ or $_ eq '') {
229 0         0 $_ = "''";
230 0         0 next;
231             }
232             s/\x00//g &&
233 37 50       97 push @err, "No way to quote string containing null (\\000) bytes";
234 37         36 my $escape = 0;
235 37 50       62 if (/=/) {
236 0 0       0 $escape = 1 if !$saw_non_equal;
237             } else {
238 37         42 $saw_non_equal = 1;
239             }
240 37 100       94 $escape = 1 if m|[^\w!%+,\-./:=@^]|;
241 37 100 33     138 if ($escape || (!$saw_non_equal && /=/)) {
      66        
242 12         20 s/'/'\\''/g; # ' -> '\''
243              
244             # make multiple ' in a row look simpler
245             # '\'''\'''\'' -> '"'''"'
246 12         17 s|((?:'\\''){2,})|q{'"} . (q{'} x (length($1) / 4)) . q{"'}|ge;
  0         0  
247 12         24 $_ = "'$_'";
248 12         16 s/^''//;
249 12         22 s/''$//;
250             }
251             } continue {
252 37         70 $ret .= "$_ ";
253             }
254 12         20 chop $ret;
255 12 50       35 if (@err) {
256 0         0 warn __PACKAGE__ . "::sh_quote: @err";
257             }
258 12         53 return $ret;
259             }
260              
261             1;
262              
263             =head1 NAME
264              
265             Env::Modify - affect Perl %ENV from subshell
266              
267             =head1 VERSION
268              
269             0.02
270              
271             =head1 SYNOPSIS
272              
273             use Env::Modify 'system', ':readpipe';
274              
275             $ENV{FOO}="bar";
276             system('echo $FOO'); # "bar"
277             system('FOO=baz');
278             print $ENV{FOO}; # "baz"
279              
280             # on Perl <=v5.8.8, say "Env::Modify::readpipe" instead
281             $out = qx(BAR=123; export BAR; echo hello);
282             print $ENV{BAR}; # "123"
283             print $out; # "hello";
284              
285             ###
286              
287             use Env::Modify 'source', ':bash', ':chdir';
288             open ENV, '>my_env.sh';
289             print ENV "export MEANING_OF_LIFE=42\n";
290             print ENV "cd \$HOME\n";
291             print ENV "cd .cpan\n";
292             close ENV;
293             my $status = source("my_env.sh");
294             print $ENV{MEANING_OF_LIFE}; # "42"
295             print "Current dir: ",Cwd::getcwd(),"\n"; # "/home/mob/.cpan"
296              
297              
298             =head1 DESCRIPTION
299              
300             New Perl programmers are often confused about how the C call
301             interacts with the environment, and they wonder why this code:
302              
303             system('export foo=bar');
304             system('echo $foo');
305              
306             behaves differently from
307              
308             system('export foo=bar; echo $foo');
309              
310             or why when they run this code
311              
312             system("chdir \$HOME/scripts");
313             system("source my.env");
314             system("./my_script.sh");
315              
316             all the environment variables that they carefully set in C
317             are ignored by C<./my_script.sh>.
318              
319             The reason these codes do not meet the new user's expectations
320             is that subshells, such as those launched by C, receive
321             their own copy of the operating system environment and can only
322             make changes to that local environment.
323              
324             This module seeks to overcome that limitation, allowing
325             C calls (and C/C/backticks) calls
326             to affect the local environment. It uses the clever mechanism
327             in Diab Jerius's L module to copy
328             the environment of the subshell back to the calling environment.
329              
330             =head1 FUNCTIONS
331              
332             =head2 system
333              
334             =head2 EXIT_CODE = system LIST
335              
336             Acts like the builtin L command,
337             but any changes made to the subshell environment are preserved
338             and copied to the calling environment.
339              
340             Like the builtin call, the return value is the exit status of
341             the command as returned by the L call.
342              
343             When you import the C command into your calling
344             package (with C),
345             C installs the C
346             function to the C namespace (see
347             L), making it
348             available anywhere that your program makes a call to C.
349              
350             =head2 readpipe
351              
352             =head2 readpipe(EXPR)
353              
354             =head2 Env::Modify::qx(EXPR), &qx(EXPR)
355              
356             =head2 backticks(EXPR)
357              
358             Executes a system command and returns the standard output of
359             the command. In scalar context, the output comes back as a single
360             (potentially multi-line) string. In list context, returns a list
361             of lines (however lines are defined with L or
362             L).
363              
364             Unlike the builtin C command, any changes made by the
365             system command to the subshell environment are preserved and
366             copied back to the calling environment.
367              
368             When any of the functions C, C, or C
369             or any of the tags C<:readpipe>, C<:qx>, C<:backticks>, or
370             C<:all> are imported into the calling namespace,
371             this module installs the C function to
372             the C namespace. As described in
373             L, an override
374             for the C function also overrides the operators
375             C<``> and C. Note that C was not supported
376             as an overridable function for the C package
377             until Perl v5.8.9. If your version of perl is older than that,
378             you will need to use function names and not C or
379             backticks notation to get this module to modify your
380             environment.
381              
382             See the L<"RT115330"> section for another important caveat
383             about the C set of functions, and how to structure
384             your C statement to make best use of
385             this module.
386              
387             =head2 readpipe_list LIST
388              
389             =head2 backticks_list LIST
390              
391             =head2 qx_list LIST
392              
393             Convenience functions to accommodate external commands with
394             shell metacharacters. Like the L<"readpipe"> function,
395             but may take a list of arguments the way that Perl's
396             C function does. Compare:
397              
398             $output = readpipe("ls -l \"My Documents\" Videos*");
399             $output = readpipe_list("ls","-l","My Documents","Videos*");
400              
401             (See also:
402             L.)
403              
404             =head2 source FILE LIST
405              
406             Like the shell built-in command of the same name (also called
407             C<.> in some shells), executes the shell
408             commands in a file and incorporates any modifications to the
409             subshell's environment into the calling environment.
410              
411             That is, if C contains
412              
413             FOO=123
414             NUM_HOME_FILES=$(ls $HOME | wc -l)
415             export FOO NUM_HOME_FILES
416              
417             then you could run
418              
419             use Env::Modify 'source';
420             source("my_env.sh");
421             print "FOO is $ENV{FOO}\n"; # expect: "123"
422             print "There are $ENV{NUM_HOME_FILES} in the home dir\n";
423              
424             =head1 RT115330
425              
426             L<"perlbug RT#115330"|https://rt.perl.org/Ticket/Display.html?id=115330>
427             described an issue with Perl code that sets the C<*CORE::GLOBAL::readpipe>
428             function, like this module does.
429             This bug was fixed in Perl v5.20.0
430             and if you are not using an older version of Perl than that, you
431             are not affected by this bug and you may stop reading.
432              
433             The short version is that the arguments received by the
434             C function are correct when you
435             use the keyword C to call the function, and they
436             need to be interpolated an extra time when you use backticks
437             or the C construction.
438              
439             The form of the C statement should resemble the way
440             that you I invoke the C command. If your
441             code literally calls the C function, then you
442             should load this module with
443              
444             use Env::Modify ':readpipe';
445              
446             If your code usually uses the C construct or backticks
447             to invoke C, then you should load the module with
448             either
449              
450             use Env::Modify ':qx';
451             use Env::Modify ':backticks';
452              
453             (these two calls are equivalent). This will have the effect
454             of interpolating the input one last time before the input
455             is passed to the shell.
456              
457             If your code uses both C and C/backticks,
458             you can always workaround this bug using fully-qualified function
459             names like C,
460             C, or
461             C. All of these function calls
462             will receive correctly interpolated input.
463              
464             =head1 MODULE VARIABLES
465              
466             =head2 $SHELL
467              
468             The shell that the L module will use to run
469             an external command. This must be a value supported by
470             L, namely one of C, C, C,
471             C, C, C, or C.
472             Defaults to C.
473              
474             The value of C<$SHELL> can also be set by specifing a tag
475             with the shell name when this module is imported. For
476             example, to specify C as the shell for this module
477             to use, load this module like
478              
479             use Env::Modify ':bash';
480              
481             =head2 $CHDIR
482              
483             If C<$Env::Modify::CHDIR> is set to a true value, then any
484             change of the current working directory in a subshell will
485             also affect the working directory in the calling (Perl)
486             environment. That is, you can say
487              
488             chdir "/some/path";
489             $f = -f "bar"; # -f /some/path/bar
490             system("cd foo");
491             $g = -f "bar"; # -f /some/path/foo/bar
492              
493             You can also enable this feature at import time with the
494             C<:chdir> tag.
495              
496             =head2 %CMDOPT
497              
498             A set of options that are passed to the L
499             constructor. See the C method in L.
500              
501             # don't load startup files (.profile, .bashrc, etc.)
502             # when system() runs below
503             local $Env::Modify::CMDOPT{startup} = 0;
504             Env::Modify::system("FOO=bar; export FOO");
505              
506              
507             =head2 %ENVSOPT
508              
509             A set of options that are passed to the C
510             method of L. See the C method
511             in L.
512              
513             # don't remove entries from Perl environment
514             local $Env::Modify::ENVSOPT{ZapDeleted} = 0;
515             source("./script_that_erases_PATH_var.sh");
516             print "PATH is still $ENV{PATH}"; # not erased
517              
518             =head1 EXPORT
519              
520             This module has four functions that can be exported into the
521             calling namespace: C, C, C, and C.
522             As C is a Perl language construction and not just a keyword,
523             if you import the C function you would either have to use
524             a fully qualified function name or a sigil to use it:
525              
526             package My::Pkg;
527             use Env::Modify 'qx';
528             ...
529             $out1 = qx($cmd1); # calls Perl built-in, not Env::Modify::qx !
530             $out2 = &qx($cmd2); # calls Env::Modify::qx
531             $out3 = My::Pkg::qx($cmd3); # calls Env::Modify::qx
532              
533             The tag C<:system> exports the C function into the calling
534             namespace and also sets the C function, so that
535             all C calls in any package in any part of your script will
536             use the C function.
537              
538             The tags C<:readpipe>, C<:qx>, or C<:backticks> export the
539             C, C, and C functions into the calling
540             namespace, and also set the C function,
541             so that all C calls, C constructions, or
542             backticks expressions in any package and in any part of your script
543             will use the C function. If you are
544             vulnerable to L<"RT115330"> (see above), then you should use
545             C<:readpipe> if your script generally uses C to
546             capture output from external programs and use C<:qx> or C<:backticks>
547             if your script generally uses C or backticks.
548              
549             The C<:all> tag behaves like C<:system> + C<:backticks>.
550              
551             You may also specify the C<:chdir> tag to enable the "chdir"
552             feature (see C<$CHDIR> under L<"MODULE VARIABLES">), or a tag
553             with the name of a shell like C<:sh>, C<:bash>, etc.
554             to specify the default shell for this module to run external
555             commands (see C<$SHELL> under L<"MODULE VARIABLES">).
556              
557             =head1 LIMITATIONS
558              
559             =over 4
560              
561             =item Portability
562              
563             C can only work on systems where L
564             will work, namely systems where POSIX-y type shells are
565             installed.
566              
567             =item Buffering
568              
569             With a regular C or C/C/backticks call,
570             lines from the standard error stream of the external command
571             (and from the standard output stream in the case of C)
572             are written to the terminal as the external program produces
573             them. Because of the nature of how this module recovers and
574             transfers the environment of the subshell, C
575             functions will hold onto external program output, and not
576             publish it to your Perl script's terminal until the command
577             has completed. This may cause
578             L,
579             and for that, the author of this module apologizes.
580              
581             =item Interlaced standard output and standard error
582              
583             In a regular C call that writes to both standard
584             output and standard error, lines from the output stream
585             and error stream will often be interleaved on your terminal.
586             Because of the nature of how this module recovers and
587             transfers the environment of the subshell, C
588             calls will not interleave error and output this way. All
589             of the standard error output, if any, will be written to
590             the terminal (file descriptor 2, which is usually but not
591             necessarily STDERR), followed by all standard output being
592             written to the terminal (file descriptor 1).
593              
594             =back
595              
596             =head1 DEPENDENCIES
597              
598             L provides the mechanism for copying a subshell's
599             environment back into the calling environment.
600              
601             =head1 SUPPORT
602              
603             You can find documentation for this module with the perldoc command.
604              
605             perldoc Env::Modify
606              
607             You can also look for information at:
608              
609             =over 4
610              
611             =item * RT: CPAN's request tracker
612              
613             L
614              
615             =item * AnnoCPAN: Annotated CPAN documentation
616              
617             L
618              
619             =item * CPAN Ratings
620              
621             L
622              
623             =item * Search CPAN
624              
625             L
626              
627             =back
628              
629             =head1 AUTHOR
630              
631             Marty O'Brien, C<< >>
632              
633             =head1 COPYRIGHT AND LICENSE
634              
635             Copyright (c) 2016, Marty O'Brien
636              
637             This library is free software; you can redistribute it and/or modify
638             it under the same terms as Perl itself, either Perl version 5.8.8 or,
639             at your option, any later version of Perl 5 you may have available.
640              
641             See http://dev.perl.org/licenses/ for more information.
642              
643             =cut
644              
645             # TODO: open2, open3 that modifies environment
646             # test on openbsd, Cygwin, other systems