Execute multiple dependent SSH commands using SSH.NET in C# - c#

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();

Related

I am using System.Diagnostics.Process to run the sqlite special command but only half of the commands get executed

string connectionString = #"Data Source=C:\sqlite\test.db; Version=3; FailIfMissing=True; Foreign Keys=True;";
SQLiteConnection conn = new SQLiteConnection(connectionString);
conn.Open();
string batFilePath = #"D:\mockforbat.bat";
if (!File.Exists(batFilePath))
{
using (FileStream fs = File.Create(batFilePath))
{
fs.Close();
}
}
using (StreamWriter sw = new StreamWriter(batFilePath))
{
sw.WriteLine(#"C:");
sw.WriteLine(#"cd\");
sw.WriteLine(#"cd sqlite");
sw.WriteLine(#"sqlite3 test.db");
sw.WriteLine(#".mode csv");
sw.WriteLine(#".import D:/Ashif/SQLITE/Bulk.csv excelUpload");
}
Process process = Process.Start(batFilePath);
process.WaitForExit();
When I execute this command, it will execute only up to the "sqlite3 test.db" line, and the other commands are not executed.
The output I get after executing of the code:
You didn't explicitly ask a question, so I assume the question is "how can I call the sqlite command line from my program with the relevant arguments?"
The sqlite3 command line interface is interactive by default, but you probably don't want that when using it as part of a script/automated execution.
The manual shows a -init file argument that should be more appropriate for what you want to achieve.
Try to add your meta-commands (.mode csv, .import x...) in a separate myFile, and execute sqlite3 -init myFile and it should work.
Aside from this main question, I see potential problems with your code:
You are opening a connection to a database with new SQLiteConnection(connectionString); but you are never using this connection. Instead, you are connecting again to your database by executing the sqlite3 test.db in your bat file.
if you are using Process.Start() you don't need a bat file, you can directly call sqlite3 with the required arguments. (Though you will probably need the full path of sqlite3 for this to work)
To sum up, you could use something like that (untested, adapt as needed):
var sqliteScriptPath = #"D:\myScript.txt";
var scriptContent = #"
.mode csv
.open test.db
.import D:/Ashif/SQLITE/Bulk.csv excelUpload";
File.WriteAllText(sqliteScriptPath, scriptContent);
Environment.CurrentDirectory = #"C:\sqlite"; // If you use relative paths somewhere in your command you will need something like that
// You can also create a ProcessStartInfo instance, and set its WorkingDirectory property before giving it to Process.Start()
Process process = Process.Start("sqlite3", $"-init {sqliteScriptPath}");
process.WaitForExit();

Sending Ctrl+break sequence through Plink

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.

How to Debug Remote Command

I need to execute this command on our remote Skype server:
SEFAUtil.exe /server:lyncserver.domain1.co.uk sip:MySelf#domain.com /addteammember:sip:OtherUser#domain.com /delayringteam:10
which adds a colleague to my team call group.
I am able to run the command on the server itself, and the code below works when sending other commands to that server:
var processToRun = new[] { process };
var connection = new ConnectionOptions();
var wmiScope = new ManagementScope(String.Format("\\\\{0}\\root\\cimv2", LyncServer), connection);
var wmiProcess = new ManagementClass(wmiScope, new ManagementPath("Win32_Process"), new ObjectGetOptions());
var reason = wmiProcess.InvokeMethod("Create", processToRun);
However, when process is the string:
"cmd /c cd /d C:\\Program Files\\Microsoft Lync Server 2013\\ResKit && SEFAUtil.exe /server:lyncserver.domain1.co.uk sip:MySelf#domain.com /addteammember:sip:OtherUser#domain.com /delayringteam:10"
Then the user is not added to the team call group.
I can see that reason contains the uint 0, which usually indicates success - but the actual command is clearly failing.
I also tried adding > C:\users\user.name\desktop\output.txt and 2> C:\users\user.name\desktop\output.txt to the end of the command, but they just created empty text files, so not very useful!
Update
I tried changing the command to the following:
const string LyncServer = "server.domain1.co.uk";
const string ResKitPath = #"C:\Program Files\Microsoft Lync Server 2013\ResKit";
var command = "SEFAUtil.exe /server:{LyncServer} sip:MySelf#domain.com /addteammember:sip:OtherUser#domain.com /delayringteam:10";
var process = $"cmd /c cd /d \"{ResKitPath}\" && {command}";
So that the path containing spaces is double-quoted and the slashes are not being escaped, but with the same results.
Does anyone know of another way of debugging this or retrieving the output for the newly created process?
I've had a similar issue, mine was that the command shell needed to run elevated. SEFA is a bit naff at giving good error messages, and fails silently.

How to run several commands with SSH.Net? [duplicate]

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.

SharpSsh Command Execution

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.

Categories

Resources