Is it possible to execute multiple SSH commands from a single login session with SSH.NET?

LightCC picture LightCC · Oct 20, 2017 · Viewed 7.5k times · Source

I'm using C# with SSH.NET and have been able to get a client SSH connection working fine to executing commands across SSH. I can connect to a Linux install I have on a hypervisor VM with no issues, read back the StdOut and StdErr, etc.

However, each command is like a completely new session - it always starts from /home/user, even if you finish from another directory with the last command. You can string multiple commands together with semicolons:

pwd; cd ..; pwd
/home/user
/home

but then if I execute:

pwd
/home/user

I'm back at the home directory for that user.

I'm not disconnecting and closing the session between the commands. Here is some example code for several commands in a row, using changes in path to illustrate the issue:

public void MultiCommandTest()
{
    string output1, output2;

    var host = Config.SshHostName;
    var port = Config.SshHostPort;
    var user = Config.SshUsername;
    var pass = Config.SshPassword;
    var auth = new PasswordAuthenticationMethod(user, pass);
    var info = new ConnectionInfo(host,port,user,auth);

    using (var ssh = new SshClient(info))
    {
        ssh.Connect();
        var cmd1 = ssh.CreateCommand("pwd; cd ..; pwd");
        output1 = cmd1.Execute();
        var cmd2 = ssh.CreateCommand("pwd; cd ..; pwd");
        output2 = cmd2.Execute();
        ssh.Disconnect();

    }

    string output = output1 + Environment.NewLine + output2;
    System.Windows.Forms.Clipboard.SetText(output);
    System.Windows.Forms.MessageBox.Show(output);
}

I realize there is a difference between an interactive and non-interactive session when connecting to Bash, etc., but don't know enough about linux, etc. to know if that could be part of the solution (i.e. maybe it has to be resolved in the login side with .profile, .bashrc, or similar?).

Question:

So - is there a way to have a shell-like connection with SSH (and SSH.NET in particular)? Or do I just need to serialize any sets of commands and send them all at once, always starting from scratch for any new command?

Answer

Martin Prikryl picture Martin Prikryl · Oct 20, 2017

You can implement a shell session using:

  • SshClient.CreateShellStream (ShellStream class) — It gives you one Stream interface, that you can both write and read. This interface is useful, when you want to interact with the shell directly.
  • or SshClient.CreateShell (Shell class) — Where you yourself provide separate streams for input, output and error output. This interface is useful, when you want to link/pipe the input and output to existing streams (like standard input and output or files). Though you can still interact directly, if you use PipeStream. See also SSH.NET doesn't process my shell input commands.

But using "shell" is really not recommended for automating commands execution. A shell is an interactive feature, that does not have a deterministic behavior.

You better use an exec channel, see How to run commands on SSH server in C#?