I am trying to automate the remote server shell commands through Plink. And one of the things which I did is grep command.
Now suppose if results lots of data then I just want to break the command.
Generally from PuTTY you just do Ctrl-C or Ctrl-Break and it will break the command.
What's the alternative for Plink?
Do not run Plink from C# application to implement SSH. Use a native .NET implementation of SSH, like SSH.NET. – It will give you a complete control over the connection and you won't have have to use hacks like sending Ctrl+C.
var client = new SshClient("example.com", "username", "password");
client.Connect();
SshCommand command = client.CreateCommand("grep pattern file");
IAsyncResult result = command.BeginExecute();
using (var outputReader = new StreamReader(command.OutputStream))
using (var extendedReader = new StreamReader(command.ExtendedOutputStream))
{
int read = 0;
while (read < 10240)
{
string s;
s = outputReader.ReadToEnd();
read += s.Length;
Console.Write(s);
s = extendedReader.ReadToEnd();
read += s.Length;
Console.Write(s);
}
}
command.CancelAsync();
Also, you can use -m switch to stop grep after certain number of matches.
Related
In Tera Term I'm connecting to a serial device over USB (on startup select radiobutton "serial" and correct port). Once connected I only change the default speed to 115200 (in setup=> serial port).
After this, tera term asks me to fill in commands like so:
Command>
I fill in the device specific command. In this case it's "PC" and I receive an expected response ie. "ABC"
Now I'm trying to do the same in C#. Unfortunately the response I get is always the same as the command I actually type in.
So If I type in "PC", the response is "PC", but I expect "ABC". Other commands have the same problem. Command "?" responds with "?" while I expect "CBA".
If I type in a faulty command => then I get the message "Unknown command"
So I suspect the device actually gets the right command.
I'm using the following code:
SerialPort COMport = new SerialPort(Port_Name, Baud_Rate); //Create a new SerialPort Object (defaullt setting -> 8N1)
COMport.DataReceived += new SerialDataReceivedEventHandler(sPort_dataReceived);
COMport.ErrorReceived += new SerialErrorReceivedEventHandler(sPort_ErrorReceived);
COMport.BaudRate = 115200;
COMport.Parity = Parity.None;
COMport.DataBits = 8;
COMport.StopBits = StopBits.One;
COMport.RtsEnable = true;
COMport.Handshake = Handshake.None;
COMport.Open();
COMport.WriteLine(Data);
Thread.Sleep(1000); // Just discovered after a lot of testing that this is necessary to read the response before the Comport closes
COMport.Close();
Then I do the following:
private void sPort_dataReceived(object sender, SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
string indata = sp.ReadExisting();
Console.WriteLine("Data Received:");
Console.Write(indata);
MessageBox.Show(indata);
}
I've tried different things, but I can't get this to work. Hopefully it's because I'm new to this. I've never worked with Tera term before.
Thanks in advance,
Some (but definately not all) things I've tried:
tried this guys advise and code: https://www.sparxeng.com/blog/software/must-use-net-system-io-ports-serialport
downloaded and tried from here: https://www.xanthium.in/building-opensource-gui-based-serial-port-communication-program-dot-net-framework-and-arduino#simple-serial-source-code (Although my device is not arduino)
Tried to add "\r\n" : C# Errors with SerialPort WriteLine commands
EDIT EDIT EDIT EDIT
So I found out more. If I use the following code (Write instead of WriteLine), I do get good results but not every time:
Sending the full command now: "Command>PC"
string Command1 = txtCommand.Text;
Command1 = Command1 + "\r\n";
string CommandSent;
int Length, j = 0;
Length = Command1.Length;
for (int i = 0; i < Length; i++)
{
CommandSent = Command1.Substring(j, 1);
ComPort.Write(CommandSent);
j++;
}
The first time, now I get good results. The second time I get "Unknow Command", the 3rd time => good results, 4th = "Unknown Command"... etc...
It always seems to work 1 time well, then 1 time not.
I can only get it to work consistently if I switch the command formatting:
First time command: "Command>PC"
Second time command: "PC"
Third time command : "Command>PC"
Fourth time command: "PC"
etc...
I've already tried to clear the buffer before sending but no effect.
ComPort.DiscardInBuffer();
ComPort.DiscardOutBuffer();
The Newline seemed to be a problem.
I needed to use Comport.Write (instead of WriteLine). Then Also I needed to append a carriage return "\r" but NO newline '\n' as I previously thought. (the incoming data showed a newline after the "Command>" making it impossible to send another meaningfull command => the cause of this was '\n' => removing it solved the problem)
This is my current code that seems to work (I no longer need to append "Command>", just sending the command as is):
if (thecommand == "")
{
ComPort.Write("\r"); //start from a clean slate
return;
}
ComPort.DiscardInBuffer();
ComPort.DiscardOutBuffer();
string Command1 = thecommand + "\r";
ComPort.Write(Command1);
I want to change directory inside SSH using C# with SSH.NET library:
SshClient cSSH = new SshClient("192.168.80.21", 22, "appmi", "Appmi");
cSSH.Connect();
Console.WriteLine("current directory:");
Console.WriteLine(cSSH.CreateCommand("pwd").Execute());
Console.WriteLine("change directory");
Console.WriteLine(cSSH.CreateCommand("cdr abc-log").Execute());
Console.WriteLine("show directory");
Console.WriteLine(cSSH.CreateCommand("pwd").Execute());
cSSH.Disconnect();
cSSH.Dispose();
Console.ReadKey();
But it's not working. I have also checked below:
Console.WriteLine(cSSH.RunCommand("cdr abc-log").Execute());
but still is not working.
I believe you want the commands to affect the subsequent commands.
But SshClient.CreateCommand uses SSH "exec" channel to execute the command. That means that every command is executed in an isolated shell and has no effect on the other commands.
If you need to execute commands in a way that previous commands affect later commands (like changing a working directory or setting an environment variable), you have to execute all commands in the same channel. Use an appropriate construct of the server's shell for that. On most systems you can use semicolons:
Console.WriteLine(cSSH.CreateCommand("pwd ; cdr abc-log ; pwd").Execute());
On *nix servers, you can also use && to make the following commands be executed only when the previous commands succeeded:
Console.WriteLine(cSSH.CreateCommand("pwd && cdr abc-log && pwd").Execute());
Some less common systems (for example AIX) may not even have a way to execute multiple commands in one "command-line". In these cases, you may need to use a shell channel, what is otherwise not recommended.
Also when the other commands are actually sub commands of the first command, you may need different solution.
See Providing subcommands to a command (sudo/su) executed with SSH.NET SshClient.CreateShellStream.
this is what I have done and its working for me:
SshClient sshClient = new SshClient("some IP", 22, "loign", "pwd");
sshClient.Connect();
ShellStream shellStream = sshClient.CreateShellStream("xterm", 80, 40, 80, 40, 1024);
string cmd = "ls";
shellStream.WriteLine(cmd + "; echo !");
while (shellStream.Length == 0)
Thread.Sleep(500);
StringBuilder result = new StringBuilder();
string line;
string dbt = #"PuttyTest.txt";
StreamWriter sw = new StreamWriter(dbt, append: true);
while ((line = shellStream.ReadLine()) != "!")
{
result.AppendLine(line);
sw.WriteLine(line);
}
sw.Close();
sshClient.Disconnect();
sshClient.Dispose();
Console.ReadKey();
I need to execute this action using a C# code:
open putty.exe in the background (this is like a cmd window)
login to a remote host using its IP address
enter a user name and password
execute several commands one after the other.
run another command that gets a response telling me that the commands I ran before that where executed successfully
So I'm trying to do it like this:
ProcessStartInfo proc = new ProcessStartInfo()
{
FileName = #"C:\putty.exe",
UseShellExecute = true, //I think I need to use shell execute ?
RedirectStandardInput = false,
RedirectStandardOutput = false,
Arguments = string.Format("-ssh {0}#{1} 22 -pw {2}", userName, hostIP, password)
... //How do I send commands to be executed here ?
};
Process.Start(proc);
You could try https://github.com/sshnet/SSH.NET.
With this you wouldn't need putty or a window at all.
You can get the responses too.
It would look sth. like this.
SshClient sshclient = new SshClient("172.0.0.1", userName, password);
sshclient.Connect();
SshCommand sc= sshclient .CreateCommand("Your Commands here");
sc.Execute();
string answer = sc.Result;
Edit: Another approach would be to use a shellstream.
Create a ShellStream once like:
ShellStream stream = sshclient.CreateShellStream("customCommand", 80, 24, 800, 600, 1024);
Then you can use a command like this:
public StringBuilder sendCommand(string customCMD)
{
StringBuilder answer;
var reader = new StreamReader(stream);
var writer = new StreamWriter(stream);
writer.AutoFlush = true;
WriteStream(customCMD, writer, stream);
answer = ReadStream(reader);
return answer;
}
private void WriteStream(string cmd, StreamWriter writer, ShellStream stream)
{
writer.WriteLine(cmd);
while (stream.Length == 0)
{
Thread.Sleep(500);
}
}
private StringBuilder ReadStream(StreamReader reader)
{
StringBuilder result = new StringBuilder();
string line;
while ((line = reader.ReadLine()) != null)
{
result.AppendLine(line);
}
return result;
}
While the answer by #LzyPanda works, using an SSH "shell" channel (SshClient.CreateShellStream), let alone an interactive terminal, is not a good idea for automating commands execution. You get lot of side-effects from that, like command prompts, ANSI sequences, interactive behavior of some commands, etc.
For automation, use an SSH "exec" channel (SshClient.CreateCommand):
using (var command = ssh.CreateCommand("command"))
{
Console.Write(command.Execute());
}
If you need to execute multiple commands, repeat the above code. You can create any number of "exec" channels for one SSH connection.
Though if the commands depend on each other (first command modified the environment, e.g. variables, that are used by the latter commands), you have execute them within one channel. Use a shell syntax for that, like && or ;:
using (var command = ssh.CreateCommand("command1 && command2"))
{
Console.Write(command.Execute());
}
If you need to continuously read the commands output use:
using (var command = ssh.CreateCommand("command"))
{
var asyncExecute = command.BeginExecute();
command.OutputStream.CopyTo(Console.OpenStandardOutput());
command.EndExecute(asyncExecute);
}
You can also use ExtendedOutputStream, which contains stderr. See SSH.NET real-time command output monitoring.
Unfortunately implementation of "exec" channel in SSH.NET does not allow providing an input to the command. For that use case, you will need to resort to the "shell" channel, until this limitation is solved.
I am trying to create a SSH client using C#. I am using the Tamir.SharpSsh library. I am having issues sending the command and getting an appropriate response from the server.
The way I have been testing it is by sending over the ls command, followed by cd.. command, followed by ls command again -- to see whether or not the cd.. command is being executed properly. However, there is no difference between the first ls command and the second ls command. I am not entirely sure what I am doing wrong. Perhaps I am using the wrong Ssh type. I have provided the code and the output I am getting. I have also added the output that I expect.
using Tamir.SharpSsh;
namespace SSHNetExample
{
class Program
{
static void Main(string[] args)
{
SshExec ssh = null;
try
{
Console.Write("-Connecting...");
ssh = new SshExec(host, username, password);
ssh.Connect();
Console.WriteLine("OK ({0}/{1})", ssh.Cipher, ssh.Mac);
Console.WriteLine("Server version={0}, Client version={1}", ssh.ServerVersion, ssh.ClientVersion);
Console.WriteLine("-Use the 'exit' command to disconnect.");
Console.WriteLine();
while(true)
{
string command = Console.ReadLine();
if(command == "exit")
break;
string data = ssh.RunCommand(command);
Console.WriteLine(data);
}
}
catch(Exception e)
{
Console.WriteLine(e.Message);
}
if(ssh != null)
{
Console.WriteLine("Disconnecting...");
ssh.Close();
Console.WriteLine("OK");
}
}
}
}
Output:
Expected Output:
The SshExec class is for executing a single command. It logs into the server for each command, and executes the command you asked it to. It's similar (but not the same) as you logging in, typing cd .., logging out again, logging in again and typing ls - when you logged in the second time you were redirected to the home directory.
If you want to continue to use the SshExec, you should combine your commands so you can just execute a single command. In this instance, for example, you would execute ls ...
If you want to be able to issue multiple commands like an interactive session, look at the SshShell class instead of SshExec.
I am trying to reach one simple requirement. I would like to create a C# library that talks to the git executable. I am writing a version control tool for my team that will allow access to git commands for non tech-savvy individuals. Unfortunately, I can not use any third party DLL's (I am using Unity and I do not want to push the requirement for Unity pro due to plugins), otherwise I would use GitSharp or something along those lines.
Currently, I have a function called RunGitCommand that is meant to do all my bidding. This snippet is as follows:
private void RunGitCommand(string executablePath, string arguments, int maxCommandDurationMilliseconds)
{
using (AutoResetEvent outputWaitHandle = new AutoResetEvent(false))
using (AutoResetEvent errorWaitHandle = new AutoResetEvent(false))
{
try
{
CommandOutput = string.Empty;
CommandError = string.Empty;
ProcessStartInfo processStartInfo = new ProcessStartInfo();
processStartInfo.CreateNoWindow = true;
processStartInfo.FileName = executablePath;
processStartInfo.Arguments = arguments;
processStartInfo.UseShellExecute = false;
processStartInfo.RedirectStandardError = true;
processStartInfo.RedirectStandardOutput = true;
int processedTime = 0;
using (Process process = new Process())
{
StringBuilder outputData = new StringBuilder();
StringBuilder errorData = new StringBuilder();
process.StartInfo = processStartInfo;
process.OutputDataReceived += (sender, e) => {
outputWaitHandle.Set();
if (e.Data == null)
{
//outputWaitHandle.Set();
}
else
{
outputData.AppendLine(e.Data);
}
};
process.ErrorDataReceived += (sender, e) =>
{
errorWaitHandle.Set();
if (e.Data == null)
{
//errorWaitHandle.Set();
}
else
{
errorData.AppendLine(e.Data);
}
};
process.Start();
if(process.Id == 0)
{
Environment.LogError("Process id is 0. Aborting.");
return;
}
RunningProcessDescriptor processDescriptor = new RunningProcessDescriptor(maxCommandDurationMilliseconds, process.Id);
ProcessIds.Add(processDescriptor);
Thread.Sleep(200);
int newMaxTime = maxCommandDurationMilliseconds - 100;
process.BeginOutputReadLine();
process.BeginErrorReadLine();
if (process.WaitForExit(newMaxTime) && outputWaitHandle.WaitOne(newMaxTime) && errorWaitHandle.WaitOne(newMaxTime))
{
process.CancelOutputRead();
process.CancelErrorRead();
CommandOutput = outputData.ToString();
CommandError = errorData.ToString();
string combinedOutput = string.Join(System.Environment.NewLine, new string[]{ CommandError, CommandOutput }).Trim();
BatchOutput = string.Join(System.Environment.NewLine, new string[]{ BatchOutput, string.Format("----------// {0} {1} //----------", executablePath, arguments), CommandOutput }).Trim();
BatchError = string.Join(System.Environment.NewLine, new string[]{ BatchError, string.Format("----------// {0} {1} //----------", executablePath, arguments), CommandError }).Trim();
InterpretErrorsAndAddToLists(combinedOutput);
}
else
{
process.Close();
process.WaitForExit();
}
ProcessIds.Remove(processDescriptor);
}
}
catch(Exception genericException)
{
Environment.LogError(genericException.Message);
Environment.LogError(genericException.StackTrace);
}
}
}
Another thing to note is that I'm using this to run these commands so that they don't execute on the main thread:
ThreadPool.QueueUserWorkItem
As you should be able to identify by my commenting and layout, this function is the result of hours of troubleshooting and I am willing to try anything at this point to make it work. I've already moved to using asynchronous calls for receiving the output/error streams, and even added AutoResetEvent objects to cause my threads to wait for each other (although I am not completely familiar with them and might be doing something wrong).
No matter what I try, it seems to randomly hang and not allow the process to exit. When I manually kill the git process, the output is spit out (and is usually right) and the process exits and everything works as normal.
I'm at the point in troubleshooting and frustration where I need professional input. Here are my questions:
Is there something blatantly or obviously wrong with my code below? If I'm doing it wrong, please advise me how to properly execute this code.
Is there another solution where I do not need to include third party DLL files and can just use raw .NET to grab the git console process and interact with it (on mac and pc)?
Other alternatives to these approaches, such as one i've been considering, that uses a "client/server" architecture. I can use third party dll's and whatnot in a separate downloadable program that communicates to the git plugin via TCP to send and receive output/input to the git process. This one is much more work, but would potentially take less time than troubleshooting the git only version.
Just let me know what your professional opinions are so that I can rest at night :).
I look forward to talking with everyone!
-Zack
I tend to use a very simple piece of code for executing a non-interactive command, and getting the standard output from the result. I would suggest starting from something like this, and checking it doesn't hang. Then build any additional logic from there.
private static string ExecuteCommand(string command, string arguments)
{
command = System.Environment.ExpandEnvironmentVariables(command);
var process = new Process
{
StartInfo =
{
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
FileName = command,
Arguments = arguments
}
};
process.Start();
process.WaitForExit();
return process.StandardOutput.ReadToEnd();
}
It's often hard to isolate the problem when there's so much 'dead wood' in the code. Strip it back to the bare bones, and see if you can isolate the problem from there.