Export/SET environment variables in windows through a shell script - powershell

There is a script.sh file
set FABRIC_CFG_PATH=<some path>
set CORE_PEER_LOCALMSPID=<some id>
If I'm running this script in windows, the env variables are not getting set.
Whereas if setting the env using the cmd approach,
E.g., on windows cmd
set FABRIC_CFG_PATH=<some path>
It works fine.
So how can I set the env in windows through a shell script file?

Since your intent is to define current-process-only environment variables (rather than persistently defined ones, which on Windows are stored in the registry) you need to use a script file / batch file that runs in-process in order for environment variables defined therein to be seen by the script's caller.
Therefore:
If the caller is a cmd.exe session, you must use a batch file: a plain-text file with filename extension .cmd (or, less preferably, .bat[1]) that uses cmd.exe syntax.
If the caller is a PowerShell session, you must use a PowerShell script: a plain-text file with filename extension .ps1 that uses PowerShell syntax.
Note: While you can call a .cmd file (batch file) from PowerShell too (but not directly vice versa), this will not work as intended, because of necessity it runs in a (cmd.exe) child process, whose environment variables aren't seen by the PowerShell caller.
As for .sh files: they have no predefined meaning on Windows, but may be defined by third-party applications, such as Git Bash. In the case of the latter, invoking a .sh file passes it to the POSIX-compatible Bash shell, which has its own syntax. More importantly, invoking such a file won't work as intended when called from either cmd.exe or PowerShell, because Bash must run in a child process, and child processes cannot set environment variables for their parents.
cmd.exe / batch-file example:
Create a file named envVars.cmd, for instance, and place the following lines in it:
#echo off
:: Note: Do NOT use `setlocal` here
set "FABRIC_CFG_PATH=C:\path\to\some directory\config"
set "CORE_PEER_LOCALMSPID=42"
Then, from your cmd.exe session / another batch file, call the file as follows to make the environment variable-definitions take effect for the current process (assuming the file is in the current directory):
.\envVars.cmd
You will then able to refer to the newly defined variables as %FABRIC_CFG_PATH% and %CORE_PEER_LOCALMSPID%.
PowerShell example:
Create a file named envVars.ps1, for instance, and place the following lines in it:
$env:FABRIC_CFG_PATH='C:\path\to\some directory\config'
$env:CORE_PEER_LOCALMSPID=42
Then, from a PowerShell session / another PowerShell script, call the file as follows to make the environment variable-definitions take effect for the current process (assuming the file is in the current directory):
./envVars.ps1
You will then able to refer to the newly defined variables as $env:FABRIC_CFG_PATH and $env:CORE_PEER_LOCALMSPID.
[1] See this answer.

After some study on the executables/batch files in windows, I have come to the conclusion that I need to write a batch .bat file to use the set command to set the env variables as I desire.

Related

Powershell environment variable

In a Powershell script (.ps1)
Launched from the command line of a console (cmd.exe)
How can set and environment variable in the console,
so that when the Powershell script ends processing,
and exits to the console where was invoked...
the environment variable exists,
and can be read by a batch file,
or viewed with the SET command ?
do not want to set a 'Machine' or a 'User' variable...
only a console process variable...
the same variable you get if you use SET in the console
To run a PowerShell script from cmd.exe invariably requires a (powershell.exe / pwsh.exe) child process, and child processes fundamentally cannot set environment variables for their parent process[1].
Your best bet is to have your *.ps1 file output the name and value of the desired environment variable and then have the calling cmd.exe process create it, based on that output.
Security note: Blindly defining environment variables based on the name-value pairs output by another command (a *.ps1 script, in your case) should only be done if you trust that command not to output malicious definitions.
Here's a simple example (run directly from an interactive cmd.exe session):
for /f "delims== tokens=1,*" %v in ('powershell.exe -c "'FOO=bar'"') do #set "%v=%w"
The above defines environment variable %FOO% with value bar, based on the PowerShell command outputting the literal name-value pair FOO=bar.
Verify with echo %FOO%.
To extend this approach to defining multiple environment variables, make the command output each definition on its own line (which in PowerShell you can achieve by outputting an array of strings):
for /f "delims== tokens=1,*" %v in ('powershell.exe -c "'FOO=bar', 'BAZ=bam'"') do #set "%v=%w"
The above additionally defines %BAZ% with value bam.
To make this more convenient, I suggest creating a wrapper batch file (*.cmd) that performs the above:
Note that you'll have to use %%v and %%w instead of %v and %w there.
Instead of -c (for -Command) with the demo command, use -File with the path to your *.ps1 file to invoke it.
Also consider use of -NoProfile as well, to bypass loading of your PowerShell environment's $PROFILE file, which not only slows things down, but may pollute your command's output.
[1] As LotPings points out, child processes inherit copies of the parent process' environment variables. Modifications of these copies are never seen by the parent. A child process is fundamentally unable to modify its parent's environment, which is a restriction at the OS level - for good reasons: Modifying a running process' environment by an arbitrary (child) process would be a serious security concern.

Set environment variables by batch file called from perl script

Let's consider the following perl script:
#!/usr/bin/perl
system("C:/Program Files (x86)/Microsoft Visual Studio/2017/Enterprise/Common7/Tools/VsDevCmd.bat");
system("msbuild");
The batch file invoked with the first system call is supposed to set up some environment variables so that the msbuild executable in the second system call can be found.
When I run this perl script I get the following error:
'msbuild' is not recognized as an internal or external command,
operable program or batch file.
So it looks like the environment variables set in the batch file are not made available to the context of the perl script. What can I do to make this work?
Note 1
Running first the batch file from a console window and then running msbuild works fine. So the batch file works as expected and msbuild is actually available.
Note 2
My real-world perl script is much longer. This example here is a massive simplification which allows to reproduce the problem. So I cannot easily replace the perl script with a batch file, for example.
Note 3
The funny thing is: I've been using this perl script for one or two years without any problems. Then suddenly it stopped working.
Your process has an associated environment which contains things like the search path.
When a sub-process starts, the new process has a new, separate, environment which starts as a copy of the parent process's environment.
Any process (including sub-processes) can change their own environment. They cannot, however, change their parent's process's environment.
Running system() creates a new environment.
So when you call system() to set up your environment, it starts a new sub-process with a new environment. Your batch program then changes this new environment. But then the sub-process exits and its environment ceases to exist - taking all of the changes with it.
You need to run the batch file in a parent process, before running your Perl program.

Create/Destroy New Environment in PowerShell Script

I'm trying to find a method whereby a PowerShell script can be executed without the environment "leaking" back-and-forth between the script and other scripts/the console.
I.e. if I set an environment variable before I call the script, I don't want the script to see that environment variable, and if I set an environment variable in the script, I don't want to be able to see it from the console afterwards.
I'm building PowerShell build/compile script that invokes vcvarsall.bat (to get all the Visual C++ 2010 variables etc. into the environment), but I don't want the environment that vcvarsall.bat creates "leaking" back onto the console -- I want to start fresh each time without closing/re-opening the PowerShell window.
You could call the script using:
powershell.exe -file <PS1 File>
Current variables will not be available in the PS1 script and variables created in the PS1 script will not be available after it exits.

Disable powershell expansion of command's extension?

We have a lot of existing batch files that help with the running of different perl and ruby scripts.
A batch file (e.g. test.bat) would normally be invoked like:
$ test
and within the batch file, it will set some settings and finally try to run the corresponding script file (e.g. test.pl) like this:
perl -S "%0.pl" %*
All works with cmd.exe, but today, I decided to switch to PowerShell and found out that it expands the commands. So trying to run "test" will actually run "Full\path\test.bat" and my script would complain that there is no file test.bat.pl.
Is there a way to prevent this command expansion? Rewriting all batch files is not an option.
One way is to call cmd explicitly:
cmd /c test

In vbscript, how do I run a batch file or command, with the environment of the current cmd prompt window?

In vbscript, how do I run a batch file or command, in the current cmd prompt window,
without starting a new process.
For example. According to script56.chm (the vbscript help apparently)
Windows Script Host
Run Method (Windows Script Host)
"Runs a program in a new process"
So if I have code that uses that e.g. a VBS file, and a BAT file.
An environment variable g has the value abc g=abc
from that command window,
The VBS file calls the BAT file with windows scripting host Run.
The bat process sets g=z. and finishes.. and the vbs process finishes.
The environment variable is left untouched as g=abc.
I know
CreateObject("Wscript.Shell").Run "c:\test.bat", 0
starts a new window as is clear when using 1 instead of 0. (since 0 hides the window)
How do I
-run the bat file from the vbs, in the same cmd environment that the vbs was called in, so changes affect the cmd environment it was called in?
-In the two windows case which this one is at the moment, how do I access the environment of the parent cmd window, from the batch file?
how do I run a batch file or command, in the current cmd prompt window, without starting a new process?
I don't think you can; your vbscript runs under a script host engine (such as cscript.exe or wscript.exe), and batch files are interpreted by the command interpreter (typically cmd.exe). Both are separate executables and neither is, to my knowledge, available as an in-process library, so you cannot interpret .vbs and .cmd files within the same process. I also highly doubt that the script host engine that is running your VBScript also could run the batch file in its parent cmd.exe - I don't think you can 'inject' a new batch file into a running cmd.exe.
how do I access the environment of the parent cmd window, from the batch file?
Not just access, but change - MSDN's "Changing Environment Variables" is quite explicit on this: "Altering the environment variables of a child process during process creation is the only way one process can directly change the environment variables of another process. A process can never directly change the environment variables of another process that is not a child of that process." You are trying to change the environment of the parent, not child, process. (I do wonder what 'directly' means in the context of this quote, though).
I would guess that the reason for this is security; imagine the havoc that could be wreaked if arbitrary processes could (maliciously or accidentally) change the PATH (or COMSPEC) environment variable of a running process, such as your vbscript host engine process - it could fail to launch your bat file entirely, breaking your program.
It would seem that you're out of luck - however, there are lots of other mechanisms for passing information between processes. Here are a couple of suggestions that are fairly simple to implement when talking between a batch file & vbscript, although it's by no means exhaustive:
Exit codes
Writing to & Parsing the consoleoutput (stdout) or a temp file
If you absolutely need to set environment variables in the parent cmd.exe (and also absolutely need the intermediate step of a vbscript), then you may have to write a wrapper batch file which runs the vbscript, consumes information produced by it and then sets environment variables; because the wrapper cmd is executing in the top-level cmd process, it will be able to change the env vars there.
Footnote: Note that you can change the permanent system/user environment variables (as opposed to process environment variables) from within a VBScript, but I wouldn't recommend this if you are trying to create a transient state; besides this won't affect already-running processes (like the parent cmd.exe) anyway.