ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

scp命令从入门到实战:如何正确拉取远程文件夹

2026/9/29 21:44:53 拓冰建站 浏览量
scp命令从入门到实战:如何正确拉取远程文件夹 很多人对 scp 工具的理解停留在“cp 加个远程路径”但实际上真到了生产环境你会发现 scp 命令的细节并不少尤其是那句高频搜索“如何通过 scp 把文件夹拉下来”回答其实非常简单可问题背后的坑却五花八门目录里有大文件超时、服务器禁止密码登录、文件名带空格、本地目录不存在……今天这篇就把 scp 工具从入门到实战拆开讲透看完你能直接照着操作也搞清楚每个参数背后的为什么。1. 内容整体设计与思路拆解1.1 这个工具解决什么问题scp 全称 Secure Copy Protocol运行在 SSH 协议之上本质就是一个“加密版的 cp”。传统 FTP 传输是明文密码和数据包能被抓包工具直接还原scp 从设计上就解决了这个安全问题所有数据都走 SSH 加密通道不需要额外搭建 FTP 服务只要目标机器开着 sshd 就能传。它的典型应用场景非常集中服务器之间迁移文件、从线上环境拉取日志、把本地代码部署到云主机、跨机房传递备份包。日常生活中你拿它从自己的云服务器把资料下载到本地桌面也是同一个动作。这个命令适合所有接触 Linux 的运维、后端开发、爬虫工程师、数据分析师甚至懂一点命令行的普通用户因为它无需图形界面一台能跑 SSH 的机器就能用。1.2 为什么不用其他传文件方式我见过有人用 GitHub 中转文件有人用网盘还有人直接在公网 FTP 挂目录这些方案在正经场景下问题都不小。GitHub 单文件限流 100MB大文件传不了网盘要登录、要等待审核自动化脚本根本没法集成FTP 更不用提极易被扫描爆破。和它们相比scp 的优势集中在三点一是直接复用 SSH 认证体系账号管理和权限控制不用单独维护二是命令本身是 Unix 哲学的标准工具可脚本化、可 cron 定时任务化三是不需要额外安装服务端目标机器只要保留 OpenSSH 相关组件就行。当然如果文件量极大且需要断点续传我会建议改用 rsync但 scp 在“一次性的、非增量同步的、快速分发”场景下依然是最顺手的选择。2. 核心细节解析与实操要点2.1 基础命令形态解析scp 的语法可以理解为 cp 命令的扩展核心形态是“把源路径复制到目标路径”只不过路径前面可以加 “用户名主机地址:” 前缀从而指向远程机器。我先列出最常用的三种等价形态# 本地文件复制到远程 scp local_file.txt userremote_host:/home/user/ # 远程文件下载到本地 scp userremote_host:/home/user/remote_file.txt ./ # 本地文件复制到远程并改名 scp local_file.txt userremote_host:/home/user/new_name.txt注意第一个参数是源第二个参数是目标永远遵循这个顺序。路径解析规则是如果路径中有冒号“:”冒号前是主机信息冒号后是远程路径没有冒号则按本地路径处理。实际操作中最容易踩的坑是目标目录不存在。scp 不会自动帮你创建目录如果远端的/home/user/backup/这个目录不存在命令直接报错。解决办法是先用 SSH 登录上去mkdir -p或者用下面的命令把文件夹整体拉下来时保证父目录存在即可。还有一种情况是远程主机的 SSH 端口不是默认 22这时要用-P参数大写 P这一点和 ssh 命令的-p小写不同我每次写都要在心里默念一次。2.2 目录递归复制核心高频场景热搜词里“如何通过 scp 把文件夹拉下来”问的就是目录整传。如果直接写scp userhost:/data/logs ./系统会告诉你logs不是一个普通文件无法复制。正确做法是加-r递归参数scp -r userremote_host:/data/logs /local/destination/这个命令会把远程的logs目录整体复制到本地/local/destination/下生成/local/destination/logs。如果希望把目录内容直接铺到目标目录里而不是再嵌套一层可以这样写scp -r userremote_host:/data/logs/ /local/destination/注意源路径最后多了一个斜杠/这样 scp 会复制 logs 里面的内容而不是 logs 目录本身。这个细节用中文搜索引擎都很难搜到准确答案很多人试了无数次发现多一层目录其实就是在斜杠上出了问题。同理把本地文件夹推到远程scp -r ./my_app userremote_host:/home/user/deploy/执行后远端会生成/home/user/deploy/my_app。生产环境部署时我通常会把my_app里除了.git和node_modules之外的内容先打成一个 tar 包再传上去解压而不是直接用scp -r无数个小文件否则速度会慢到让人怀疑人生。这个优化后面专门讲。2.3 常用参数表与作用我把平时高频使用的参数整理成一张表方便你对照查阅参数含义典型案例-r递归复制整个目录scp -r /dir userhost:/path-P指定远程 SSH 端口scp -P 2222 file userhost:/path-C传输时压缩数据scp -C -r data userhost:/path-l限制带宽单位 Kbit/sscp -l 10240 big_file userhost:/path-i指定私钥文件scp -i ~/.ssh/id_ed25519 file userhost:/path-p保留原文件的修改时间、访问时间、权限scp -p file userhost:/path-q静默模式不显示进度条脚本内使用-v调试模式输出详细信息排查连接问题时使用-c指定加密算法scp -c aes256-gcmopenssh.com file userhost:/path-o透传 ssh 参数scp -o ConnectTimeout10 file userhost:/path这里有几个参数特别容易被忽略。-l限速参数单位是 Kbit/s不是 KB/s很多人写成-l 1024以为限制 1MB/s实际只有 128KB/s相差很大。如果希望限速 10MB/s正确写法是-l 8192010 × 1024 × 8。-C压缩参数适合纯文本日志、JSON、CSV 文件能减少不少带宽但如果传的是图片、压缩包、视频再加-C只会白白消耗 CPU速度反而更慢。-p保留权限这个参数在迁移服务器时非常有用比如你要把网站附件从旧机拷贝到新机不带-p的话文件权限会变成你当前登录用户的默认 umask 值很可能导致新环境静态文件读不了产生一堆 403 错误。3. 实操过程与核心环节实现3.1 从零到一本地与远程互传全流程我以一台阿里云 ECS 和一台本地笔记本为例完整走一遍。假设远端 IP 是203.0.113.10举例说明非真实地址用户名deploySSH 端口是默认 22本地当前目录是/home/me/project。第一步先确认网络连通与否同时确认 SSH 端口可达ping -c 3 203.0.113.10 nc -zv 203.0.113.10 22nc -zv这个命令能快速测试端口通不通比直接跑 scp 获得报错效率高得多。端口通了再执行下面的目录推送scp -r /home/me/project/build deploy203.0.113.10:/home/deploy/nginx/html/这一步要求你手动输入 deploy 用户密码。每次手输密码既不安全也容易打断自动化流程所以第二步我建议立刻配置免密登录配置方式放到下一小节。先验证推送之后登录远端检查文件是否完整ssh deploy203.0.113.10 ls -l /home/deploy/nginx/html/build | head -20重点查看目录层级是否符合预期有没有缺文件权限是否正常。确认无误后再反过来学习“拉下来”的操作。比如你需要把远端/home/deploy/nginx/logs下面的日志全部拉到本地分析scp -r deploy203.0.113.10:/home/deploy/nginx/logs /home/me/project/logs_backup这样本地会生成/home/me/project/logs_backup/logs目录。如果你想直接拉成/home/me/project/logs_backup不嵌套就改成scp -r deploy203.0.113.10:/home/deploy/nginx/logs/ /home/me/project/logs_backup注意这里源路径末尾的斜杠。而且目标路径/home/me/project/logs_backup首先要存在否则 scp 会把所有内容合并成一个名为 logs_backup 的文件最终报错“Is a directory”。3.2 免密登录配置与密钥选择频繁输密码解决不了就是配密钥对。我推荐用 Ed25519 算法它比 RSA 更安全、密钥更短、生成更快。如果远端 SSH 版本较老不支持 Ed25519再用 RSA 也不迟。本地执行ssh-keygen -t ed25519 -C my-laptop-scp -f ~/.ssh/id_ed25519一路回车不设口令即可生成私钥~/.ssh/id_ed25519和公钥~/.ssh/id_ed25519.pub。然后把公钥追加到远端~/.ssh/authorized_keys中ssh-copy-id -i ~/.ssh/id_ed25519.pub deploy203.0.113.10如果远端端口特殊用-p注意这里是 ssh 命令的-p小写别和 scp 混淆ssh-copy-id -p 2222 -i ~/.ssh/id_ed25519.pub deploy203.0.113.10配置好之后scp 无需密码即可运行。安全建议私钥文件权限必须设为600公钥和.ssh目录权限分别要求644和700如果权限太宽SSH 会直接忽略该密钥报错“UNPROTECTED PRIVATE KEY FILE”。这是很多人配置完免密却不生效的一大原因。生产环境中还有更谨慎的做法禁用密码登录、仅允许密钥登录。这会大幅降低服务器被爆破的概率。在你完成密钥部署后可以编辑远端/etc/ssh/sshd_config设置PasswordAuthentication no然后重启 sshd。只要你的私钥没泄露远程文件传输的安全性就有了基础保障。3.3 性能调优大文件高速传输方案scp 默认的加密算法、窗口大小可能不是为该场景最优的传输大文件时你会感到明显瓶颈。我实测过几个优化点第一是选用更快的加密算法。OpenSSH 7.6 以上支持aes256-gcmopenssh.com这个算法在多数 CPU 上有 AES-NI 硬件加速速度能提升 20% 以上scp -c aes256-gcmopenssh.com big_file.raw userhost:/data/第二是加-C压缩配合处理高重复文本数据。比如百万行 JSON 日志压缩后传输量能降到原来的 1/10代价是消耗两端 CPU。针对小带宽、慢链路的场景-C的效率提升非常显著。第三是保持默认参数时限制 SSH 多路复用也能提速。这个玩法稍微高级一点你先建立一条 SSH 控制连接后面的 scp 都走这条已有通道免去反复握手的时间ssh -M -S /tmp/ssh_socket -o ControlPersist600 userhost后续 scp 使用时加上-o ControlPath/tmp/ssh_socketscp -o ControlPath/tmp/ssh_socket big.iso userhost:/data/第一次连接建立控制通道之后 600 秒内再传文件不需要重新 TCP 握手、密钥交换速度会有明显提升尤其连续传几十个小文件时节省的握手时间加起来很可观。第四是绕开 scp 改用 rsync 配合 SSH 做增量同步。严格来说这不算 scp 参数优化但当文件已经存在一半或者需要断点续传我会果断用 rsyncrsync -avz --partial -e ssh userhost:/data/big.zip /local/rsync 的--partial支持中断后续传这是 scp 做不到的。但用户问的是 scp那就先别把 rsync 扯太远知道这个差异就足够了。3.4 文件夹整体拉取的完整示例与验证为了让你直接抄作业我把“通过 scp 把文件夹拉下来”这个高频需求整理成一段完整可复用的命令组合。假设远程服务器上有/srv/app/uploads这个目录你需要把它全部拉到本地的~/backup_uploads/。# 1. 创建本地目标目录 mkdir -p ~/backup_uploads # 2. 确认远端目录结构可选但推荐 ssh userremote_host du -sh /srv/app/uploads ls -l /srv/app/uploads | head -5 # 3. 执行拉取 scp -r -C -P 22 userremote_host:/srv/app/uploads/ ~/backup_uploads/ # 4. 校验数量与大小 find ~/backup_uploads/ -type f | wc -l du -sh ~/backup_uploads/这段命令把远端uploads目录里的所有内容直接平铺到本地~/backup_uploads/下所以我给源路径加了尾随斜杠。执行完成后务必对比两端文件数量特别是文件很多时因为个别传输中断会导致缺失scp -r并不会中途报错只有最终校验才能发现。我自己一般还会用 rsync 做一次校验同步比如rsync -avz --delete -e ssh userhost:/srv/app/uploads/ ~/backup_uploads/利用它的校验和机制确保两边绝对一致这比手动数文件靠谱得多。4. 常见问题与排查技巧实录4.1 连接超时与报错信息对照我整理了这几年遇到的典型报错方便你查询定位报错信息原因解决方案WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED远端主机密钥变更局域网内 IP 被其他机器占用执行ssh-keygen -R 203.0.113.10清除旧的主机密钥后重试Permission denied (publickey,password)密码错误 / 密钥未授权 / 使用了错误的身份文件检查用户名与密码检查密钥是否已加入authorized_keys检查本地是否使用-i指定了正确私钥No such file or directory目标目录不存在或者源路径拼错先ssh登录确认路径存在必要时mkdir -pIs a directory复制单个文件时源或目标写了目录未加-r加-rConnection timed out防火墙屏蔽了端口 / 网络不通检查安全组、iptables、网络连通性Bad owner or permissions on /home/user/.ssh远端.ssh目录权限不对远端执行chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keysBroken pipe传输大文件时连接中断可能是 SSH 超时或网络抖动加-o ServerAliveInterval60保持心跳或改用 rsync 续传Broken pipe是最折磨人的。默认 SSH 会话如果长时间没有数据流动一些网络设备会静默断开。如果你在传输一个超大文件可能 10 分钟后连接就被中间设备切了。解决方案是在 scp 命令中附带 SSH 心跳参数scp -o ServerAliveInterval60 -o ServerAliveCountMax3 big_file.iso userremote_host:/data/ServerAliveInterval60意思是每 60 秒客户端给服务器发一个心跳包ServerAliveCountMax3表示连续三次心跳无响应才判定连接已死。这样网络设备长期看到活跃的数据包不会误判闲置断连。4.2 文件名空格与特殊字符处理方法路径里带空格、中文、$、单引号这类特殊字符是新手最容易绕晕的。在 scp 中远端路径的解析由远端 shell 完成所以空格需要被转义或加引号。正确做法是给整个远端路径加上单引号scp userhost:/srv/app/my files/data.txt ./ scp userhost:/srv/app/my files/data.txt /tmp/单引号是传给远端 shell 的双引号是给本地 shell 用的。如果本地路径带空格则本地 shell 需要用转义或双引号包裹scp /home/me/My\ Documents/report.pdf userhost:/tmp/ scp /home/me/My Documents/report.pdf userhost:/tmp/中文路径的问题本质和空格一致统一用引号包裹即可。文件路径中如果包含$在远端 shell 中会被当作变量符号此时必须用单引号包裹防止变量展开。通过find -exec或while read批量拉取文件时建议将文件路径写入列表然后用循环处理避免命令行转义地狱while read file; do scp userhost:$file /local/dest/ done (ssh userhost find /srv/data -name *.log)但这里要留意$file已经在外层被本地 shell 展开了所以实际传到远端的语句需要确保变量值中的特殊字符还在相对复杂我只推荐在文件数量少、路径可控时用这种循环否则直接tar打包再传更省心。4.3 目录权限与属主问题scp 权限问题的本质是传输后文件默认以当前登录用户的身份创建权限规则受远端文件系统的 umask 影响。在迁移服务器或部署网站时经常会遇到如下情况你以root身份把文件拉到本地本地所有者和组都变成了root但你的 Web 服务运行在www-data用户下访问直接 403。解决办法有两个方向。一个是拉取后批量调整权限scp -r userhost:/srv/app/uploads ./uploads_backup chown -R www-data:www-data ./uploads_backup chmod -R 755 ./uploads_backup另一个是使用rsync -p保留权限属性scp 只有在加-p时才保留而且前提是你的远端账号对原文件有读取权限。如果远端原始目录是root:root且权限 600那么仅用普通用户 scp 拉取根本读不到数据这时候要么让远端把文件临时复制到公开目录再拉要么配置免密用 root 账号直接拉但 root 登录本身风险更高务必仅在可靠网络中使用。4.4 断线重传与大文件保护思路scp 自己没有断点续传能力中断后只能从头再来。对于几个 GB 的备份包这个风险不可接受。我的处理逻辑是把 scp 和 tar 结合tar czf - /srv/data | ssh userremote_host cat /backup/data_$(date %Y%m%d).tar.gz这条管道把本地 tar 压缩的结果直接以二进制流推给远端通过 SSH 写盘。好处是远端不需要预先留一块等大的暂存空间流式传输会持续写入而且一旦断了因为源文件还在本地重新执行一次即可不需要删除远端不完整的残留文件。但这个方案也不支持断点续传所以我通常还会再用rsync --partial做最终交付。如果追求简单且必须使用 scp那就先压缩再传输tar czf data.tar.gz /srv/data scp data.tar.gz userhost:/backup/同时配合nohup放到后台执行避免自己睡一觉起来终端断开导致传输终止nohup scp -o ServerAliveInterval60 data.tar.gz userhost:/backup/ scp.log 21 这样 ssh 退出也不会杀掉 scp 进程。日志里可以看到进度完成后再用du对比两端文件大小即可。5. 实用经验与避坑指南5.1 文件数量极多时的传输策略scp -r 处理成千上万的小文件时效率很低因为每个文件都要经过一次 SSH 通道的加密和网络往返大量时间浪费在握手和单文件传输就绪上而实际网络带宽根本跑不满。我去年代维某个 Git 仓库迁移目录里有 20 多万个小文件直接用scp -r跑了近半个小时还没同步完改用 tar 管道方式后几分钟就传完。cd /srv/repo tar cf - . | ssh usernew_host mkdir -p /srv/repo tar xf - -C /srv/repo如果要带上压缩把cf改成czf远端解包时用xzf。这个方法也顺便解决了文件数量多、单个文件小的问题因为 tar 把所有文件打包成一个流网络层不再被小文件的头部信息拖垮。5.2 传输前目标空间检查目标磁盘空间不足是最尴尬的失败方式传输进行到 90% 时磁盘写满scp 报错退出但已经写入的部分不完整文件还挂在磁盘上。为了避免这个坑我习惯在传输前直接查询远端磁盘剩余空间ssh userhost df -h /backup本地同样执行df -h检查。如果空间不够先清理再传不要抱有侥幸心理。这个习惯在拉取文件夹时特别重要因为文件夹大小不容易一眼看出来。可以先看远端目录总大小ssh userhost du -sh /srv/app/uploads再对比本地剩余空间做到心里有数。5.3 多主机分发技巧如果你要把同一个文件分发到多台服务器一个个执行 scp 太浪费人力。我会写一个非常简单的循环for host in web01 web02 web03; do scp app.tar.gz root${host}:/data/app/ \ ssh root${host} cd /data/app tar xzf app.tar.gz rm -f app.tar.gz done这样既传了文件又顺手解压清理减少中间状态残留。如果主机数量在几十台以上建议配合 Ansible 的copy模块但 scp 作为底层机制依然起着关键作用。5.4 从 Windows 上传送文件到 LinuxWindows 上使用 scp 也是可行的。Windows 10/11 自带 OpenSSH 客户端直接打开 PowerShell用法完全一致scp -r C:\Users\me\project deployremote:/home/deploy/ scp deployremote:/home/deploy/logs C:\Users\me\logs唯一要注意的是 Windows 路径分隔符是反斜杠如果路径带空格PowerShell 中需要使用引号包裹scp C:\Users\me\My Project\app.jar deployremote:/home/deploy/另外Windows 上的 scp 默认读取的密钥位置是C:\Users\用户名\.ssh\id_rsa或id_ed25519如果有多个密钥可以用-i指定。5.5 脚本中安全使用密码前面反复强调配置密钥但还是有人非要在脚本里明文写密码。切记不要把密码写进命令行历史也不要在脚本中硬编码。如果实在无法使用密钥建议用sshpass工具把密码从文件中读取但文件权限必须设置为 600。另外开启 bash 历史记录时shell 会把命令行中的明文密码记录到~/.bash_history这是高危行为。最稳妥的方式始终是密钥登录一次配置长期免密。甚至可以给密钥单独设置 passphrase配合ssh-agent缓存既能保证私钥文件即使泄露也无法直接使用又不会频繁输密码。我把免密登录的完整步骤放在这里方便你一次性搞定# 本地生成密钥不设密码 ssh-keygen -t ed25519 -N -f ~/.ssh/id_ed25519 # 拷贝公钥到远端 ssh-copy-id -i ~/.ssh/id_ed25519.pub userremote_host # 测试免密 ssh userremote_host whoami测试通过后scp 命令自然就不需要密码了。这一步做完后面一切自动化脚本才有基础。6. 写在最后的一点实战体会我最早用 scp 是从一个简单需求开始的把自己电脑上的文件丢到服务器上部署。后来经过几百次生产环境迁移之后最深的体会就是“scp 容易迁移难”。真正的问题从来不是命令本身而是命令背后的路径细节、权限模型、网络稳定性、操作习惯。现在每次执行 scp 前我都会默默过一遍四个问题源目录末尾有没有多了斜杠目标父目录存在不存在远端磁盘空间够不够有没有提前配置密钥这四关过去基本不会出大问题。下次你再碰到“如何通过 scp 把文件夹拉下来”自己心里就能立刻展开一张操作清单先确认端口再加上-r注意尾随斜杠传输后校验文件数必要时候用 tar 管道提升效率。大多数人被这命令卡住不是因为它难而是因为还没有在一次失败中系统整理过常见报错。希望这篇笔记能让你少走些弯路把时间花在真正需要处理业务的地方。