http://www.perlmonks.org?node_id=931273

akrrs7 has asked for the wisdom of the Perl Monks concerning the following question:

This is my dos batch script:
@echo off @set SCRIPTDIR=%~dp0 @for /f %%I in ("%SCRIPTDIR%\.") do @set BingApps=%%~dpI @for /f %%I in ("%BingApps%\.") do @set BingAppsHome=%%~dpI @for /f %%I in ("%BingAppsHome%") do @set myDRIVE=%%~dI @set SCRIPTDIR=%SCRIPTDIR:~0,-1% set SCRIPTDIR set BingApps @for /f %%I in ("%BingAppsHome%") do @set BingTcEnv=%%~nI @set TEMP=%DRIVE%\temp @set THIS_HOME=%DRIVE%\thisHome @set thisRoot=%BingAppsHome%\thisRoot @set thisData=%BingAppsHome%\thisData set thisData echo doneDosTest
This is my perl script
use strict; use warnings; use File::Find; use File::Spec; use File::Path; use File::Copy; use File::Basename; use Cwd; my $scriptDir = cwd; #------------------------------------------ my $operatingSystem = $^O; my $windows = "MSWin32"; my $unix = "hpux"; print "Running under ${operatingSystem} environment.\n\n"; my $pathSeparator = ""; if ( $operatingSystem eq $windows ) { $pathSeparator = "\\"; } else { $pathSeparator = "/"; } #------------------------------------------ #execute the environment variables script if ( $operatingSystem eq $windows ) { system("$scriptDir/dosTest.bat"); } else { system("$scriptDir/unixTest.sh"); } #---------------------------------------------- $ENV{'ksENV'} = "c:/temp"; $ENV{'script_dir'} = "$ENV{'ksENV'}/bin"; my $someFile = "$ENV{'ksEnv'}/bin/someFile.txt"; open theFile, $someFile or die $!; while (my $record = <theFile>) { print $record; } close(theFile); print "\n$ENV{script_dir}"; print "\n$someFile"; print "\n$ENV{THIS_HOME}"; print "\n$ENV{thisRoot}";
When the perl script is executed the final two print statements for printing the environment variables set by the dos script return this: Use of uninitialized value $ENV{"THIS_HOME"} in concatenation (.) or string at perltest.pl line 50.... The 'set' statements in the dos script execute fine..they return the value of the environment variables that I expect to see. I would've expected the environment variables set by the dos script to stay put in the cmd window - but they don't. Please advise on what I could be doing wrong here. Thanks...

Replies are listed 'Best First'.
Re: Issue with env variables set through dos batch
by ikegami (Patriarch) on Oct 13, 2011 at 16:55 UTC

    Batch files need a shell to execute them, so Windows will launch a new cmd to execute the batch file when you do system("$scriptDir/dosTest.bat");. Similarly, executing the shell script in linux will launch the shell listed in the #! line. The batch file or shell script is then executed by this shell.

    The batch file or shell script then proceeds to set the shells environment variables, which works.

    Then the shell exits and Perl continues to execute. Nowhere did you change Perl's environment. Nor is it possible to do so.

    At least not directly. The trick is to have the batch file or shell script tell your Perl script how to modify its environment, then have your Perl script obey the request.

    :: test.bat @echo off echo FOO=foo echo BAR=bar
    for (split /\n/, `test.bat`) { my ($k, $v) = split /=/, $_, 2; $ENV{$k} = $v; }

    I'm guessing you can't run modify the batch file. If so, you can use the following strategy instead:

    :: wrapper.bat @echo off call existing.bat set
    :: existing.bat @echo off set FOO=foo set BAR=bar
    for (split /\n/, `wrapper.bat`) { my ($k, $v) = split /=/, $_, 2; $ENV{$k} = $v; }
      Hello Ikegami... Yes..I cannot modify the batch file. It is as it is and I was thinking of doing one of 2 things: 1. Small batch/shell script that first calls the env var batch/shell script and then my perl script - this would ensure that the env variables are set before my perl script is executed. 2. Use the File Open in Perl to read the batch/shell script and execute each line like a perl command....not sure if do-able ?

        You forgot 3. Use the solution I provided.

        (1) is the simplest.

        What you described in #1 should work. This is very similar to what happens with the portable version of Strawberry Perl.

        In the case of portable Strawberry Perl, there's batch script that you run first and it does two things: opens a command prompt window and sets the environment variables for just that command prompt window. From there, you can run your scripts and Windows will know where to find all of the Perl files (perl.exe, "installed" modules, etc.) for the portable Strawberry Perl.

        Also, I haven't tested it, but ikegami's suggestion looks like it should work too.

      what will happen in case batch file have values like : %VAL%=<some path>:%VAL% i you read it as a key value pair, then the values that get appended are lost

        what will happen in case...

        It should take all of 10 seconds to find out :) Try It To See

        i[f] you read it as a key value pair, then the values that get appended are lost

        Not at all. The code line:

        my ($k, $v) = split /=/, $_, 2;

        splits each data line into two parts: the part to the left of the first = character — which is assigned to $k — and the entire part to the right of the first = character — which is assigned to $v. So nothing is lost. In your example, the batch file would contain:

        set VAL=<some path>:%VAL%

        and if VAL already contained, say, /bin, the result would be the assignment of VAL to $k and of <some path>:/bin to $v. See split:

        If LIMIT is specified and positive, it represents the maximum number of fields into which the EXPR may be split; in other words, LIMIT is one greater than the maximum number of times EXPR may be split.

        Hope that helps,

        Athanasius <°(((><contra mundum

Re: Issue with env variables set through dos batch
by moritz (Cardinal) on Oct 13, 2011 at 15:44 UTC

    I'm not really familiar with DOS batch files, but on linux a shell script (the equivalent to a batch file) is run in a new process. And when the process exits, its environment variables vanish with it.

    There you need to launch the perl script that is supposed to see the environment variables from within the shell script, so that it is a subprocess of the shell which holds the interesting environment variables.

    To check if that's the case in DOS/Windows too, simply inspect the environment variables on the dos prompt after you ran the batch file.

      Very true...that is exactly the behavior I'm observing in the windows cmd window. How do I get the env. variables to stay put ? Running the perl script from a batch script is not right now an option for me.
        In general if you set ENV variables in a shell and then launch a program from that shell's environment, the program will inherit the environment variables.

        I you want some ENV variables to apply to any command line window, Go to Control Panel|System|Environment Variables, change them and then set them "to apply to all future command line windows". This is for example, how you would set "PATH" to be persistent.

        If this is a "one off" thing, then I would pass special settings as part of @ARG.

      While environment variables are inherited from parent to child like in Linux, a batch files are run more like source foo.bat than cmd /c foo.bat when possible (when the parent is cmd).
      There is no DOS anymore.

      The Windows command prompt is like one of the ~unix shells. for example:

      #------------------------------------------ my $operatingSystem = $^O; my $windows = "MSWin32"; my $unix = "hpux"; print "Running under ${operatingSystem} environment.\n\n"; my $pathSeparator = ""; if ( $operatingSystem eq $windows ) { $pathSeparator = "\\"; } else { $pathSeparator = "/"; } #------------------------------------------
      is not needed!

      pathSeparator of "/" works on Windows and ~Unix (LINUX, etc..)

      Where it really matters, the logic above to determine the OS is woefully inadequate.

        I really appreciate your input..however it does not answer my question on the environment variables. I'm not really concerned about the part of the perl script with the check for the os...I'm not using that $pathSeparator variable anywhere.
Re: Issue with env variables set through dos batch
by Marshall (Canon) on Oct 13, 2011 at 15:41 UTC
    I see some problems with your scripts.
    1) DOS lingo is ~ a do-do bird. - forget it!
    2) The Windows command line is not DOS.
    3) In almost all cases, forward slash, '/' works like '\'.

      In almost all cases, forward slash, '/' works like '\'.

      That's pushing it. It's true in Perl (since the Windows API accepts "/"), but not so much in the shell (because "/" is the command line option starter, and it doesn't need to be preceded by whitespace). For example, dir /users won't work.

      That said, if you place the path in quotes, "/" is much more likely to get accepted. For example, dir "/users" does work.

      This is only relevant if $pathSeparator is used to build shell commands, which is possible. It's hard to tell, since $pathSeparator isn't actually used in the posted snippet.

        Yes, there are issues with \ vs / in Windows. I think that there are things like '\' is needed on a path to an .exe.

        In Perl code, using "/" in a Perl function almost always or at least I don't know of a case where it does not. But YES, there are cases where Windows itself is pretty finicky about this. Yes, I would quote a path, like "/file", because there are even more lexical pitfalls like "divided" by file!

        Without spending hours thinking about it, I cannot cover every single case. My method is simplistic, I write something that "feels right" and if it is wrong, I try again and then verify that the new formulation is "right". It just seems like when I think I've learned everything about it, I get bit with a new wrinkle when dealing with this \ and / Windows command line stuff! I think that is just the nature of the beast.

        Anyway, I post my code and learn things. Thanks!

      The batch script run by itself from the cmd line works fine and sets the environment variables as expected. Even from within the Perl script - the set statements in the batch script output the said environment variables. The issue is not with the batch script. I am wondering why the last two print statements in the Perl script give me the error they do - or how do I make the environment variables stay put in the cmd window ? It seems that once the batch script is run (from within the perl script) - it wipes out the environment variables that are set and are not even recognized from within the same perl script...if that is the case how can I use those environment variables in the perl script ?
        If you start a script, it gets the environment at the time. If that script fiddles with an environment variable, that is only a temporary setting for the duration that that script is running.

        If you set an environment variable before a script is started, then the script inherits that value when it runs. Back up another level, when Windows boots, certain values are set in the environment and all processes inherit those values.

Re: Issue with env variables set through dos batch
by bart (Canon) on Nov 06, 2012 at 12:14 UTC
    You can't set environment variables set in a program that you call using system (or backticks), and expect them to persist into the parent process. It just doesn't work that way. You'll have to find another way to store them somewhere; or pass them back to the parent (perl).

    You can run a perl script in the child process, which can for example use DATA::Dumper or JSON to store multiple environment variables from %ENV, reliably, and either save that into a temporary file where your parent perl script can read it in and parse it easily; or output it on STDOUT so processing the data via backticks is possible. Hell, you can probably just simply use set as the last command in the batch script to get a complete list of environment variables.

    BTW as $scriptDir is set to cwd it is unnecessary to use it in your call to system; though in Unix you will have to use "./" instead. And you can always use catfile from File::Spec or File::Spec::Functions to join parts from file paths portably.