Use case: I am checking certain credentials on a remote system by running commands via PsExec (i.e. for this example, I am trying to retrieve the KB articles currently installed on the remote system).
I have the following to retrieve command output:
public string GetCmDOutput(string cmd)
ProcessStartInfo startInfo = new ProcessStartInfo("control", cmd)
{
WindowStyle = ProcessWindowStyle.Hidden,
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
};
string output = string.Empty;
Process process = Process.Start(startInfo);
process.OutputDataReceived += (sender, e) => output = string.Concat(output, e.Data);
process.BeginOutputReadLine();
process.Start();
process.WaitForExit();
Delay.Milliseconds(1500) //API-specific delay
return output;
}
Whenever I use GetCmdOutput() to run a command locally it works like a charm, but if I try to run a command with PsExec, my output is empty.
For instance, I ran the following:
string cmd = #"\psexec.exe \\remoteComputerName -u username -p password -c cmd /c wmic qfe";
GetCmdOutput(cmd);
Report.Info(cmd); //API-specific reporting
And an empty string was returned.
After playing around with this for a couple of hours, I feel I may need a second set of eyes. What might be causing this issue?
I have run into this same problem. My solution was to run cmd and have it call psexec. I have psexec's output saved to a temp file for further manipulation. My code is returning a List.
public List<string> ExecutePSExec(string hostname)
{
List<string> recordNames = new List<string>();
string command = #"\\path\to\psexec.exe /accepteula \\" + hostname + ". exe-to-run-remotely";
try
{
string location = AppDomain.CurrentDirectory.BaseDirectory;
string cmdWithFileOutput = string.Format("{0} >{1}temp.log", command, location);
procStartInfo.UseShellExecute = true;
procStartInfo.CreateNoWindow = true;
procStartInfo.WindowStyle = ProcessWindowStyle.Hidden;
Process proc = new Process();
proc.StartInfo = procStartInfo;
proc.Start();
proc.WaitForExit();
// Read file contents, manipulate data and then delete temp file here
}
catch (Exception e)
{
Console.WriteLine("Failure to run psexec: {0}", e.Message);
}
return recordNames;
}
NOTE: I ran into another problem and found out that running psexec this way requires the remote hostname (not IP Address) in the command to end in a period \\" + hostname + ".
This code assumes you can run psexec on the remote machine as your current user.
Related
I found this function to run any command through bash from C#. So I tried passing it a command to create a symlink, but it's not doing anything. If I run that same command directly in a Terminal, it works. I tried running other commands like "ls" and that printed the proper items. Why isn't the symlink command working?
string output = ExecuteBashCommand("ln -s \"/Users/tim/academy-v2/Shared/Client Code\" \"/Users/tim/academy-v2/Shared Assets/Assets/Shared Code/Client Code\"");
UnityEngine.Debug.Log(output); // Prints "" like it worked, but didn't actually do anything.
...
static string ExecuteBashCommand(string command)
{
command = command.Replace("\"", "\"\"");
var proc = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "/bin/bash",
Arguments = "-c \"" + command + "\"",
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
}
};
proc.Start();
proc.WaitForExit();
return proc.StandardOutput.ReadToEnd();
}
Replacing the command with this seems to work, though not sure why.
"ln -s /Users/tim/academy-v2/Shared/Client\\ Code /Users/tim/academy-v2/Shared\\ Assets/Assets/Shared\\ Code/Client\\ Code"
I am using PsExec to remotely fire a program. I can fire the actual program (not displayed here) or cmd.exe remotely with no problems whatsoever from the command line. When I try to fire it from ASP and C#, it will not trigger the command prompt, even though I am using the same exact string. Here is the string I am using that works every time, and the code that doesn't. Help please!
Working String: C:\psexec \\10.0.0.25 -u Administrator -p password -d -i cmd.exe
Non-working code:
ProcessStartInfo psi = new ProcessStartInfo(#"C:\PsExec.exe")
{
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
RedirectStandardInput = true,
Arguments = #"\\10.0.0.25 -u Administrator -p password -d -i cmd.exe"
};
process.StartInfo = psi;
var success = process.Start();
One option, assuming you have control over the machine, is to setup the psexec command as a Task Scheduler job, then execute the task scheduler job from your ASP app. You can configure the task scheduler to run as an administrator, and when you fire off the job it will run under that credentials. You won't get any output that way though, so if that's an issue there may not be a good choice.
See How to run existing windows 7 task using command prompt for an example of running the task..
It's been a while since I was a system administrator, but if I recall correctly psexec has to be run from an administrative command prompt. Maybe the account your app is running under doesn't have rights to reach across the network and do stuff to a remote machine?
Put this in your Page_Load temporarily:
Response.Write(Environment.UserName);
and run it again, it should show you the name you're looking for at the top of your app.
Well, I am right now doing some automation and have figured out a few things. Please see below code maybe it will help you out
public static void PSExec_Method()
{
try
{
string userName = #"ABC";
string password = "ABC";
string remoteMachine = "ABC";
//How to restart AppPool
//string operation = "stop";
//string apppoolname = "APPPOOL";
//string command = #"%SYSTEMROOT%\System32\inetsrv\appcmd " + operation + " apppool /apppool.name:\"" + apppoolname + "\"";
string command = #"powershell -noninteractive Get-Content C:\tmp\tmp.csv -Head 5";
//string command = #"ipconfig";
string PSPath = #"C:\PSTools\PsExec.exe";
string fullcommand = PSPath + " -u " + userName + " -p " + password + " \\\\" + remoteMachine + " -h cmd.exe /c " + command;
System.Diagnostics.Process process = new System.Diagnostics.Process();
process.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.FileName = "cmd.exe";
process.StartInfo.Arguments = fullcommand;
process.Start();
Console.WriteLine(process.StandardOutput.ReadToEnd());
Console.WriteLine(process.StandardError.ReadToEnd());
process.WaitForExit();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
I want to run a gpu accelerated python script on windows using conda environment (dlwin36).
I’m trying to activate dlwin36 and execute a script:
1) activate dlwin36
2) set KERAS_BACKEND=tensorflow
3) python myscript.py
If I manually open cmd on my machine and write:"activate dlwin36"
it works.
But when I try opening a cmd from c# I get:
“activate is not recognized as an internal or external command, operable program or batch file.”
I tried using the following methods:
Command chaining:
var start = new ProcessStartInfo();
start.FileName = "cmd.exe";
start.Arguments = "/c activate dlwin36&&set KERAS_BACKEND=tensorflow&&python myscript.py";
Process.Start(start).WaitForExit();
(I’ve tested several variations of UseShellExecute, LoadUserProfile and WorkingDirectory)
Redirect standard input:
var commandsList = new List<string>();
commandsList.Add("activate dlwin36");
commandsList.Add("set KERAS_BACKEND=tensorflow");
commandsList.Add("python myscript.py");
var start = new ProcessStartInfo();
start.FileName = "cmd.exe";
start.UseShellExecute = false;
start.RedirectStandardInput = true;
var proc = Process.Start(start);
commandsList.ForEach(command => proc.StandardInput.WriteLine(command));
(I’ve tested several variations of LoadUserProfile and WorkingDirectory)
In both cases, I got the same error.
It seems that there is a difference between manually opening cmd and opening it from c#.
The key is to run activate.bat in your cmd.exe before doing anything else.
// Set working directory and create process
var workingDirectory = Path.GetFullPath("Scripts");
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "cmd.exe",
RedirectStandardInput = true,
UseShellExecute = false,
RedirectStandardOutput = true,
WorkingDirectory = workingDirectory
}
};
process.Start();
// Pass multiple commands to cmd.exe
using (var sw = process.StandardInput)
{
if (sw.BaseStream.CanWrite)
{
// Vital to activate Anaconda
sw.WriteLine("C:\\PathToAnaconda\\anaconda3\\Scripts\\activate.bat");
// Activate your environment
sw.WriteLine("activate your-environment");
// Any other commands you want to run
sw.WriteLine("set KERAS_BACKEND=tensorflow");
// run your script. You can also pass in arguments
sw.WriteLine("python YourScript.py");
}
}
// read multiple output lines
while (!process.StandardOutput.EndOfStream)
{
var line = process.StandardOutput.ReadLine();
Console.WriteLine(line);
}
You need to use the python.exe from your environment. For example:
Process proc = new Process();
proc.StartInfo.FileName = #"C:\path-to-Anaconda3\envs\tensorflow-gpu\python.exe";
or in your case:
start.Arguments = "/c activate dlwin36&&set KERAS_BACKEND=tensorflow&&\"path-to-Anaconda3\envs\tensorflow-gpu\python.exe\" myscript.py";
I spent a bit of time working on this and here's the only thing that works for me: run a batch file that will activate the conda environment and then issue the commands in python, like so. Let's call this run_script.bat:
call C:\Path-to-Anaconda\Scripts\activate.bat myenv
set KERAS_BACKEND=tensorflow
python YourScript.py
exit
(Note the use of the call keyword before we invoke the activate batch file.)
After that you can run it from C# more or less as shown above.
ProcessStartInfo start = new ProcessStartInfo();
start.FileName = "cmd.exe";
start.Arguments = "/K c:\\path_to_batch\\run_script.bat";
start.UseShellExecute = false;
start.RedirectStandardOutput = true;
start.RedirectStandardError = true;
start.WorkingDirectory = "c:\\path_to_batch";
string stdout, stderr;
using (Process process = Process.Start(start))
{
using (StreamReader reader = process.StandardOutput)
{
stdout = reader.ReadToEnd();
}
using (StreamReader reader = process.StandardError)
{
stderr = reader.ReadToEnd();
}
process.WaitForExit();
}
I am generating the batch file on the fly in C# to set the necessary parameters.
If this is gonna help anyone in the future. I found that you must run the activation from C:\ drive.
This question already has answers here:
Trying to get StandardOutput after running a console process
(2 answers)
Closed 5 years ago.
I'm running a CMD in c# programmatically and everything works fine, it will run whatever command I input but when I ask to return the output to the console, only some commands output gets returned.
For example if I write "ipconfig" as the command, it will return my ip info but if I write "/ipconfig", it will just return "/ipconfig" as appose to "/ipconfig is not recognized as an interal ..." you get the idea. I also tried it with deleting files, the file deletes just fine but if the file doesnt exist, it should output "could not find file" like it does it normal cmd. It seems it only gives me the output of a command thats runs and not one that doesnt even though I read the cmd till the end of anything it outputs. If this is the case is there anyway to get around this?
Code:
private void Form1_Load(object sender, EventArgs e)
{
string command = "/ipconfig";
string root = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
Console.WriteLine(Command(command, root));
}
public string Command(string command, string directory)
{
string commandOutput;
commandOutput = DateTime.Now + Environment.NewLine + command + Environment.NewLine;
ProcessStartInfo procStartInfo = new System.Diagnostics.ProcessStartInfo("cmd", "/c " + command);
procStartInfo.WorkingDirectory = directory;
procStartInfo.RedirectStandardError = true;
procStartInfo.RedirectStandardOutput = true;
procStartInfo.UseShellExecute = false;
procStartInfo.CreateNoWindow = true;
Process proc = new System.Diagnostics.Process();
proc.StartInfo = procStartInfo;
proc.Start();
commandOutput += proc.StandardOutput.ReadToEnd();
proc.WaitForExit();
return commandOutput;
}
I'm pretty sure cmd.exe will write that to standard error, not standard output. If that is the case, you'll need to use proc.StandardError.ReadToEnd() to get the error message.
I want to execute a batch command and save the output in a string, but I can only execute the file and am not able to save the content in a string.
Batch file:
#echo off
"C:\lmxendutil.exe" -licstatxml -host serv005 -port
6200>C:\Temp\HW_Lic_XML.xml notepad C:\Temp\HW_Lic_XML.xml
C# code:
private void btnShowLicstate_Click(object sender, EventArgs e)
{
string command = "'C:\\lmxendutil.exe' -licstatxml -host lwserv005 -port 6200";
txtOutput.Text = ExecuteCommand(command);
}
static string ExecuteCommand(string command)
{
int exitCode;
ProcessStartInfo processInfo;
Process process;
processInfo = new ProcessStartInfo("cmd.exe", "/c " + command);
processInfo.CreateNoWindow = true;
processInfo.UseShellExecute = false;
// *** Redirect the output ***
processInfo.RedirectStandardError = true;
processInfo.RedirectStandardOutput = true;
process = Process.Start(processInfo);
process.WaitForExit();
// *** Read the streams ***
string output = process.StandardOutput.ReadToEnd();
string error = process.StandardError.ReadToEnd();
exitCode = process.ExitCode;
process.Close();
return output;
}
I want the output in a string and do this directly in C# without a batch file, is this possible?
Don't need to use "CMD.exe" for execute a commandline application or retreive the output, you can use "lmxendutil.exe" directly.
Try this:
processInfo = new ProcessStartInfo();
processInfo.FileName = "C:\\lmxendutil.exe";
processInfo.Arguments = "-licstatxml -host serv005 -port 6200";
//etc...
Do your modifications to use "command" there.
I hope this helps.
It doesn't look to me like your batch file will produce any output. If you run it in the command line, do you see an output? You have the redirection > operator in your bat file line, so it seems like you're sending output to the xml file.
If you have saved the output to an xml file, maybe you should just load that using C# once your process exits.