Reputation: 363
I have two batch scripts I am trying to run using c#, and then see how they affect the environment.
The code I have is
string finalEnvVarsFile = Path.GetTempFileName();
string arguments = string.Format("/c {0} & {1} & set > {2}", "foo.bat", "bar.bat", finalEnvVarsFile);
// Helper to run processes easier and reads in stdout and stderr.
ProcessHelper processHelper = ProcessHelper.Create("cmd.exe", arguments);
bool success = processHelper.Run(null, true, true);
foreach (var envVar in File.ReadAllLines(finalEnvVarsFile))
{
Console.WriteLine("Environment Variable: " + envVar);
}
The environment variables I get seem to be what the process had originally, not what the batch files are setting. Am I doing something wrong, or are the bat file not running correctly for some reason? Is there a better way to do this?
Upvotes: 1
Views: 4680
Reputation: 5243
Here is complete example with support of environment variables as well as support for no hang if child process is waiting for stdout or stderr pipes to read data.
/// <summary>
/// Executes command
/// </summary>
/// <param name="cmd">command to be executed</param>
/// <param name="output">output which application produced</param>
/// <param name="transferEnvVars">true - if retain PATH environment variable from executed command</param>
/// <returns>true if process exited with code 0</returns>
static bool ExecCmd(string cmd, out String output, bool transferEnvVars = false)
{
ProcessStartInfo processInfo;
Process process;
if (transferEnvVars)
cmd = cmd + " && echo --VARS-- && set";
processInfo = new ProcessStartInfo("cmd.exe", "/c " + cmd);
processInfo.CreateNoWindow = true;
processInfo.UseShellExecute = false;
processInfo.RedirectStandardError = true;
processInfo.RedirectStandardOutput = true;
process = Process.Start(processInfo);
// Executing long lasting operation in batch file will hang the process, as it will wait standard output / error pipes to be processed.
// We process these pipes here asynchronously.
StringBuilder so = new StringBuilder();
process.OutputDataReceived += (sender, args) => { so.AppendLine(args.Data); };
StringBuilder se = new StringBuilder();
process.ErrorDataReceived += (sender, args) => { se.AppendLine(args.Data); };
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
output = so.ToString();
String error = se.ToString();
if (transferEnvVars)
{
Regex r = new Regex("--VARS--(.*)", RegexOptions.Singleline);
var m = r.Match(output);
if (m.Success)
{
output = r.Replace(output, "");
foreach ( Match m2 in new Regex("(.*?)=([^\r]*)", RegexOptions.Multiline).Matches(m.Groups[1].ToString()) )
{
String key = m2.Groups[1].Value;
String value = m2.Groups[2].Value;
Environment.SetEnvironmentVariable(key, value);
}
}
}
if(error.Length != 0)
output += error;
int exitCode = process.ExitCode;
if (exitCode != 0)
Console.WriteLine("Error: " + output + "\r\n" + error);
process.Close();
return exitCode == 0;
}
Upvotes: 0
Reputation: 563
Getting the Environment variables directly from another process is not possible, but a simple workaround can be like:
Sample code is as follows:
/// pathToBat = bat file pathToBat
/// arg = any required arguments
/// envVar = name of environment variable
public string GetEnvVariable(string pathToBat, string arg, string envVar)
{
List<string> data = new List<string>();
data.Add("@ECHO OFF");
/// >nul 2>&1 = to put all the output away.
data.Add(String.Format("CALL \"{0}\" {1} >nul 2>&1", pathToBat.Trim(), arg.Trim()));
data.Add(String.Format("ECHO %{0}%", envVar));
/// Write the file
string path = Path.Combine("env.bat");
File.WriteAllLines(path, data);
string execute = String.Format("/c \"{0}\"", path);
return ExecuteProcess("cmd.exe", execute);
}
private string ExecuteProcess(string executable, string argument)
{
ProcessStartInfo processStartInfo = new ProcessStartInfo();
Process process = new Process();
StringBuilder output = new StringBuilder();
AutoResetEvent outputWaitHandle = new AutoResetEvent(false);
processStartInfo.UseShellExecute = false;
processStartInfo.FileName = executable;
processStartInfo.Arguments = command;
processStartInfo.RedirectStandardOutput = true;
process.StartInfo = processStartInfo;
process.OutputDataReceived += (sender, e) =>
{
if (e.Data == null)
outputWaitHandle.Set();
else
output.AppendLine(e.Data);
};
bool stat = process.Start();
process.BeginOutputReadLine();
process.WaitForExit();
int exitCode = process.ExitCode;
if (exitCode == 0 && stat)
{
//Log.Debug("Command Executed successfully");
return output.ToString();
}
else
{
//Log.Debug("Command Failed");
return String.Empty;
}
}
Upvotes: 0
Reputation: 49097
Your C# application runs cmd.exe
with following string as parameter:
/c foo.bat & bar.bat & set > "File name of temporary file with full path for variables"
I'm not a C# programmer and have no development environment installed for testing the provided C# code. Therefore I don't know if the last file name is enclosed in double quotes as required most likely, but it looks like this part works.
However, this command line results (most likely) in executing:
A first command process with the command line
cmd.exe /c foo.bat
with the environment variables inherited from C# application.
A second command process with the command line
bar.bat
resulting in executing by Windows
cmd.exe /c bar.bat
with the environment variables inherited from C# application.
A third command process with the command line
set > "File name of temporary file with full path for variables"
resulting in executing by Windows
cmd.exe /c set > "File name of temporary file with full path for variables"
with the environment variables inherited from C# application.
I suggest to use in your C# source file:
string arguments = string.Format("/c \"{0} & {1} & set > {2}\"", "foo.bat", "bar.bat", finalEnvVarsFile);
This result in execution of:
cmd.exe /c "foo.bat & bar.bat & set > "File name of temporary file with full path for variables""
Now the two batch files and command SET are executed by same command process and the temporary file might (not verified) contain the modified environment variables.
See also the answers on:
Difference in Delayed expansion of ERRORLEVEL on cmd prompt and win32_process
Upvotes: 1