简体   繁体   中英

PHP ssh2_exec no feof on empty stdout

This PHP snippet should execute a simple command via SSH (stripped down to minimal working example):

$sshconn = ssh2_connect($HostAddr, 22);
ssh2_auth_pubkey_file($sshconn, $user, $sshkey . '.pub', $sshkey);
$stdout = ssh2_exec($sshconn, 'echo hello');
if ($stdout !== false)
{
    stream_set_blocking($stdout, true);
    while (!feof($stdout))
    {
        $proc_stdout = fgets($stdout, 3E6);
        if ($proc_stdout !== false) echo $proc_stdout;
    }
    fclose($stdout);
}

Works great as long as there is any output to stdout. However, if stdout remains empty, the loop turns into an endless loop.

$stdout = ssh2_exec($sshconn, 'echo hello >&2');

How do I read the stdout properly if

  • stdout may be empty
  • but stdout may also very large (several Gigabytes, impossible to slurp into a variable by a single call to stream_get_contents or the like).

Addendum: My real world code that hangs executes mysqldump with an unknown parameter. Obviously, echo -n >&2 works as expected, although its stdout is also empty.

If anyone stumbles upon this too: stderr has to be read too.

$sshconn = ssh2_connect($HostAddr, 22);
ssh2_auth_pubkey_file($sshconn, $user, $sshkey . '.pub', $sshkey);

$stdout = ssh2_exec($sshconn, 'echo Hello >&2');
if ($stdout !== false)
{
    $stderr = ssh2_fetch_stream($stdout, SSH2_STREAM_STDERR);
    stream_set_blocking($stdout, false);
    stream_set_blocking($stderr, false);
    while (!feof($stdout))
    {
            $proc_stdout = fgets($stdout, 3E6);
            if ($proc_stdout !== false) echo $proc_stdout;
            $proc_stderr = fgets($stderr, 3E6);
            if ($proc_stderr !== false) fwrite(STDERR, $stderr);
    }
    fclose($stdout); fclose($stderr);
}

The disadvantage of this is that the SSH connection is no longer usable afterwards (a further ssh2_exec cannot be executed).

A possible solution is to keep both streams blocking and read stdout and stderr non-interleaved:

$stdout = ssh2_exec($sshconn, 'echo Hello >&2');
$stderr = ssh2_fetch_stream($stdout, SSH2_STREAM_STDERR);
stream_set_blocking($stdout, true);
stream_set_blocking($stderr, true);
while ($content = fread($stdout, 3E6)) echo $content;
while ($content = fread($stderr, 3E6)) fwrite(STDERR, $content);

I invite anyone who knows how to have the streams interleaved and keep the SSH connection usable to post another answer.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM