ssh2_scp_send和ssh2_scp_recv仅支持整文件传输,不能读写远程文件内容;若需逐行读写、追加或随机访问,必须使用ssh2_sftp配合fopen/fread/fwrite等流操作函数。

ssh2_scp_send 和 ssh2_scp_recv 不是数据读写函数,它们只负责**文件级传输**——把本地磁盘文件发到远程服务器,或从远程服务器拉一个文件到本地磁盘。你不能用它们“读取远程文件内容”或“向远程文件追加一行”,就像不能用 cp 命令来 fgets() 一样。
如果你真正想做的是:**在PHP里操作远程服务器上的文件内容(比如读一行、写一段JSON、追加日志)**,那得走 SFTP 通道,而不是 SCP 协议本身。下面分两块说清楚。
scp_send / scp_recv 只能传整个文件,不能读写内容
这两个函数本质是封装了底层 SCP 协议的文件拷贝逻辑,行为严格对应命令行 scp local.txt user@host:/path/remote.txt:
-
ssh2_scp_send($conn, '/tmp/data.json', '/var/www/config.json', 0644):把本地/tmp/data.json整个覆盖写到远程路径,不检查远程原文件是否存在、是否可写、内容是什么 -
ssh2_scp_recv($conn, '/var/log/app.log', '/local/app.log'):把远程文件完整下载下来,存成本地新文件;它不会“读取内容后返回字符串”,也不会支持偏移读取或流式处理 - 失败时只返回
false,没有详细错误码;常见卡点是权限(远程目录不可写)、路径不存在、SSH连接未认证成功(漏掉ssh2_auth_password()或密钥加载失败) - 不支持通配符、不支持断点续传、不支持并发多文件——它就是一个单次原子拷贝
想读写远程文件内容?必须用 ssh2_sftp + fopen/fread/fwrite
真正能模拟“远程 fopen()”行为的是 SFTP 子系统。PHP 的 ssh2 扩展提供了 ssh2_sftp() 创建 SFTP 资源,之后就能用标准文件函数操作远程路径:
- 先建立 SSH 连接并认证:
$conn = ssh2_connect('192.168.1.100', 22); ssh2_auth_password($conn, 'user', 'pass'); - 再打开 SFTP 会话:
$sftp = ssh2_sftp($conn); - 构造远程文件句柄:
$stream = fopen('ssh2.sftp://' . intval($sftp) . '/var/www/data.txt', 'r+');—— 注意这个 URL 格式,intval($sftp)是必需的整型资源 ID - 然后就可以
fgets($stream)、fwrite($stream, "new line\n")、fseek($stream, 0, SEEK_END)等,和本地文件几乎一致 - ⚠️ 坑点:SFTP 模式不支持
"a"(追加),只能用"r+"+fseek(..., SEEK_END)模拟;且某些老旧 OpenSSH 版本对ftruncate()支持不全
shell_exec("scp ...") 看似简单,其实更难控错
用 shell_exec() 调系统 scp 命令,表面看代码少,但实际埋雷更多:
- 输出不可靠:
scp在传输中会打印进度条、警告(如 host key changed),这些混在shell_exec()返回值里,无法区分成功/失败 - 密码交互无法自动化:除非用
sshpass或提前配置免密登录,否则脚本会卡住等待输入密码 - 超时难控制:
scp默认无超时,网络卡顿时 PHP 进程可能长期挂起 - 权限继承混乱:执行用户(如
www-data)必须有 SSH 密钥或密码访问权限,且远程目标目录需对其可写——这比 PHP 扩展里显式传参更容易出权限混淆 - 替代建议:真要用命令行,优先选
proc_open()配合ssh+sftp子命令,至少能捕获 stderr 并设 timeout
ssh2_connect() 成功,不代表 ssh2_sftp() 就一定能初始化——有些 SSH 服务端(尤其是嵌入式设备或精简版 OpenWrt)只开了 SCP,禁用了 SFTP 子系统,此时 ssh2_sftp() 会直接返回 false,连错误信息都没有。动手前,先手动 ssh user@host sftp 测试一下是否可用。php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











