Execute command with SSH.NET without blocking and keep it running after closing the connection
This is not really SSH.NET question, but rather a generic SSH/shell question.
See How to make a program continue to run after log out from ssh?
So this should do:
client.RunCommand("nohup /home/my-script.sh > foo.out 2> foo.err < /dev/null &");
SshNet Command Execution
The answer to this is the same as the one for your question using the Tamir SSH library - you are using a class intended for executing single commands, which logs in and executes the command each time. You will not be able to use this like a shell.
If you want to implement interactive commands, you'll need something which keeps the connection open, and you'll need to deal with working out when a command has finished (which is normally done by looking for the prompt pattern). The Shell class provides methods for dealing with this - you can find examples of its use on their website.
Providing input/subcommands to a command (cli) executed with SSH.NET SshClient.RunCommand
AFAIK, cli
is a kind of a shell/interactive program. So I assume you have tried to do something like:
client.RunCommand("cli");
client.RunCommand("some cli subcommand");
That's wrong. cli
will keep waiting for subcommands and never exit, until you explicitly close it with a respective command (like exit
). And after it exits, the server will try to execute the cli subcommand
as a separate top-level command, failing too.
You have to feed the "cli subcommand" to the input of the cli
command. But SSH.NET unfortunately does not support providing an input with the SshClient.RunCommand
/SshClient.CreateCommand
interface. See Allow writing to SshCommand.
There are two solutions:
Use the appropriate syntax of the server's shell to generate the input on the server, like:
client.RunCommand("echo \"cli subcommand\" | cli");
Or use a shell session (what is otherwise a not recommended approach for automating a command execution).
Use
SshClient.CreateShellStream
orSshClient.CreateShell
and send the commands to its input:"cli\n" + "cli subcommand\n"
For a sample code see Providing subcommands to a command (sudo/su) executed with SSH.NET SshClient.CreateShellStream or C# send Ctrl+Y over SSH.NET.
How to run commands on SSH server in C#?
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;
}
Commands fail with “command not found”, when executed using SSH.NET CreateCommand in C#
The SSH.NET SshClient.CreateCommand
(or SshClient.RunCommand
) does not run shell in "login" mode and does not allocate a pseudo terminal for the session. As a consequence a different set of startup scripts is (might be) sourced (particularly for non-interactive sessions, .bash_profile
is not sourced), than in your regular interactive SSH session. And/or different branches in the scripts are taken, based on an absence/presence of TERM
environment variable.
Possible solutions (in preference order):
Fix the command not to rely on a specific environment. Use a full path to
ideviceinstaller
in the command. E.g.:/path/to/ideviceinstaller ...
If you do not know the full path, on common *nix systems, you can use
which ideviceinstaller
command in your interactive SSH session.Fix your startup scripts to set the
PATH
the same for both interactive and non-interactive sessions.Try running the script explicitly via login shell (use
--login
switch with common *nix shells):bash --login -c "ideviceinstaller ..."
If the command itself relies on a specific environment setup and you cannot fix the startup scripts, you can change the environment in the command itself. Syntax for that depends on the remote system and/or the shell. In common *nix systems, this works:
PATH="$PATH;/path/to/ideviceinstaller" && ideviceinstaller ...
Another (not recommended) is to use "shell" channel to execute the command via
SshClient.CreateShellStream
orSshClient.CreateShell
as these allocate pseudo terminalShellStream shellStream = client.CreateShellStream(string.Empty, 0, 0, 0, 0, 0);
shellStream.Write("ideviceinstaller\n");
while (true)
{
string s = shellStream.Read();
Console.Write(s);
}Using the shell and pseudo terminal to automate a command execution can bring you nasty side effects.
NOT getting the full output from ssh.net SshCommand
Okay. As @MartinPrikryl pointed out in the comments above.
The SshCommand() method does not access the shell. (it uses the "exec" channel in the SSH.NET API).
If you want to access the "shell" using SSH.NET, then you will need to use the "ShellStream".
here is a link to 27 different examples of how others have used ShellStream in the real world.
https://csharp.hotexamples.com/examples/Renci.SshNet/ShellStream/-/php-shellstream-class-examples.html
SSH.NET is not executing command on device
Your server obviously does not support the SSH "exec" channel that is used behind the SSH.NET SshClient.RunCommand
method and PLink's plink.exe command
syntax.
Or actually it does support it, but incorrectly. It seems to start an interactive session (a shell), instead of executing the command.
To confirm this assumption, try this with PLink:
echo AT| plink username@ip -pw password > log.txt
If that works, you may need to use the SshClient.CreateShell
(SSH "shell" channel") and write the command to its input stream, instead of using the SshClient.RunCommand
. While this is generally a wrong approach, you need to take this approach due to your broken server.
A similar question for Python/Paramiko:
Executing command using Paramiko exec_command on device is not working.
Related Topics
Creating Simple C++.Net Wrapper. Step-By-Step
Is There a Tool for Finding Unreferenced Functions (Dead, Obsolete Code) in a C# App
Linq: How to Exclude Condition If Parameter Is Null
Load Different CSS File Based on Browser
How to Display a Loading Control While a Process Is Waiting for Be Finished
Finding the Concrete Type Behind an Interface Instance
Linq to Entities Generated SQL
SQL Injections with Replace Single-Quotation and Validate Integers
Spawn a New Thread to Open a New Window and Close It from a Different Thread
System.Data.Sqlclient.Sqlexception: Invalid Column Name 'Phone_Types_Phone_Type_Id'
Htmlagilitypack and Dynamic Content Issue
Does C# Have a Way of Giving Me an Immutable Dictionary
Calling a JavaScript Function in The C# Webbrowser Control
Recursive Linq Query: Select Item and All Children with Subchildren