Streaming Output

Long-running programs can produce output well before they exit. Session.enter() exposes that output as it arrives, so Python can process a log follower, monitor, backup tool, or packet capture incrementally.

Stream output

from sshscript import Session

session = Session()
try:
    with session.enter("ping -c 4 example.net") as process:
        for line in process.stdout(10):
            print(line, end="")
finally:
    session.close(strict=True)

process.stdout(10) returns an iterator. It yields received stdout chunks, normally lines, and resets its ten-second timer whenever new output arrives. If nothing new arrives before the timeout, it raises TimeoutError.

For a continuous command, supply an exit action:

with session.enter(
    "tail -F /var/log/syslog",
    exit=chr(3),
) as process:
    for line in process.stdout(30):
        if "ERROR" in line:
            print("alert:", line, end="")

exit=chr(3) sends Ctrl-C when the interactive context ends.

Stop on a condition

matches = 0

with session.enter(
    "tail -F /var/log/myapp.log",
    exit=chr(3),
) as process:
    for line in process.stdout(60):
        if "completed" in line:
            matches += 1
        if matches == 10:
            break

Leaving the context after break sends the configured exit action. An intentionally endless command should always have an exit action or another well-defined termination mechanism.

Iterator options

process.stdout(timeout=None, silent=False, shift=True)
Argument Behavior
timeout Seconds to wait after the last output; None waits indefinitely.
silent If False, inactivity raises TimeoutError. If True, iteration ends normally.
shift If True, yielded chunks leave the live buffer. If False, the buffer is retained.

Treat unexpected silence as an error:

try:
    with session.enter(
        "tcpdump -n -i eth0",
        exit=chr(3),
    ) as process:
        for line in process.stdout(15):
            process_packet(line)
except TimeoutError as exc:
    raise RuntimeError(
        "tcpdump produced no output for 15 seconds"
    ) from exc

For an expected quiet interval, use silent=True:

with session.enter(
    "./wait-for-work.sh",
    exit=chr(3),
) as process:
    for line in process.stdout(5, silent=True):
        print(line, end="")

Preserve accumulated output

Iteration consumes output by default. Use shift=False when the complete buffer is needed afterward:

with session.enter(
    "./progressive-report.sh",
    exit=chr(3),
) as process:
    for line in process.stdout(20, shift=False):
        print(line, end="")
        if "DONE" in line:
            break

    report = str(process.stdout)

Keep the consuming default for unbounded streams so processed output does not accumulate indefinitely.

Remote and privileged streams

with local.connect("ops@example.net") as remote:
    with remote.sudo(password=password) as root:
        with root.enter(
            "journalctl -f -u nginx",
            exit=chr(3),
        ) as process:
            for line in process.stdout(30):
                if "error" in line.lower():
                    print(line, end="")

The output-handling code is the same for local, remote, nested, and privileged Sessions.

Optional Dollar syntax

with $.enter("journalctl -f -u nginx", exit=chr(3)):
    for line in $.stdout(30):
        if "error" in line.lower():
            print(line, end="")

Last Updated: 2026-09-14 18:02:02