前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版

Linux之scp传输文件,从基础用法到rsync取舍

首页2018-12-03 18:32:41Back-End
LinuxSCPrsync文件传输

构建产物打好了,要往测试机上放一份。你敲下 scp -p 36000 -r dist user@1.2.3.4:/home/data/,回车之后终端不动了,几十秒后报一个 ssh: connect to host 1.2.3.4 port 22: Connection refused。端口明明写了啊。

这个错我踩过不止一次,问题出在 p 的大小写上。scp 指定端口用的是大写 -P,小写 -p 是保留文件的修改时间和权限位,跟端口一点关系没有。而 ssh 命令恰恰相反,它用的是小写 -p。两个天天一起用的命令,在同一个参数上约定相反。

这篇把 scp 的用法从头理一遍,顺带把免密配置、常见报错的排查路径,以及什么时候该换成 rsync 说清楚。

在本篇文章中,我们将从浅入深,和大家一起学习以下知识:

  • scp 底层到底走的什么通道,为什么它天然是加密的
  • 上传和下载两个方向的写法,路径顺序怎么记
  • -P 和 -p 的大小写坑,以及 -r 递归的边界
  • 通配符在 scp 里由谁展开,为什么 .next 这类隐藏目录传不上去
  • 免密配置怎么做,~/.ssh/config 能帮你省掉多少参数
  • 五类常见报错的排查顺序
  • 传大目录、断点续传、增量同步这些场景为什么该换 rsync
  • sftp 的取舍,以及新版 OpenSSH 对 scp 的态度变化

# 一、scp 其实就是跑在 ssh 上的 cp

scp 的全称是 secure copy,名字里的 secure 不是它自己实现的。它做的事情很简单,建立一条 ssh 连接,然后在这条连接上把文件内容送过去。

所以你能 ssh 上去的机器,基本就能 scp;ssh 连不上的机器,scp 也一定连不上。这个关系很重要,排查问题的时候先用 ssh 试一次,能把一半的可能性直接排除掉。

同样的道理,ssh 那套认证机制 scp 全部复用。你配了密钥登录,scp 就不用输密码;服务器改了 SSH 端口,scp 也得跟着改。传输过程是加密的,这一点跟 FTP 那种明文协议完全不同,往公网传东西的时候这是个硬性优势。

理解了这层关系,scp 的参数就不用死记了,它无非是「ssh 的连接参数」加上「cp 的复制参数」拼起来的。

# 二、两个方向的基本写法

scp 的参数顺序和 cp 一样,永远是「源在前,目标在后」。区别只是源和目标里可以有一个带上 用户@主机: 前缀。

# 2.1 从服务器下载文件

在本地电脑上执行,注意最后要写清楚本地的落地路径。

scp username@servername:/path/filename /tmp/local_destination
@前端进阶之旅: 代码已经复制到剪贴板

比如把 192.168.0.101 上的 /home/poetry/test.txt 拉到本地的 ~/Downloads/。

scp root@192.168.0.101:/home/poetry/test.txt ~/Downloads/
@前端进阶之旅: 代码已经复制到剪贴板

原文这条命令只写了远端路径,把本地目标路径漏了。漏掉的话 scp 会报 usage 错误,不会默认下到当前目录,这点和 wget 不一样。想下到当前目录得显式写个 .。

# 2.2 递归拷贝目录

传目录必须加 -r,不加的话 scp 会告诉你 not a regular file 然后什么都不做。

scp -P 36000 -r user_00@192.168.1.201:/usr/local/services/nginx/conf ~/Downloads/
@前端进阶之旅: 代码已经复制到剪贴板

这里的 -P 36000 是远端 SSH 服务监听的端口,不是 22 的时候必须显式指定。

# 2.3 传目录到服务器

方向反过来,把带 用户@主机: 的那一侧挪到后面就行。

scp -r /tmp/local_dir username@servername:remote_dir
@前端进阶之旅: 代码已经复制到剪贴板

远端路径不以 / 开头的话,是相对于该用户家目录的相对路径。生产脚本里我一律写绝对路径,相对路径在换了个部署账号之后会悄悄传到别的地方去,这种错很难发现。

# 三、大小写和参数顺序上的几个坑

# 3.1 -P 和 -p

前面提过一次,这里单独拎出来说,因为它是最高频的翻车点。

scp 的 -P 大写才是端口,小写 -p 是 preserve,保留文件的 mtime、atime 和权限位。ssh 的端口参数则是小写 -p。真要说原因,scp 的小写 -p 是从 cp -p 继承过来的,端口只好让给大写。

记不住的话有个笨办法,把端口写进 ~/.ssh/config 里,之后两个命令都不用带端口参数了。第五节会讲怎么配。

# 3.2 通配符是本地 shell 展开的

原文里有这么一条命令,注释说 deployBuildFiles 下的 .next 默认不上传到服务器。

scp -P36000 -r deployBuildFiles/** user_00@192.168.1.201:/home/data/services/goods-prev.yesdat.com/
@前端进阶之旅: 代码已经复制到剪贴板

这个现象值得展开讲讲,因为它不是 scp 的行为,是 shell 的行为。

deployBuildFiles/** 这串东西在 scp 拿到之前,就已经被本地 shell 替换成一堆具体的路径了。而 bash 默认不启用 globstar,** 和 * 的效果完全一样,只匹配一层;更关键的是,glob 默认不匹配以 . 开头的文件名。所以 .next、.env 这类隐藏文件根本没进入参数列表,scp 压根不知道它们的存在。

所以「.next 默认不上传」这句话,准确的说法是「.next 没被通配符匹配到」。

想连隐藏文件一起传,最省事的做法是直接传目录本身而不是它下面的内容。

scp -P 36000 -r deployBuildFiles user_00@192.168.1.201:/home/data/services/goods-prev.yesdat.com/
@前端进阶之旅: 代码已经复制到剪贴板

这样远端会多出一层 deployBuildFiles 目录,可以接受的话这是最不容易出错的写法。要是必须打平,bash 里可以 shopt -s dotglob 打开隐藏文件匹配,但这个开关会影响整个脚本的后续行为,我一般不推荐在部署脚本里随手开。

顺着这块聊,通配符如果写在远端那一侧,展开的就不是本地 shell 而是远端 shell 了,路径里带空格和特殊字符的时候还得考虑两层引号转义。这也是 scp 在复杂场景下容易失控的地方之一。

# 3.3 -C 压缩和 -l 限速

传文本类的文件(日志、SQL 备份、前端产物里的 JS)加个 -C 开压缩,跨机房传的时候能省不少时间。传已经压缩过的东西(图片、视频、tar.gz)加 -C 是白费 CPU,甚至更慢。

-l 可以限带宽,单位是 Kbit/s。白天在办公网传几个 G 的备份,不限速的话整个办公室的网都会被你吃掉。

scp -C -l 8000 -P 36000 backup.sql user_00@192.168.1.201:/data/backup/
@前端进阶之旅: 代码已经复制到剪贴板

# 四、五类常见报错怎么排

scp 的报错信息普遍比较简短,但对应的原因其实很好区分。

Permission denied (publickey,password),这是认证没过。先单独 ssh 一次确认账号密码或者密钥对不对,如果 ssh 能上去 scp 却不行,看看是不是密钥有 passphrase 而脚本环境里没有 ssh-agent。

Permission denied 但发生在传输开始之后,那是远端目录的写权限问题,跟登录无关。ssh 上去 ls -ld 看一眼目标目录属主。

No such file or directory,scp 不会帮你创建中间目录,目标路径的父目录必须已经存在。这跟 mkdir -p 的习惯不一样,很多人在这卡住。

Host key verification failed,服务器重装或者 IP 复用之后,本机 ~/.ssh/known_hosts 里存的指纹对不上了。正确做法是确认这台机器确实是你自己的,然后 ssh-keygen -R 主机名 把旧记录删掉,不要图省事全局关掉 host key 检查。

卡住不动没有任何输出,多半是网络层被防火墙 DROP 了(不是 REJECT,所以不返回 Connection refused,只能等超时)。加 -v 能看到卡在哪一步,是 TCP 建连还是 SSH 握手,一眼就分得清。

排查顺序我一般是这样:先 ssh 一次看连通性和认证,再 ls -ld 看目标目录权限,最后才去看 scp 的参数写没写对。

# 五、免密配置,以及 ssh config 能省掉多少事

每次传文件都输一遍密码,写进脚本更是没法自动化。密钥登录配一次就一劳永逸。

本地生成密钥对,已经有 ~/.ssh/id_rsa.pub 的话跳过这步。

ssh-keygen -t ed25519 -C "deploy@laptop"
@前端进阶之旅: 代码已经复制到剪贴板

然后把公钥推到服务器上。

ssh-copy-id -p 36000 user_00@192.168.1.201
@前端进阶之旅: 代码已经复制到剪贴板

ssh-copy-id 做的事情就是把你的公钥追加到远端的 ~/.ssh/authorized_keys 里,顺便把权限位设对。手工做也行,但权限位这块很容易错,~/.ssh 必须是 700,authorized_keys 必须是 600,权限太宽 sshd 会直接拒绝这个公钥,而且日志里不一定给你写清楚原因。这个我排查过一次,最后是看 /var/log/secure 才发现的。

配好之后再进一步,把连接参数写进 ~/.ssh/config。

Host prev
    HostName 192.168.1.201
    User user_00
    Port 36000
    IdentityFile ~/.ssh/id_ed25519
@前端进阶之旅: 代码已经复制到剪贴板

之后所有命令都可以只写 prev 这个别名。

scp -r dist prev:/home/data/services/goods-prev.yesdat.com/
ssh prev
rsync -avz dist/ prev:/home/data/services/goods-prev.yesdat.com/
@前端进阶之旅: 代码已经复制到剪贴板

端口、用户名、密钥路径全都不用带了,前面说的 -P 大小写问题也就自然消失了。我自己的感受是,这个配置文件是提升 SSH 相关操作体验最划算的一件事,五分钟配好,后面几年都受益。

# 六、什么时候该换成 rsync

scp 的模型是「把源文件从头到尾读一遍,全量写到目标」。这个模型简单可靠,但有两个硬伤。

一是没有增量。前端项目 dist 目录几百个文件,你只改了一个 JS,scp 还是把几百个文件重传一遍。

二是没有断点续传。传到 90% 网断了,重来一次还是从 0 开始。传几百兆的时候这很折磨。

rsync 就是来解决这两件事的。

rsync -avz --partial --progress dist/ prev:/home/data/services/goods-prev.yesdat.com/
@前端进阶之旅: 代码已经复制到剪贴板

拆开看这几个参数。-a 归档模式,等价于保留权限、时间戳、软链接、递归这一整套;-v 打印传了哪些文件;-z 传输时压缩;--partial 保留传了一半的临时文件,下次接着传;--progress 显示进度。

rsync 的增量判断默认看文件大小和修改时间,不一致才传。真正厉害的是跨机器传输时它会用滚动校验算法,只把文件里变化的那些块发过去,大文件小改动的场景能省掉绝大部分流量。

fe
  • 一、scp 其实就是跑在 ssh 上的 cp
  • 二、两个方向的基本写法
    • 2.1 从服务器下载文件
    • 2.2 递归拷贝目录
    • 2.3 传目录到服务器
  • 三、大小写和参数顺序上的几个坑
    • 3.1 -P 和 -p
    • 3.2 通配符是本地 shell 展开的
    • 3.3 -C 压缩和 -l 限速
  • 四、五类常见报错怎么排
  • 五、免密配置,以及 ssh config 能省掉多少事
  • 六、什么时候该换成 rsync
  • 七、sftp 的位置,以及 scp 这些年的变化
  • 总结
  • 参考

← 常用命令之wget使用记录,断点续传与整站镜像babel升级7.xx总结 →