略微加速

PHP官方手册 - 互联网笔记

PHP - Manual: ssh2_exec

2024-11-14

ssh2_exec

(PECL ssh2 >= 0.9.0)

ssh2_execExecute a command on a remote server

说明

ssh2_exec(
    resource $session,
    string $command,
    string $pty = ?,
    array $env = ?,
    int $width = 80,
    int $height = 25,
    int $width_height_type = SSH2_TERM_UNIT_CHARS
): resource|false

Execute a command at the remote end and allocate a channel for it.

参数

session

An SSH connection link identifier, obtained from a call to ssh2_connect().

command

pty

env

env may be passed as an associative array of name/value pairs to set in the target environment.

width

Width of the virtual terminal.

height

Height of the virtual terminal.

width_height_type

width_height_type should be one of SSH2_TERM_UNIT_CHARS or SSH2_TERM_UNIT_PIXELS.

返回值

Returns a stream on success 或者在失败时返回 false.

范例

示例 #1 Executing a command

<?php
$connection 
ssh2_connect('shell.example.com'22);
ssh2_auth_password($connection'username''password');

$stream ssh2_exec($connection'/usr/local/bin/php -i');
?>

参见

add a noteadd a note

User Contributed Notes 17 notes

up
9
magicaltux at php dot net
13 years ago
The "pty" parameter is not documented.

You should pass a pty emulation name ("vt102", "ansi", etc...) if you want to emulate a pty, or NULL if you don't.

Passing false will convert false to a string, and will allocate a "" terminal.
up
7
Rainer Perske
2 years ago
The following function can be used to read both stdout and stderr even if both streams contain gigabytes of data. It solves most problems mentioned in other comments, like blocking, like further using the existing connection etc.

  function ssh2_run($ssh2,$cmd,&$out=null,&$err=null){
    $result=false;
    $out='';
    $err='';
    $sshout=ssh2_exec($ssh2,$cmd);
    if($sshout){
      $ssherr=ssh2_fetch_stream($sshout,SSH2_STREAM_STDERR);
      if($ssherr){
        # we cannot use stream_select() with SSH2 streams
        # so use non-blocking stream_get_contents() and usleep()
        if(stream_set_blocking($sshout,false) and
           stream_set_blocking($ssherr,false)
        ){
          $result=true;
          # loop until end of output on both stdout and stderr
          $wait=0;
          while(!feof($sshout) or !feof($ssherr)){
            # sleep only after not reading any data
            if($wait)usleep($wait);
            $wait=50000; # 1/20 second
            if(!feof($sshout)){
              $one=stream_get_contents($sshout);
              if($one===false){ $result=false; break; }
              if($one!=''){ $out.=$one; $wait=0; }
            }
            if(!feof($ssherr)){
              $one=stream_get_contents($ssherr);
              if($one===false){ $result=false; break; }
              if($one!=''){ $err.=$one; $wait=0; }
            }
          }
        }
        # we need to wait for end of command
        stream_set_blocking($sshout,true);
        stream_set_blocking($ssherr,true);
        # these will not get any output
        stream_get_contents($sshout);
        stream_get_contents($ssherr);
        fclose($ssherr);
      }
      fclose($sshout);
    }
    return $result;
  }
up
16
christopher dot millward at gmail dot com
11 years ago
There doesn't appear to be a way to get data from BOTH stderr and stdout streams simultaneously, or at least I've yet to find one.

The following code *should* get the error message written to stderr, but if the call to stream_get_contents() for stdout is run first, the subsequent call for stderr won't return anything. 

If the order of the statements is reversed, the call for stderr will return any errors and call for stdout will return nothing

<?php
// Run a command that will probably write to stderr (unless you have a folder named /hom)
$stream = ssh2_exec($connection, "cd /hom");
$errorStream = ssh2_fetch_stream($stream, SSH2_STREAM_STDERR);

// Enable blocking for both streams
stream_set_blocking($errorStream, true);
stream_set_blocking($stream, true);

// Whichever of the two below commands is listed first will receive its appropriate output.  The second command receives nothing
echo "Output: " . stream_get_contents($stream);
echo
"Error: " . stream_get_contents($errorStream);

// Close the streams       
fclose($errorStream);
fclose($stream);

?>

My initial suspicion is that either a) I've done something wrong  or b) the blocking nature of both streams means that by the time first stream has received data and returned, the buffer for the second stream has already emptied.

I've done preliminary tests with blocking disabled, but haven't had any luck there either.
up
7
jaimie at seoegghead dot com
13 years ago
I believe most of the problem that people are having here is that there is a misconception about what blocking *really* means.

Blocking means a read from the *stream* will wait until there is data.  Not necessarily all the data from the application -- but *some*.  So it won't help you at all if you're executing a command that doesn't write to stdout, or writes a whole lot of data.

So there are 2 problems:

1. If you need to know that a silent program is done via ssh2_exec, you'll need to signal it to yourself.  ssh2_exec will *not* block execution until the command is done executing.  And 2 consecutive ssh2_execs may execute asynchronously.  You could also log into a shell via ssh2_shell and parse up to the next prompt -- but that's overkill.  You can also do this by adding on some sort of sentinel at the end of your command, such as echo "@," and then block on reads until you see a "@."  Ensure "@" won't appear in the output, or escape the output via some encoding mechanism if you can't do that.

2. If the program takes awhile, you have the same problem.  You need to read until you're done.  So you need a sentinel value like the above.

3. Sleep() is just a bad idea here.  Commands rarely take the same amount of time to execute a command twice.  It may be OK if you're doing *one* thing and can just wait 5 seconds.  But that's not cool if it's something you're doing in a loop.

Hope this helps!
up
3
gakksimian at yahoo dot com
16 years ago
Executing remote Windows commands...
After some hair pulling, I thought I'd suggest a couple things that might help others:

1. Use 'ssh2_fetch_stream' to open a stderr stream according to the manual.
2. Windows shell commands require 'cmd /C [command]' to execute.  (I had forgotten this)

<?php
   
// code fragment
   
$stdout_stream = ssh2_exec($connection, 'cmd /C dir');
   
$stderr_stream = ssh2_fetch_stream($stdout_stream, SSH2_STREAM_STDERR);
?>

I didn't realize the need for the 'cmd /C' until I saw the stderr response 'dir: not found'.
up
3
edoceo at gmail dot com
5 years ago
Here's what I use for doing ssh2_exec for long running scripts, expanding on other examples.  It waits for a timeout and checks if both streams are finished.  Does not use blocking and it gets data from both STDOUT and STDERR.

<?php
$stdout
= ssh2_exec($ssh, $cmd);
$stderr = ssh2_fetch_stream($stdout, SSH2_STREAM_STDERR);
if (!empty(
$stdout)) {

   
$t0 = time();
   
$err_buf = null;
   
$out_buf = null;

   
// Try for 30s
   
do {

       
$err_buf.= fread($stderr, 4096);
       
$out_buf.= fread($stdout, 4096);

       
$done = 0;
        if (
feof($stderr)) {
           
$done++;
        }
        if (
feof($stdout)) {
           
$done++;
        }

       
$t1 = time();
       
$span = $t1 - $t0;

       
// Info note
       
echo "while (($tD < 20) && ($done < 2));\n";

       
// Wait here so we don't hammer in this loop
       
sleep(1);

    } while ((
$span < 30) && ($done < 2));

    echo
"STDERR:\n$err_buf\n";
    echo
"STDOUT:\n$out_buf\n";

    echo
"Done\n";

} else {
    echo
"Failed to Shell\n";
}
?>

Adjust your timeout as necessary.
up
1
Betsy Gamrat
15 years ago
It is also good to use register_shutdown_function to shred the keys after this runs.
up
1
lko at netuse dot de
14 years ago
if you are using exec function, and have problems with a output > 1000 lines
you should use 

<?php
                stream_set_blocking
($stream, true);
                while(
$line = fgets($stream)) {
                       
flush();
                        echo
$line."<br />";
                }
?>

except

<?php
                stream_set_blocking
($stream, true);
               echo
stream_get_contents($stream);
?>
up
1
yairl at savion dot huji dot ac dot il
17 years ago
If you want to run many exec orders throught ssh2 in the same process using the same variable $stream for exemple and to read the output for each order, you must close the stream after each order else you will not be able to read the next order output.

<?php
$stream
=ssh2_exec($conn_id,"/usr/bin/ls .");
   
stream_set_blocking( $stream, true );
   
$cmd=fread($stream,4096);
fclose($stream);

if(
ereg("public_html",$cmd)){
// ...........................................
}

$stream=ssh2_exec($conn_id,"/usr/bin/ls public_html");
   
stream_set_blocking( $stream, true );
   
$cmd=fread($stream,4096);
fclose($stream);

if(
ereg("images",$cmd)){
// ..........................................
}


$stream=ssh2_exec($conn_id,"/usr/bin/ls public_html/images");
   
stream_set_blocking( $stream, true );
   
$cmd=fread($stream,4096);
fclose($stream);

if(
ereg("blabla",$cmd)){
// ..........................................
}
?>
up
-1
dosentnd at gmail dot com
2 years ago
Running multiple commands shows this warning and second(and any next) command wont be executed:
- PHP Warning:  ssh2_exec(): Unable to request a channel from remote host in ...

Solution with fclose($stream) does not work, but this is simple solution which works for me:

<?php

$connection
= ssh2_connect('SOME SSH IP ADDRESS', 22);
ssh2_auth_password($connection, 'username', 'passwortd');

//those commands are for some HUAWEI enterprise routers

   
$stream = ssh2_exec( $connection, "screen-length 0 temporary\ndisplay isis name-table" );
   
stream_set_blocking( $stream, true );
   
$stream_out = ssh2_fetch_stream( $stream, SSH2_STREAM_STDIO );
    echo
stream_get_contents($stream_out);

?>

first command is: screen-length 0 temporary
second command is: display isis name-table
which is executed with \n in the same line.
You have to separete outputs of those commands later.

Looks like ssh2_exec allocates exec stream in session you are connected in, and for freeing it , you have to ssh2_disconnect.
Usualy you are ok to ssh2_exec one command then disconnect, then connect and ssh2_exec next command. This is how some other php libraries works as I have seen. But sometimes some variables are sesion dependent, and when you disconnect variables resets.
up
0
Betsy Gamrat
15 years ago
If the ssh2_exec takes awhile to run, and you need a handshake, you can use a file.  In this case, $flag names a handshake file that is written when the ssh script finishes.

<?php
                $stream
= ssh2_exec($connection, $command . " $public_key $private_key;");
               
$i=0;
                while (!
file_exists ($flag) && ($i < MAX_TIME))
                {
                       
sleep(1);
                       
$i++;
                }
               
$ret_val=($stream!=FALSE);
?>

This is an extract out of the bash script that is running.  Be sure to allow the webserver permission to read the file that the script writes.

echo 0 > $OUTFILE
chmod 666 $OUTFILE

One other note:  you can put more than one command in by separating them with ';' (semicolons).
up
0
tabber dot watts at gmail dot com
16 years ago
This is the best way I found to automatically run multiple commands or commands that might take longer then expected.  NOTE: this assumes that no where in the output is there the text '[end]' otherwise the function will end prematurely.  Hope this helps people.

<?php
$ip
= 'ip_address';
$user = 'username';
$pass = 'password';

$connection = ssh2_connect($ip);
ssh2_auth_password($connection,$user,$pass);
$shell = ssh2_shell($connection,"bash");

//Trick is in the start and end echos which can be executed in both *nix and windows systems.
//Do add 'cmd /C' to the start of $cmd if on a windows system.
$cmd = "echo '[start]';your commands here;echo '[end]'";
$output = user_exec($shell,$cmd);

fclose($shell);

function
user_exec($shell,$cmd) {
 
fwrite($shell,$cmd . "\n");
 
$output = "";
 
$start = false;
 
$start_time = time();
 
$max_time = 2; //time in seconds
 
while(((time()-$start_time) < $max_time)) {
   
$line = fgets($shell);
    if(!
strstr($line,$cmd)) {
      if(
preg_match('/\[start\]/',$line)) {
       
$start = true;
      }elseif(
preg_match('/\[end\]/',$line)) {
        return
$output;
      }elseif(
$start){
       
$output[] = $line;
      }
    }
  }
}

?>

[EDIT BY danbrown AT php DOT net: Contains a bugfix supplied by (jschwepp AT gmail DOT com) on 17-FEB-2010 to fix a typo in a function name.]
up
-2
Jim
11 years ago
It is important to note that this will not work ~all~ hosts that allow SFTP. We deny shell access, but allow SFTP using the sftp-internal subsystem.

When attempting to use ssh2_exec() with this configuration, and stream_set_blocking set to true, the script will hang indefinitely. When stream_set_blocking is set to false, nothing will be returned.

Even if you can SFTP files, be sure to check with your host to see if you have shell access.
up
-4
noreply at voicemeup dot com
9 years ago
*** IMPORTANT ***
If you are having issues getting STDERR on PHP 5.2.X, make sure to update to 5.2.17 and latest ssh2 extension from PECL.
up
-5
gwinans at gmail dot com
12 years ago
Oddly enough, I've noticed if I attempt to execute anything nothing happens. Now, I don't mean "No content is returned!".. I mean, "The command I told it to execute didn't execute!".

Unless the following pattern is followed:

<?php
$stream
= ssh2_exec($connection, "touch ~/some_fake_file", 'xterm');
stream_get_contents($stream);
?>

Nowhere in the manual does it imply that stream_get_contents is required for the actual execution to take place, but in this case.. it seems that way. So, if you're having trouble getting commands to execute, try using stream_get_contents().
up
-9
mbs
7 years ago
<?php
$connection
= ssh2_connect('shell.example.com', 22);
ssh2_auth_password($connection, 'username', 'password');

$stream = ssh2_exec($connection, '/usr/local/bin/php -i');
?>
up
-4
Jon-Eirik Pettersen
15 years ago
The command  may not finish before the function return if the stream is not set to blocking mode. You may have to set the stream to blocking mode in order to get any output from the command.

<?php
 
// [Open connection]
  // ...

 
$stream = ssh2_exec($connection, 'ps ax');
 
stream_set_blocking($stream, true);
 
 
// The command may not finish properly if the stream is not read to end
 
$output = stream_get_contents($stream);
 
 
// ...
  // [Close stream and connection]
?>

官方地址:https://www.php.net/manual/en/function.ssh2-exec.php

北京半月雨文化科技有限公司.版权所有 京ICP备12026184号-3