有一个特殊需求,需要远程在管理多台主机,执行一些命令操作,又不想在远程机器上多装程序或者做接口。考虑了几个方案,nginx+lua,nginx+python,都比较麻烦。想来想去,干脆直接php执行shell好了。查了一下,有libssh2+ssh2的方案,了解之后觉得还不错,只需要在主控一端安装,并且是熟悉的PHP。于是装了个试试,封装了一下常用的几个ssh操作。
在Mac OS X上,通过brew安装libssh2,brew install libssh2 --build-from-source,安装好之后使用pecl安装ssh2扩展,pecl install ssh2 channel://pecl.php.net/ssh2-0.12,没有开启pecl的后面看通过源码安装,安装成功之后到php.ini当中添加extension,然后通过php -i | grep ssh看一下是不是有ssh了。
在Linux上,比如CentOS,可以通过yum install libssh2 libssh2-devel来安装。php的ssh扩展除了通过上方pecl方式安装之外,可以通过源码安装。从http://pecl.php.net/package/ssh2下载最新的源码包后参考下面的过程安装
tar vxzf ssh2-version.tgz cd ssh2-version phpize ./configure --with-ssh2 make make install
接下来到您php安装的扩展目录当中去找刚编译出来的so文件,比如我的在/usr/local/php/lib/php/extensions/no-debug-zts-20121212/目录下ssh2.so。到php.ini中添加一行extention=/path/to/ssh2.so。这样安装之后同样可以通过php -i | grep ssh查看是否成功安装模块。
下面的内容是我封装的常用的ssh操作,包括执行命令,sftp的get和put,文件操作,包括mkdir,rmdir,rename,rm,file。具体看代码吧。
class SSH { private $connection; private $host; private $port; private $username; private $password; function __construct($host,$port,$username,$password){ $this->host=$host; $this->port=$port; $this->username=$username; $this->password=$password; } private function connect(){ if(!$this->connection) { $this->connection=ssh2_connect($this->host,$this->port); if($this->connection) { if(!ssh2_auth_password($this->connection,$this->username,$this->password)) throw new Exception('Fail to login with {$this->username}:PASSWORD[YES]'); } else throw new Exception("Fail connect server {$this->host}:{$this->port}"); } } public function execute($command){ if(!$this->connection) $this->connect(); $stream=ssh2_exec($this->connection,$command); stream_set_blocking($stream,true); return trim(stream_get_contents($stream)); } public function shell($type='xterm',$env=array(),$width=80,$height=25,$width_height_type=SSH2_TERM_UNIT_CHARS){ if (!$this->connection) $this->connect(); return ssh2_shell($this->connection,$type,$env,$width,$height,$width_height_type); } public function put($remote_path,$local_path,$mode=0644){ if (!$this->connection) $this->connect(); return ssh2_scp_send($this->connection,$local_path,$remote_path,$mode); } public function get($remote_path,$local_path){ if (!$this->connection) $this->connect(); return ssh2_scp_recv($this->connection,$remote_path,$local_path); } public function file($file){ if (!$this->connection) $this->connect(); $sftp = ssh2_sftp($this->connection); if ($sftp) return ssh2_sftp_stat($sftp,$file); else return false; } public function mkdir($path,$mode=0777){ if(!$this->connection) $this->connect(); $sftp = ssh2_sftp($this->connection); if ($sftp) return ssh2_sftp_mkdir($sftp,$path,$mode,true); else return false; } public function rmdir($path){ if (!$this->connection) $this->connect(); $sftp=ssh2_sftp($this->connection); if($sftp) return ssh2_sftp_rmdir($sftp,$path); else return false; } public function mv($old,$new){ if (!$this->connection) $this->connect(); $sftp = ssh2_sftp($this->connection); if ($sftp) return ssh2_sftp_rename($sftp,$old,$new); else return false; } public function rm($file){ if (!$this->connection) $this->connect(); $sftp = ssh2_sftp($this->connection); if ($sftp) return ssh2_sftp_unlink($sftp,$file); else return false; } }
虽说通过这个ssh2执行命令是没有问题,但是对于返回内容的处理还是比较麻烦的,实际返回内容是命令执行后显示的字符串,通过函数能够得到的也是完整的内容字符串,因此上面的封装当中也是直接将字符串内容返回,至于这当中的内容要怎么处理只能在后面的操作当中另行处理了,也建议在执行一些命令时不妨通过管道将内容通过grep或者awk或者sed处理一下,这样在判断结果时候可能会略方便一些。
W3c0.com 提供的内容仅用于培训。我们不保证内容的正确性。通过使用本站内容随之而来的风险与本站无关。W3c0 简体中文版的所有内容仅供测试,对任何法律问题及风险不承担任何责任。 当使用本站时,代表您已接受了本站的使用条款和隐私条款。版权所有,保留一切权利。 鲁ICP备15022115号