Skip to content

Commit 3de93e9

Browse files
committed
Document streaming execution examples and usage scenarios
Expand documentation with detailed examples for `SshCommandStream`, including incremental output processing, file redirection, real-time log monitoring, and PTY usage. Update comparisons between standard and streaming command execution methods in relevant topics. Refresh README and test documentation to include streaming support references.
1 parent 1dcddb3 commit 3de93e9

File tree

3 files changed

+170
-5
lines changed

3 files changed

+170
-5
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ A modern, cross-platform .NET library providing managed bindings for libssh2, en
3636
| | File download | `ReadFile`, `ReadFileAsync` ||
3737
| **Command Execution** | | | |
3838
| | One-shot command execution | `ExecuteCommand`, `ExecuteCommandAsync` ||
39+
| | Streaming command execution | `ExecuteCommandStreaming`, `ExecuteCommandStreamingAsync` ||
3940
| | Exit code retrieval | `SshCommandResult.ExitCode` ||
4041
| | Exit signal retrieval | `SshCommandResult.ExitSignal` ||
4142
| | stdout/stderr separation | `SshCommandResult.Stdout`, `SshCommandResult.Stderr` ||

docs/Writerside/topics/command-execution.md

Lines changed: 165 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,164 @@ catch (OperationCanceledException)
233233
}
234234
```
235235

236+
## Streaming Command Execution
237+
238+
For commands that produce large amounts of output or when you need to process output as it arrives, use streaming execution. Unlike standard command execution which buffers all output in memory, streaming provides direct access to stdout and stderr streams.
239+
240+
### Basic Streaming
241+
242+
```c#
243+
using NullOpsDevs.LibSsh;
244+
using NullOpsDevs.LibSsh.Core;
245+
using NullOpsDevs.LibSsh.Credentials;
246+
247+
var session = new SshSession();
248+
session.Connect("example.com", 22);
249+
session.Authenticate(SshCredential.FromPassword("user", "password"));
250+
251+
// Execute command with streaming output
252+
using var stream = session.ExecuteCommandStreaming("cat /var/log/syslog");
253+
254+
// Read stdout as a stream
255+
using var reader = new StreamReader(stream.Stdout);
256+
while (!reader.EndOfStream)
257+
{
258+
var line = reader.ReadLine();
259+
Console.WriteLine(line);
260+
}
261+
262+
// Get exit code after consuming the streams
263+
var result = stream.WaitForExit();
264+
Console.WriteLine($"Exit code: {result.ExitCode}");
265+
```
266+
267+
### Stream Output Directly to a File
268+
269+
Streaming is ideal for downloading large command output without buffering in memory:
270+
271+
```c#
272+
using var stream = session.ExecuteCommandStreaming("mysqldump database_name");
273+
274+
// Stream directly to a file
275+
using var file = File.Create("backup.sql");
276+
stream.Stdout.CopyTo(file);
277+
278+
var result = stream.WaitForExit();
279+
if (result.ExitCode != 0)
280+
{
281+
Console.WriteLine("Backup failed!");
282+
}
283+
```
284+
285+
### Reading Both Stdout and Stderr
286+
287+
Access both output streams separately:
288+
289+
```c#
290+
using var stream = session.ExecuteCommandStreaming("./build.sh");
291+
292+
// Read both streams
293+
using var stdoutReader = new StreamReader(stream.Stdout);
294+
using var stderrReader = new StreamReader(stream.Stderr);
295+
296+
var stdout = stdoutReader.ReadToEnd();
297+
var stderr = stderrReader.ReadToEnd();
298+
299+
var result = stream.WaitForExit();
300+
301+
Console.WriteLine("Output:");
302+
Console.WriteLine(stdout);
303+
304+
if (!string.IsNullOrEmpty(stderr))
305+
{
306+
Console.WriteLine("Errors:");
307+
Console.WriteLine(stderr);
308+
}
309+
```
310+
311+
### Async Streaming
312+
313+
For non-blocking streaming operations:
314+
315+
```c#
316+
using var stream = await session.ExecuteCommandStreamingAsync("tail -f /var/log/app.log",
317+
cancellationToken: cancellationToken);
318+
319+
using var reader = new StreamReader(stream.Stdout);
320+
321+
// Process lines as they arrive
322+
while (!reader.EndOfStream)
323+
{
324+
var line = await reader.ReadLineAsync();
325+
ProcessLogLine(line);
326+
}
327+
```
328+
329+
### Processing Incremental Output
330+
331+
For commands that produce output over time (like progress indicators), read incrementally:
332+
333+
```c#
334+
using var stream = session.ExecuteCommandStreaming("./slow-process.sh");
335+
336+
var buffer = new byte[4096];
337+
int bytesRead;
338+
339+
while ((bytesRead = stream.Stdout.Read(buffer, 0, buffer.Length)) > 0)
340+
{
341+
var text = Encoding.UTF8.GetString(buffer, 0, bytesRead);
342+
Console.Write(text); // Output as it arrives
343+
}
344+
345+
var result = stream.WaitForExit();
346+
```
347+
348+
### Streaming with PTY
349+
350+
Combine streaming with PTY for commands that require terminal features:
351+
352+
```c#
353+
var options = new CommandExecutionOptions
354+
{
355+
RequestPty = true,
356+
TerminalType = TerminalType.Xterm256Color
357+
};
358+
359+
using var stream = session.ExecuteCommandStreaming("htop -n 1", options);
360+
361+
using var reader = new StreamReader(stream.Stdout);
362+
var output = reader.ReadToEnd();
363+
Console.WriteLine(output); // Contains ANSI color codes
364+
365+
stream.WaitForExit();
366+
```
367+
368+
### Important Usage Notes
369+
370+
1. **Consume streams before getting exit code**: Always read from `Stdout` and `Stderr` before calling `WaitForExit()`. The streams must be consumed to allow the command to complete.
371+
372+
2. **Dispose the stream**: The `SshCommandStream` owns the underlying SSH channel. Always dispose it when done:
373+
```c#
374+
using var stream = session.ExecuteCommandStreaming("command");
375+
// ... use streams ...
376+
```
377+
378+
3. **Streams are read-only**: The `Stdout` and `Stderr` properties return read-only streams. You cannot write to them.
379+
380+
4. **WaitForExit can only be called once**: After calling `WaitForExit()`, the streams are no longer readable.
381+
382+
### When to Use Streaming vs. Standard Execution
383+
384+
| Scenario | Use |
385+
|----------|-----|
386+
| Small command output (< 1MB) | `ExecuteCommand()` |
387+
| Large file downloads via command | `ExecuteCommandStreaming()` |
388+
| Real-time log monitoring | `ExecuteCommandStreaming()` |
389+
| Simple scripts and automation | `ExecuteCommand()` |
390+
| Processing output incrementally | `ExecuteCommandStreaming()` |
391+
| Commands with predictable output | `ExecuteCommand()` |
392+
| Memory-constrained environments | `ExecuteCommandStreaming()` |
393+
236394
## Advanced: Channel Settings
237395

238396
For fine-tuning performance, adjust channel settings:
@@ -363,10 +521,13 @@ finally
363521

364522
## See Also
365523

366-
- `SshSession.ExecuteCommand()` (SshSession.cs:401) - Execute commands synchronously
367-
- `SshSession.ExecuteCommandAsync()` (SshSession.cs:536) - Execute commands asynchronously
368-
- `CommandExecutionOptions` (CommandExecutionOptions.cs:11) - Configure command execution
369-
- `SshCommandResult` (SshCommandResult.cs:6) - Command execution results
524+
- `SshSession.ExecuteCommand()` - Execute commands synchronously
525+
- `SshSession.ExecuteCommandAsync()` - Execute commands asynchronously
526+
- `SshSession.ExecuteCommandStreaming()` - Execute commands with streaming output
527+
- `SshSession.ExecuteCommandStreamingAsync()` - Execute commands with streaming output asynchronously
528+
- `SshCommandStream` - Streaming command result with stdout/stderr streams
529+
- `CommandExecutionOptions` - Configure command execution
530+
- `SshCommandResult` - Command execution results
370531
- [Authentication](authentication.md) - Authenticate before executing commands
371532
- [Advanced Terminal Control](advanced-terminal-control.md) - Configure terminal modes for PTY
372533
- [Session Timeouts](session-timeouts.md) - Set timeouts for long-running commands

docs/Writerside/topics/running-tests-locally.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,10 @@ The test suite includes the following categories:
7575
- Command with exit codes
7676
- Command with stderr output
7777
- Commands with PTY allocation
78-
- Long-running commands
78+
- Streaming command stdout/stderr
79+
- Streaming to file
80+
- Async streaming
81+
- Incremental output streaming
7982

8083
### File Transfer Tests
8184
- SCP upload (small, medium, large files)

0 commit comments

Comments
 (0)