Execute BATCH script in a programs memory - c#

I am currently faced with a problem, I need to execute a batch script within a programs memory (so it does not have to extract the batch file to a temporary location).
I am open to solutions in C# and C++
Any help would be appreciated

cmd.exe won't run a script from the memory of your process. The options which seem most obvious to me are:
Relax the constraint that stops you extracting the script to a temporary file.
Compress your script into a single line and use cmd.exe /C to execute it. You'll need to use the command separator &&.
Write your own batch command interpreter.
Use a different scripting language.
Options 3 and 4 aren't really very attractive! Option 1 looks pretty good to me but I don't know what's leading to your constraint.

Open a pipe to the command shell and write the program code into that pipe. Here is an example: http://support.microsoft.com/default.aspx?scid=kb;en-us;190351

In C# it's an easy way to use System.Diagnostics for the job.
How!?
Basically, every batch command is an .exe file so you can start it in a separate process.
Some code:
using System.Diagnostics;
static void Main()
{
Process batch;
batch = Process.Start("ping.exe", "localhost");
batch.WaitForExit();
batch.Close();
batch = Process.Start("choice.exe", "");
batch.WaitForExit();
batch.Close();
batch = Process.Start("ping.exe", "localhost -n 10");
batch.WaitForExit();
batch.Close();
}
If you don't want to start every command in a separate process the solution is with a simple stream redirection.
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = #"cmd.exe"; // Specify exe name.
startInfo.UseShellExecute = false;
startInfo.ErrorDialog = false;
startInfo.RedirectStandardInput = true;
//
// Start the process.
//
Process process = Process.Start(startInfo);
string[] batchFile = {"ping localhost", "ping google.com -n 10", "exit"};
int cmdIndex = 0;
while (!process.HasExited)
{
if (process.Threads.Count == 1 && cmdIndex < batchFile.Length)
{
process.StandardInput.WriteLine(batchFile[cmdIndex++]);
}
}

What's a good way to write batch scripts in C#?

Related

Execute all .bat files in the current folder and delete them after execution

I am a newbie programmer wanting to make averaging videos. I have made a program to create n .bat files doing the average of n images, now I would like to execute them as fast as possible.
The .bat files are independent.
I am in a Windows environment.
I have looked at C# multi threading (threadpool, parrallel.for, parralel.foreach etc), but none of the functions there seems to work. I have no illusion that it's me who's doing something wrong though.
Powershell has a function doing what I want, but only for other powershell commands.
The code I have now that mostly works is:
(complete solution at https://github.com/Madsfoto/ParallelExecutionForEach )
var paths = Directory.GetFiles(Directory.GetCurrentDirectory(), "*.bat"); // have a list of all .bat files in the current directory
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo.CreateNoWindow = true;
proc.StartInfo.UseShellExecute = false; // above is to not see the cmd window
proc.StartInfo.WorkingDirectory = Directory.GetCurrentDirectory(); // It's easier than having to specify where this program will be run.
Parallel.ForEach(paths, new ParallelOptions { MaxDegreeOfParallelism = 4 }, currentFile => // 4 is set because I have 4 cores to use
{
proc.StartInfo.FileName = currentFile; // Set the currentfile as the one being executed. currentFile is the name of the .bat file to execute
proc.Start(); // execute the .bat file
proc.WaitForExit();
File.Delete(currentFile);
});
I get System.InvalidOperationException: No process is associated with this object and System.UnauthorizedAccessException’s when I run more than 3-4 processes at the same time.
I suspect that it is the WaitForExit() that is giving me problems, but do not have the skills to debug it.
I have looked at Threading.Task as well, but my skill is not good enough to use it.
So the solution I am after is as follows:
Execute either 1 input file with x lines of independent action or x files with 1 action, with a limit of y processes at the same time either set at compile or runtime.
Programming language is not important to me, although my preference is understandable C#.
(The result is something like https://www.youtube.com/watch?v=ph6-6bYTgs0, with n frames averaged together)
The solution was to move the
proc.Start(); // execute the .bat file
proc.WaitForExit();
try
{
File.Delete(FileName);
}
catch
{
}
code into it's own function (with the bookkeeping stuff (defining proc etc)).
The File.Delete() was the culprit, it turns out that there might be a bug in the Parallel.ForEach, but I have been unable to reproduce it reliably (experimenting gives errors ~0.01% of the time), but this way it works. It does require people to rerun the executable, but that is a burden I can justify pushing to the user.
The github link has been updated with a working version.

Shell Script File(.sh) does not run from c# core on linux

I am trying to run ".sh" file from c# core application.But it doesn't seem to be running properly.Here is my scenario.
I am working on .Net core project which is hosted on Linux environment.We are trying to create "PDF" in our project for which we have used "Apache FOP".
Here i have created one "shell script" file "transform.sh" which internally calls "fop" with required parameters.Since developement is being done on windows machine we tested the same usinf "batch" file i.e. "transform.bat",but since we cannot use the "batch" file on linux enviornment we have created shell script file "transform.sh"
Following is the code from"transform.sh"
./fop -xml $1 -xsl $2 -pdf $3
Following is C# code from which i am calling the "shell script file
var process = new Process
{
StartInfo = new ProcessStartInfo
{
UseShellExecute = false,
RedirectStandardOutput = true,
Arguments = string.Format("{0} {1} {2}", XML_filename, XSL_filename, output)
}
};
process.StartInfo.FileName = "Path to shell script file";
process.Start();
process.WaitForExit();
Above code doesnot give any error but it also does not create the pdf file.If i directly run the shell script file from "Terminal" it works fine and create pdf file.
./transform.sh "/home/ubuntu/psa//PdfGeneration/ApacheFolder/XMLFolder/test.xml" "/home/ubuntu/psa/PdfGeneration/ApacheFolder/XSLTFolder/Certificate.xsl" "/home/ubuntu/psa/PdfGeneration/ApacheFolder/PDFFolder/t444t.pdf"
Please let me know if there is something wrong i am doing?How can i make the sheel script run on linux through C# core application.
Thanks.
I was able to solve the issue,just thought that i should put my solution here so that it may help others in future...
As mentioned in Question i was not able to generate the PDF file through shell script on linux machine.After debugging as suggested by "#JNevill" I came to understand that the shell script file was not getting called from .net process itself.
So my first task was to make the shell script file called through .Net Process.
After lots of searching through Net and trying out different solutions i got solution at How to perform command in terminal using C#(Mono).
So changed my code of calling the process as follow,
var command = "sh";
var myBatchFile = //Path to shell script file
var argss = $"{myBatchFile} {xmlPath} {xsltPath} {pdfPath}"; //this would become "/home/ubuntu/psa/PdfGeneration/ApacheFolder/ApacheFOP/transform.sh /home/ubuntu/psa/PdfGeneration/ApacheFolder/XMLFolder/test.xml /home/ubuntu/psa/PdfGeneration/ApacheFolder/XSLTFolder/Certificate.xsl /home/ubuntu/psa/PdfGeneration/ApacheFolder/PDFFolder/test.pdf"
var processInfo = new ProcessStartInfo();
processInfo.UseShellExecute = false;
processInfo.FileName = command; // 'sh' for bash
processInfo.Arguments = argss; // The Script name
process = Process.Start(processInfo); // Start that process.
var outPut = process.StandardOutput.ReadToEnd();
process.WaitForExit();
After changing the code ,the ".sh" file got executed and i was able to generate the PDF file.
Also script of the ".sh" file i.e. (transform.sh) which was calling Apache FOP file i.e. "FOP.sh" also needed to be changed.
Initially code was
./fop -xml $1 -xsl $2 -pdf $3
Which i changed as follow,(Change was to give full path of the FOP file)
/home/ubuntu/psa/PdfGeneration/ApacheFolder/ApacheFOP/fop -xml $1 -xsl $2 -pdf $3
Late answer, but for me, it worked just by setting the RedirectStandardOutput to true and changing the FileName property like this:
processInfo.FileName = #"C:\Program Files\Git\git-bash.exe";
processInfo.RedirectStandardOutput = true;

Writing and executing multiple lines sequentially in an elevated command prompt using c#

Am a Newbie in C# and I have 3 commands(command2, command3 and command4) I need to execute in the elevated command prompt and I will also like to view the execution process as it happens. Currently, the problem is that the code below just opens the elevated command prompt and without executing the commands. I also seek better interpretations of the lines if wrong.
My code and Interpretation/Understanding of each line based on reviews of similar cases: ConsoleApp1
class Program
{
static void Main(string[] args)
{
string command2 = #"netsh wlan";
string command3 = #" set hostednetwork mode=true ssid=egghead key=beanhead keyusage=persistent";
string command4 = #" start hostednetwork";
string maincomm = command2.Replace(#"\", #"\\") + " " + command3.Replace(#"\", #"\\") ; //I merged commands 2 and 3
ProcessStartInfo newstartInfo = new ProcessStartInfo();
newstartInfo.FileName = "cmd"; //Intend to open cmd. without this the newProcess hits an error saying - Cannot run process without a filename.
newstartInfo.Verb = "runas"; //Opens cmd in elevated mode
newstartInfo.Arguments = maincomm; //I intend to pass in the merged commands.
newstartInfo.UseShellExecute = true; //
newstartInfo.CreateNoWindow = true; // I intend to see the cmd window
Process newProcess = new Process(); //
newProcess.StartInfo = newstartInfo; //Assigns my newstartInfo to the process object that will execute
newProcess.Start(); // Begin process and Execute newstartInfo
newProcess.StartInfo.Arguments = command4; //I intend to overwrite the initial command argument hereby passing the another command to execute.
newProcess.WaitForExit(); //
}
}
This is what I did to overcome the challenge and It gave me exactly what I wanted. I modified my code to use the System.IO to write directly to the elevated command prompt.
ProcessStartInfo newstartInfo = new ProcessStartInfo();
newstartInfo.FileName = "cmd";
newstartInfo.Verb = "runas";
newstartInfo.RedirectStandardInput = true;
newstartInfo.UseShellExecute = false; //The Process object must have the UseShellExecute property set to false in order to redirect IO streams.
Process newProcess = new Process();
newProcess.StartInfo = newstartInfo;
newProcess.Start();
StreamWriter write = newProcess.StandardInput ; //Using the Streamwriter to write to the elevated command prompt.
write.WriteLine(maincomm); //First command executes in elevated command prompt
write.WriteLine(command4); //Second command executes and Everything works fine
newProcess.WaitForExit();
Referrence: http://msdn.microsoft.com/en-us/library/system.diagnostics.process.standardinput(v=vs.110).aspx
http://msdn.microsoft.com/en-us/library/system.diagnostics.processstartinfo(v=vs.110).aspx
I think an understanding of some properties of the ProcessStartInfo might clear things.
The verb - Gets or sets the verb to use when opening the application or document specified by the FileName property.,
+The UseShellExecute - Gets or sets a value indicating whether to use the operating system shell to start the process.
+The FileName - Gets or sets the application or document to start MSDN Docs
When you use the operating system shell to start processes, you can start any document (which is any registered file type associated with an executable that has a default open action) and perform operations on the file, such as printing, by using the Process object. When UseShellExecute is false, you can start only executables by using the Process object Documentation from MSDN.
In my case, cmd is an executable. the verb property is some thing that answers the question "How should my I run my FileName(for executables e.g cmd or any application)?" for which I answered - "runas" i.e run as administrator. When the FileName is a document (e.g `someFile.txt), the verb answers the question "What should I do with the file for which answer(verb) could be -"Edit","print" etc. also?"
use true if the shell should be used when starting the process; false if the process should be created directly from the executable file. The default is true MSDN Docs - UserShellInfo.
Another thing worth noting is knowing what you are trying to achieve. In my case, I want to be able to run commands via an executable(cmd prompt) with the same process - i.e starting the cmd as a process I can keep track of.

How can I open an external Java console application with arguments, capture output and execute commands on it?

I have a Java .jar application that is ran from a .bat file to have arguments passed to the Java application. The application opens a console (cmd.exe to be exact), writes things to it regularly and accepts some commands. I'm trying to create a kind-of wrapper around it in C# Winforms to ease it's use. How can I run the .jar with the same arguments as are in the .bat file, capture realtime output and write execute commands?
Yes, it is possible to do this using the System.Diagnostics.Process class and the ProcessStartInfo class from the .NET Framework. The Process class is used to control (start / stop) the desired process (application) and the ProcessStartInfo class is used to configure the process instance that will be started (arguments, redirect input and output, show / hide process window, and so on).
The code for starting a jar file looks like this:
var jarFile = "D:\\software\\java2html\\java2html.jar");
// location of the java.exe binary used to start the jar file
var javaExecutable = "c:\\Program Files (x86)\\Java\\jre7\\bin\\java.exe";
try
{
// command line for java.exe in order to start a jar file: java -jar jar_file
var arguments = String.Format(" -jar {0}", jarFile);
// create a process instance
var process = new Process();
// and instruct it to start java with the given parameters
var processStartInfo = new ProcessStartInfo(javaExecutable, arguments);
process.StartInfo = processStartInfo;
// start the process
process.Start();
}
catch (Exception exception)
{
Console.WriteLine(exception.Message);
}
The usual way to start a jar file is:
java -jar file.jar
To be sure, that the process will find the (in this case java) executable, it is a good practice to specify the fully qualified path to the process you want to start.
In order to redirect the standard output of the application you are starting, you need to set the ProcessStartInfo.RedirectStandardOutput property to true and then use the Process.StandardOutput property stream to fetch the output of the started application. The modified code for the application from the example above looks like this:
// command line for java.exe in order to start a jar file: java -jar jar_file
var arguments = String.Format(" -jar {0}", jarFile);
// indicate, that you want to capture the application output
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
// create a process instance
var process = new Process();
// and instruct it to start java with the given parameters
var processStartInfo = new ProcessStartInfo(javaExecutable, arguments);
process.StartInfo = processStartInfo;
// start the process
process.Start();
// read the output from the started appplication
string output = process.StandardOutput.ReadToEnd();
process.WaitForExit();
If you want to control the input too, set the ProcessStartInfo.RedirectStandarInput property to true and then use the Process.StandardInput property stream to send input data to the started application.

How to send series of commands to a command window process?

We have a few commands(batch files/executables) on our network path which we have to call to initialize our 'development environment' for that command window. It sets some environmental variables, adds stuff to the Path etc. (Then only whatever working commands we type will be recognized & I don't know what goes inside those initializing commands)
Now my problem is, I want to call a series of those 'working commands' using a C# program, and certainly, they will work only if the initial setup is done. How can I do that? Currently, I'm creating a batch file by scratch from the program like this for example:
file.Writeline("InitializationStep1.bat")
file.Writeline("InitializeStep2.exe")
file.Writeline("InitializeStep3.exe")
Then the actual commands
file.Writeline("Dowork -arguments -flags -blah -blah")
file.Writeline("DoMoreWork -arguments -flags -blah -blah")
Then finally close the file writer, and run this batch file.
Now if I directly execute this using Process.<strike>Run</strike>Start("cmd.exe","Dowork -arguments"); it won't run.
How can I achieve this in a cleaner way, so that I have to run the initialization commands only once? (I could run cmd.exe each time with all three initializers, but they take a lot of time so I want to do it only once)
As #Hakeem has pointed out, System.Diagnostic.Process does not have a static Run method. I think you are referring to the method Start.
Once you have completed building the batch file, then simply execute it using the following code,
Process p = new Process();
p.StartInfo.FileName = batchFilePath;
p.StartInfo.Arguments = #"-a arg1 -b arg2";
p.Start();
Note that the # symbol is required to be prefixed to the argument string so that escape sequence characters like \ are treated as literals.
Alternative code
Process.Start(batchFilePath, #"-a arg1 -b arg2");
or
ProcessStartInfo processStartInfo = new ProcessStartInfo();
processStartInfo.FileName = batchFilePath;
processStartInfo.Arguments = #"-a arg1 -b arg2";
Process.Start(processStartInfo);
More information
Process.Start method
Example of multi command batch file
dir /O
pause
dir
pause
Save this file as .bat and then execute using the Start method. In this case you can specify the argument with the command in the batch file itself (in the above example, the /O option is specified for the dir command.
I suppose you already have done the batch file creation part, now just append the arguments to the commands in the batch file.
Redirecting Input to a process
Since you want to send multiple commands to the same cmd process, you can redirect the standard input of the process to the take the input from your program rather than the keyboard.
Code is inspired from a similar question at: Execute multiple command lines with the same process using C#
private string ProcessRunner()
{
ProcessStartInfo processStartInfo = new ProcessStartInfo("cmd.exe");
processStartInfo.RedirectStandardInput = true;
processStartInfo.RedirectStandardOutput = true;
processStartInfo.UseShellExecute = false;
Process process = Process.Start(processStartInfo);
if (process != null)
{
process.StandardInput.WriteLine("dir");
process.StandardInput.WriteLine("mkdir testDir");
process.StandardInput.WriteLine("echo hello");
//process.StandardInput.WriteLine("yourCommand.exe arg1 arg2");
process.StandardInput.Close(); // line added to stop process from hanging on ReadToEnd()
string outputString = process.StandardOutput.ReadToEnd();
return outputString;
}
return string.Empty;
}
The method returns the output of the command execution. In a similar fashion, you could also redirect and read the StandardOuput stream of the process.
The Process.Run method that you mentioned, is that from the Process class in System.Diagnostics namespace? AFAIK, the Process type doesn't have either a static or instance method named Run. If you haven't already I'd try with the Start method on Process, either instance or static

Categories