Execute commands on remote SSH servers and retrieve their output, exit codes, and error messages. NullOpsDevs.LibSsh makes it simple to run commands remotely and process their results.
Basic Command Execution
Execute a simple command and get the result:
using NullOpsDevs.LibSsh;
using NullOpsDevs.LibSsh.Credentials;
var session = new SshSession();
session.Connect("example.com", 22);
session.Authenticate(SshCredential.FromPassword("user", "password"));
// Execute a command
var result = session.ExecuteCommand("ls -la /home/user");
Console.WriteLine("Output:");
Console.WriteLine(result.Stdout);
Console.WriteLine($"Exit code: {result.ExitCode}");
Understanding Command Results
The SshCommandResult structure contains all information about the command execution:
var result = session.ExecuteCommand("whoami");
// Standard output (stdout)
Console.WriteLine($"Output: {result.Stdout}");
// Standard error (stderr)
if (!string.IsNullOrEmpty(result.Stderr))
{
Console.WriteLine($"Errors: {result.Stderr}");
}
// Exit code (0 typically means success)
if (result.ExitCode == 0)
{
Console.WriteLine("Command succeeded!");
}
else
{
Console.WriteLine($"Command failed with exit code: {result.ExitCode}");
}
// Exit signal (if the command was terminated by a signal)
if (result.ExitSignal != null)
{
Console.WriteLine($"Terminated by signal: {result.ExitSignal}");
}
// Overall success indicator
if (result.Successful)
{
Console.WriteLine("Execution was successful");
}
Async Command Execution
For non-blocking operations, use the async version:
var result = await session.ExecuteCommandAsync("apt update", cancellationToken: cancellationToken);
Console.WriteLine(result.Stdout);
Handling Command Errors
Always check the exit code to determine if a command succeeded:
var result = session.ExecuteCommand("cat /nonexistent/file");
if (result.ExitCode != 0)
{
Console.WriteLine("Command failed!");
Console.WriteLine($"Exit code: {result.ExitCode}");
Console.WriteLine($"Error output: {result.Stderr}");
}
else
{
Console.WriteLine(result.Stdout);
}
Commands Requiring PTY (Pseudo-Terminal)
Some commands need a pseudo-terminal to work correctly. Enable PTY when:
Commands check for terminal presence
You need ANSI color output and terminal control codes
Programs behave differently when attached to a terminal
using NullOpsDevs.LibSsh.Core;
var options = new CommandExecutionOptions
{
RequestPty = true,
TerminalType = TerminalType.Xterm256Color // Enable color support
};
var result = session.ExecuteCommand("ls --color=always", options);
Console.WriteLine(result.Stdout); // Contains ANSI color codes
Common PTY Use Cases
Getting Colored Output
var options = new CommandExecutionOptions
{
RequestPty = true,
TerminalType = TerminalType.Xterm256Color
};
var result = session.ExecuteCommand("ls --color=always", options);
// Output contains ANSI color codes that can be displayed in a terminal
Console.WriteLine(result.Stdout);
Running Commands That Check for TTY
// Some commands behave differently when they detect a terminal
var options = new CommandExecutionOptions { RequestPty = true };
var result = session.ExecuteCommand("./script-that-checks-tty.sh", options);
if (result.ExitCode == 0)
{
Console.WriteLine("Script executed successfully");
}
Customizing Terminal Settings
When requesting a PTY, you can customize the terminal:
using NullOpsDevs.LibSsh.Core;
using NullOpsDevs.LibSsh.Terminal;
var options = new CommandExecutionOptions
{
RequestPty = true,
TerminalType = TerminalType.Xterm256Color, // Terminal emulation type
TerminalWidth = 120, // Width in characters
TerminalHeight = 40, // Height in characters
};
var result = session.ExecuteCommand("top -b -n 1", options);
Console.WriteLine(result.Stdout);
Available Terminal Types
Terminal Type
Description
Use Case
Xterm
Standard xterm (default)
General purpose
XtermColor
xterm with color support
Basic color output
Xterm256Color
xterm with 256 colors
Full color support
VT100
DEC VT100
Legacy compatibility
VT220
DEC VT220
Legacy compatibility
Linux
Linux console
Linux-specific features
Screen
GNU Screen multiplexer
Screen sessions
Running Multiple Commands
Sequential Commands (with error handling)
// Stop on first failure
var result1 = session.ExecuteCommand("mkdir -p /tmp/myapp");
if (result1.ExitCode != 0)
{
Console.WriteLine("Failed to create directory");
return;
}
var result2 = session.ExecuteCommand("cd /tmp/myapp && touch file.txt");
if (result2.ExitCode != 0)
{
Console.WriteLine("Failed to create file");
return;
}
Console.WriteLine("All commands executed successfully");
Using Shell Operators
// Run multiple commands in a single execution (with && for conditional execution)
var result = session.ExecuteCommand("cd /tmp && mkdir test && cd test && pwd");
Console.WriteLine(result.Stdout); // Should print: /tmp/test
// Run commands regardless of success (with ;)
result = session.ExecuteCommand("command1; command2; command3");
// Run command in background (with &)
result = session.ExecuteCommand("long-running-task &");
Long-Running Commands
For commands that take a long time, consider using cancellation tokens:
using var cts = new CancellationTokenSource(TimeSpan.FromMinutes(5));
try
{
var result = await session.ExecuteCommandAsync(
"./long-script.sh",
cancellationToken: cts.Token
);
Console.WriteLine("Script completed!");
Console.WriteLine(result.Stdout);
}
catch (OperationCanceledException)
{
Console.WriteLine("Command timed out after 5 minutes");
}
Advanced: Channel Settings
For fine-tuning performance, adjust channel settings:
var options = new CommandExecutionOptions
{
WindowSize = 4 * 1024 * 1024, // 4MB window (default: 2MB)
PacketSize = 64 * 1024, // 64KB packets (default: 32KB)
};
var result = session.ExecuteCommand("cat large-file.txt", options);
Complete Example
Here's a comprehensive example showing various command execution scenarios:
using NullOpsDevs.LibSsh;
using NullOpsDevs.LibSsh.Core;
using NullOpsDevs.LibSsh.Credentials;
using NullOpsDevs.LibSsh.Exceptions;
var session = new SshSession();
try
{
// Connect and authenticate
session.Connect("example.com", 22);
session.Authenticate(SshCredential.FromPublicKeyFile(
"username",
"~/.ssh/id_ed25519.pub",
"~/.ssh/id_ed25519"
));
// 1. Simple command
Console.WriteLine("=== System Information ===");
var result = session.ExecuteCommand("uname -a");
Console.WriteLine(result.Stdout);
// 2. Command with error checking
Console.WriteLine("\n=== Disk Usage ===");
result = session.ExecuteCommand("df -h /");
if (result.ExitCode == 0)
{
Console.WriteLine(result.Stdout);
}
else
{
Console.WriteLine($"Error: {result.Stderr}");
}
// 3. Command requiring PTY
Console.WriteLine("\n=== Running with sudo ===");
var ptyOptions = new CommandExecutionOptions { RequestPty = true };
result = session.ExecuteCommand("sudo ls /root", ptyOptions);
if (result.ExitCode == 0)
{
Console.WriteLine(result.Stdout);
}
// 4. Multiple commands
Console.WriteLine("\n=== Creating test directory ===");
result = session.ExecuteCommand("mkdir -p /tmp/test && cd /tmp/test && pwd");
Console.WriteLine($"Created: {result.Stdout.Trim()}");
// 5. Async command
Console.WriteLine("\n=== Async operation ===");
result = await session.ExecuteCommandAsync("ps aux | head -n 5");
Console.WriteLine(result.Stdout);
}
catch (SshException ex)
{
Console.WriteLine($"SSH error: {ex.Message}");
}
finally
{
session.Dispose();
}
Best Practices
Always check exit codes:
Don't rely solely on Successful - check ExitCode
Zero typically means success, non-zero means failure
Use PTY when needed:
Enable for sudo, interactive commands, or color output
Disable for scripting and automation (default)
Handle both stdout and stderr:
Some programs write to stderr even on success
Check both streams for complete information
Quote arguments properly:
Use shell quoting for arguments with spaces
Example: "ls '/path with spaces/'"
Avoid command injection:
Don't concatenate user input directly into commands
Validate and sanitize all user-provided data
Set appropriate timeouts:
Use cancellation tokens for long-running commands
Consider session timeouts with SetSessionTimeout()
Common Issues
Issue: Command works locally but not via SSH
Solution: The command might require PTY. Enable RequestPty = true.
Issue: Interactive commands hang or fail
Solution: Interactive commands (password prompts, vim, interactive sudo) are not supported. The library cannot handle interactive terminal I/O. Use non-interactive alternatives:
For sudo: Configure passwordless sudo
For passwords: Pass via command arguments or environment variables
For interactive tools: Use non-interactive flags (e.g., vim -e for ex mode)
Issue: Color codes appear as garbage
Solution: Either enable PTY with appropriate terminal type, or disable colors in the command (e.g., ls --color=never).
Issue: Working directory not persisted
Solution: Each ExecuteCommand() starts in the user's home directory. Use cd /path && command or absolute paths.