You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

phpseclib scp->get()返回额外NULL字符问题及解决求助

phpseclib SCP->get() 函数返回内容末尾多出NULL字节的问题

问题现象

phpseclib的scp->get()函数存在异常:获取文件时,会在返回内容的末尾添加一个原文件中不存在的NULL字符。

复现测试

  • 测试流程:编写脚本先通过scp->put()将本地文件上传至localhost的/tmp目录,再分别以「返回字符串」「写入本地文件」两种方式调用scp->get()获取该文件
  • 测试结果:
    • 除空文件外,所有常规文件(文本、二进制等)的返回结果都比原文件多1字节(即额外的NULL)
    • scp->put()上传至远程的文件与原文件完全一致(经diff验证)
  • 注意:直接去除末尾NULL的方案不可行,因为存在该NULL为文件合法内容的场景

测试脚本代码

#!/usr/bin/env php
<?php

set_include_path(get_include_path() . PATH_SEPARATOR . '/www/cgi-bin/phpseclib');
require_once('Crypt/RSA.php');
require_once('Net/SSH2.php');
require_once('Net/SCP.php');

function executeSSHCommand($ssh, $command) {
    $result = $ssh->exec($command);
    printf("    cmd: %s (%s)\n", $command, ($result === false ? 'ERROR' : 'OK'));
    if (is_string($result) && strlen($result) > 0) {
        $responseLines = explode("\n", $result);
        foreach ($responseLines as $line) {
            if (strlen(trim($line)) > 0)
                printf("        > %s\n", $line);
        }        
    }
}

function toHumanPerms($perms) {
    $rtnVal = '';
    $workPerms = $perms;
    foreach ([ 'o', 'g', 'u' ] as $type) {
        $thisPerms = ($workPerms & 0x07);
        $workPerms >>= 3;
        $r = ($thisPerms & 0x04) ? 'r' : '';
        $w = ($thisPerms & 0x02) ? 'w' : '';
        $x = ($thisPerms & 0x01) ? 'x' : '';
        if (strlen($rtnVal) > 0) $rtnVal .= ',';
        $rtnVal .= $type . '=' . $r . $w . $x;
    }
    return $rtnVal;
}

$home = getenv('HOME');
$system = 'localhost';
$user = getenv('LOGNAME');
$hostString = $user . '@' . $system;

$key = new Crypt_RSA();
if (! $key->loadKey(file_get_contents($home . DIRECTORY_SEPARATOR . '.ssh/id_rsa'))) {
    die("Could not load private key.\n");
}
$ssh = new Net_SSH2($system);
if ($ssh) {
    if ($ssh->login($user, $key)) {
        $scp = new Net_SCP($ssh);
        $localFile = (count($argv) > 1 ? $argv[1] : $home . DIRECTORY_SEPARATOR . '.bashrc');   // 未通过参数指定则复制.bashrc
        $remoteFile = '/tmp/' . preg_replace('%[/]%','-',$localFile); // 示例:/tmp/-home-user-.bashrc
        $copyBack = $localFile . '-copiedBack';
        $fileSize = filesize($localFile);
        $success = $scp->put($remoteFile , $localFile, NET_SCP_LOCAL_FILE);
        printf("    put: %s -> %s: %s\n", $localFile, $remoteFile, ($success === true ? 'OK' : '*ERROR*'));
        if ($success) {
            $permsString = toHumanPerms(fileperms($localFile));
            $command = 'chmod ' . $permsString . ' ' . escapeshellarg($remoteFile);
            executeSSHCommand($ssh, $command);
            $fileModTime = date('YmdHi.s', filemtime($localFile));
            $command = sprintf('touch -t %s %s', $fileModTime, escapeshellarg($remoteFile));
            executeSSHCommand($ssh, $command); // 执行命令(日志记录,不输出到终端)
            $remoteData = $scp->get($remoteFile);           // 将文件内容复制到本地变量
            $success = $scp->get($remoteFile, $copyBack);   // 将文件下载到本地文件系统
            printf("    get: %s -> %s: %s\n\n", $remoteFile, $copyBack, $success === true ? 'OK' : '*ERROR*');
            printf("Original file size was %d, returned data size is %d, returned file size is %d\n", $fileSize, strlen($remoteData), filesize($copyBack));
        }
    } else {
        printf("**** Unable to authenticate user %s at host %s. ****\n", $user, $system);
    }
} else {
    printf("**** Unable to connect to server %s. ****\n", $system);
}

临时修复方案

修改phpseclib/Net/SCP.php的get()函数,在以下代码之后:

while ($size < $info['size']) {
    $data = $this->_receive();

添加以下代码:

// 2023年5月6日添加,解决scp->get()获取非空文件时总是多返回1字节的问题
    $expected = $info['size'] - $size;
    if (strlen($data) == $expected + 1) // 如果接收的数据刚好比预期多1字节
        $data = substr($data, 0, $expected);

诉求

希望找到该问题的根源及更优解决方案,或了解问题的正确上报方式

内容的提问来源于stack exchange,提问作者Dennis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 22:23:10