1. 为什么 SFTP 值得单独拿出来讲
很多人第一次接触 SFTP,是在部署网站或者传文件的时候。图形化工具拖拽几下就完事,看起来没什么门槛。但真正到了服务器只开放命令行、或者需要写自动化脚本批量同步文件的场景,图形界面那套就不够用了。这时候能不能熟练敲 SFTP 命令,直接决定了你是花十分钟搞定,还是折腾一下午。
SFTP 全称是 SSH File Transfer Protocol,注意它和 FTP 完全是两码事。FTP 是独立的一套协议,明文传输,端口 21;而 SFTP 是跑在 SSH 通道里的子系统,默认走 22 端口,所有数据包括账号密码、传输内容全程加密。你可以把它理解成:SSH 给你开了一条加密隧道,SFTP 就是在这条隧道里专门负责搬文件的那位搬运工。也正因为如此,只要你能 SSH 登录一台机器,基本就能用 SFTP,不需要额外开端口、装服务。
这篇文章我打算把 SFTP 从登录到日常命令行的用法完整捋一遍。适合三类人看:一是刚接触 Linux 服务器、还在用图形工具传文件的新手;二是需要写部署脚本、做自动化同步的运维或后端;三是平时用 SFTP 但只会put和get、想系统补一下命令的人。我会把每个命令背后的逻辑讲清楚,配上实际能抄的示例,再把我自己踩过的坑和排查思路一并放出来。看完你至少能做到:不查文档就能完成一次完整的远程文件操作,遇到连不上、传不动、权限报错也能自己定位。
2. SFTP 登录方式全拆解
2.1 最基础的密码登录与端口指定
最朴素的登录方式就是一条命令:
sftp username@host回车之后会提示你输入密码,验证通过就进入sftp>交互提示符。这里有个细节很多人不知道:SFTP 的密码输入是没有任何回显的,你敲键盘屏幕上不会出现星号或者圆点,这是正常现象,别以为键盘坏了。
如果目标服务器的 SSH 端口不是默认的 22,比如改成了 2222,那就要用-P参数指定端口。注意这里是大写 P,小写 p 在 SFTP 里是preserve(保留文件属性)的意思,两者完全不同,这是新手最容易搞混的地方之一。
sftp -P 2222 username@host我个人的习惯是,只要端口不是 22,就一定显式写-P,哪怕记错了默认值也不会连错地方。另外主机地址既可以是 IP,也可以是域名,SFTP 本身不关心你用哪种,解析交给系统。
2.2 密钥登录:免密码的正确姿势
生产环境里几乎不会用密码登录,一是弱密码风险高,二是自动化脚本没法交互式输密码。这时候就要用密钥对。前提是你本地已经生成了密钥,并且公钥已经放到了服务器的授权文件里。
sftp -i ~/.ssh/id_ed25519 username@host-i后面跟私钥文件路径。这里有个常见误区:很多人以为指定了-i就不用管权限了,实际上私钥文件的权限必须是 600,也就是只有属主可读写。如果权限是 644 或者更宽松,SSH 会直接拒绝使用这个密钥,报错大概是 "Permissions are too open"。修复很简单:
chmod 600 ~/.ssh/id_ed25519我踩过一次坑:把私钥从一台机器拷贝到另一台,scp之后权限变成了 644,怎么都登不上,排查了半天才发现是权限问题。所以拿到新私钥的第一件事,先chmod 600。
2.3 用配置文件简化重复登录
如果你经常连同一批服务器,每次都敲一长串username@host -P 端口 -i 密钥太累了。可以在~/.ssh/config里预先定义好别名:
Host myserver HostName 192.0.2.10 User deploy Port 2222 IdentityFile ~/.ssh/id_ed25519配置好之后,登录只需要:
sftp myserverSFTP 会自动读取 SSH 的配置文件,把主机名、用户、端口、密钥全部套用上。这个技巧在管理多台机器时特别省事,而且scp、ssh命令也共用同一份配置,一处定义处处生效。我建议只要你有超过两台常用服务器,就立刻把 config 建起来。
2.4 登录时的几个实用参数
除了上面几个,还有几个参数值得记住。-v是 verbose 模式,会打印详细的连接过程,排查连不上的问题时非常有用,能看到卡在认证的哪一步。-b可以指定一个批处理文件,让 SFTP 非交互式执行一串命令,这个在脚本里会用到,后面实操部分会展开。-r是递归传输,传整个目录的时候必须加,否则只会传目录本身不传里面的内容。
提示:登录失败时先别急着改配置,加上
-v看输出,90% 的问题(端口错、密钥权限、用户名错)都能从日志里直接看出来。
3. 命令行核心操作逐条讲透
3.1 目录导航:pwd、ls、cd 的本地与远程之分
SFTP 交互模式里最让人困惑的一点是:命令分本地和远程两套。ls列的是远程目录,lls列的才是本地目录;cd切的是远程目录,lcd切的是本地目录。这个 "l" 前缀就是 local 的意思,记住这个规律,所有本地命令都好记了。
pwd # 查看当前远程目录 lpwd # 查看当前本地目录 ls # 列出远程目录内容 lls # 列出本地目录内容 cd /var/www # 切换到远程 /var/www lcd ~/downloads # 切换到本地下载目录刚进 SFTP 的时候,远程目录默认是你的家目录,本地目录默认是你执行 sftp 命令时所在的目录。我见过不少人传文件传错地方,就是因为没注意当前本地目录在哪,结果文件传到了意料之外的位置。养成习惯:进 SFTP 先pwd和lpwd各敲一次,确认两边位置。
3.2 文件传输:put 与 get 的完整用法
上传用put,下载用get,这是最核心的两个命令。
put localfile.txt # 上传单个文件到远程当前目录 put localfile.txt /remote/path/newname.txt # 上传并重命名 get remotefile.txt # 下载到本地当前目录 get /remote/path/file.txt ~/local/ # 下载到指定本地目录传目录要加-r:
put -r myfolder # 递归上传整个目录 get -r remotefolder # 递归下载整个目录这里有个非常关键的坑:put -r上传目录时,如果远程已经存在同名目录,行为在不同版本上不一致,有的会合并,有的会报错。稳妥的做法是先确认远程目录状态,或者干脆传之前先删掉旧的。另外递归传输大量小文件时速度会很慢,因为每个文件都要单独走一次协议交互,这种情况建议先在本地打包成 tar,传完再在远程解压,效率能提升好几倍。
还有一个-p参数容易被忽略,它能保留文件的修改时间、访问时间和权限模式:
put -p localfile.txt做增量同步或者需要保持时间戳一致的场景,这个参数很重要。默认情况下上传的文件时间会变成上传那一刻,如果你靠时间戳判断文件新旧,不加-p就会乱套。
3.3 文件管理:mkdir、rm、mv、rename 的边界
远程端的文件管理命令和 Linux shell 很像,但有几个细节要注意。
mkdir newdir # 创建远程目录 rm file.txt # 删除远程文件 rmdir emptydir # 删除空目录 mv old.txt new.txt # 重命名(部分版本支持) rename old.txt new.txt # 重命名(更通用的写法)rm在 SFTP 里默认只能删文件,删目录要用rmdir,而且只能删空目录。想删非空目录,得先递归删里面的内容,或者退出 SFTP 用ssh执行rm -rf。这是 SFTP 的一个设计限制,它故意不提供递归删除,防止误操作把整个目录干掉。我个人觉得这个限制是好事,至少逼你多想一秒。
mv和rename在不同实现里支持程度不一样,OpenSSH 自带的 sftp 两个都支持,但有些精简版客户端只认rename。写脚本的时候用rename兼容性更好。
3.4 权限与属性:chmod、chown、stat
传完文件经常要改权限,SFTP 里可以直接操作:
chmod 644 file.txt # 修改远程文件权限 chmod 755 script.sh chown user file.txt # 修改属主(需要权限) stat file.txt # 查看文件详细属性chmod的用法和 Linux 一致,支持数字模式和符号模式。stat会输出文件的大小、权限、修改时间等信息,排查问题时很有用。需要注意的是chown通常需要 root 权限,普通用户改属主会报 "Operation not permitted",这不是命令写错了,是权限不够。
注意:改权限前先想清楚。给脚本 777 看似省事,实际上把执行权限开放给了所有人,生产环境里这是安全隐患。脚本一般 755,普通文件 644,敏感配置 600,按这个来基本不会错。
3.5 查看与执行:cat、less、! 命令的妙用
SFTP 里可以直接看远程文件内容:
cat config.txt # 输出整个文件 less bigfile.log # 分页查看大文件less在 SFTP 里能用,但交互体验不如在 shell 里流畅,大文件建议还是下载下来看或者用ssh配合tail。
真正好用的是!前缀,它让你在不退出 SFTP 的情况下执行本地 shell 命令:
!ls # 执行本地 ls !pwd # 查看本地当前目录 !tar czf backup.tar.gz myfolder # 本地打包这个功能在需要临时处理本地文件时特别方便,不用退出再重进。我经常用它来确认本地文件到底在不在,避免传了个不存在的文件还纳闷为什么没反应。
4. 实操过程:从零完成一次完整传输
4.1 场景设定与准备工作
假设现在有个真实需求:把本地一个网站项目目录上传到远程服务器,同时下载服务器上的日志文件回来分析。本地项目在~/projects/mysite,远程目标目录是/var/www/mysite,日志在/var/log/app/下。
第一步先确认本地文件状态:
ls -la ~/projects/mysite du -sh ~/projects/mysitedu -sh看目录总大小,心里有个数,知道要传多久。如果目录里有node_modules这种体积大又不需要传的,先排除掉,不然传半天。
4.2 建立连接并核对两端环境
sftp -P 2222 deploy@192.0.2.10进去之后先核对环境:
pwd lpwd ls /var/www/确认远程/var/www/存在,当前远程目录是家目录。如果目标目录不存在,先建:
mkdir /var/www/mysite这里有个权限问题要提前想:/var/www/通常属于 root,普通用户可能没权限在里面建目录。如果mkdir报 "Permission denied",要么用有权限的账号,要么让管理员提前建好并授权。别硬刚,权限问题硬刚没用。
4.3 执行上传并验证结果
lcd ~/projects/mysite cd /var/www/mysite put -r -p .lcd切到本地项目目录,cd切到远程目标目录,然后put -r -p .把当前目录内容递归上传并保留属性。用.表示当前目录,比写完整路径简洁。
传完之后验证:
ls -la对比本地和远程的文件列表,确认数量一致。如果文件多,可以用!ls | wc -l和ls | wc -l分别数一下两边的文件数,数字对得上基本就没漏。
4.4 下载日志并做后续处理
lcd ~/downloads cd /var/log/app get -p app.log下载完退出:
bye或者exit、quit都行,效果一样。退出后在本地 shell 里就能直接分析日志了。
整个流程走下来,核心就是"先核对两端位置,再传,传完验证"这三步。我见过太多人跳过验证这一步,结果传了一半断了都不知道,等到线上出问题才发现文件不全。
5. 常见问题与排查技巧实录
5.1 连接类问题速查
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| Connection refused | 端口错或服务未启动 | 用-v看端口,确认 SSH 服务在跑 |
| Connection timed out | 网络不通或防火墙拦截 | ping主机,检查网络策略 |
| Permission denied (publickey) | 密钥不对或权限过宽 | chmod 600私钥,确认公钥已授权 |
| Host key verification failed | 主机指纹变了 | 核对指纹后清理旧记录重连 |
连接问题里最常见的就是密钥权限。我前面反复强调chmod 600,就是因为这个坑太普遍了。另一个高频问题是主机指纹变化,比如服务器重装过系统,本地还存着旧指纹,就会拒绝连接。这种情况要确认新指纹确实是目标服务器的,再清理旧记录。
5.2 传输类问题与应对
传大文件传到一半断了,是另一个高频场景。SFTP 本身不支持断点续传,断了就得重来。应对办法有两个:一是用支持续传的工具(比如rsync,它底层也走 SSH),二是传之前先压缩,减少传输量。
传输速度慢的话,先判断瓶颈在哪。小文件多导致的慢,打包解决;大文件慢,可能是带宽限制,可以试试加压缩参数。SFTP 协议本身支持压缩,但不是所有客户端都默认开启。
权限报错 "Permission denied" 出现在put时,说明你对目标目录没有写权限。用ls -ld看目录权限,确认自己的用户和组,再决定是换目录还是找管理员授权。
5.3 我踩过的几个真实坑
第一个坑:用put -r传目录,远程已存在同名目录,结果文件被塞进了子目录里,结构全乱了。后来我养成了习惯,传目录前先确认远程目录不存在,或者先删干净再传。
第二个坑:脚本里用 SFTP 批量传文件,没加-b批处理模式,结果卡在交互提示符上,脚本一直挂着。非交互场景一定要用-b指定命令文件,或者用-oBatchMode=yes禁止任何交互提示。
第三个坑:文件名里有空格或特殊字符,直接put my file.txt会被拆成两个参数。正确做法是用引号包起来:put "my file.txt"。这个坑在 Windows 传过来的文件里特别常见。
第四个坑:以为 SFTP 能像scp一样一条命令搞定,结果发现它默认是交互式的。想一条命令传完就退出,得配合-b或者用scp。两者定位不同,SFTP 适合交互式操作和批量命令,scp适合单次快速传输。
5.4 批处理模式:脚本自动化的关键
前面提到-b,这里展开说一下。建一个命令文件sftp_commands.txt:
cd /var/www/mysite lcd ~/projects/mysite put -r -p . bye然后执行:
sftp -b sftp_commands.txt -P 2222 deploy@192.0.2.10这样整个传输过程无需人工干预,适合放进部署脚本或定时任务。注意批处理模式下,任何需要交互的命令(比如输密码)都会导致失败,所以必须用密钥登录。另外批处理模式下遇到错误默认会继续执行后续命令,想让它出错就停,可以在命令前加-或者用-oBatchMode=yes配合错误检查。
提示:写批处理脚本时,命令文件里每一行末尾不要有多余空格,某些版本会把它当成路径的一部分,导致莫名其妙的失败。
6. 几个提升效率的进阶习惯
用熟基础命令之后,有几个习惯能明显提升效率。第一个是善用 Tab 补全,SFTP 交互模式支持路径补全,敲一半按 Tab 能省不少事,也能避免手敲路径出错。第二个是把常用操作写成批处理文件存起来,比如"上传网站"、"下载日志"各一个,需要时直接调用。
第三个习惯是传输前先算大小。用du -sh看本地目录,用df -h看远程磁盘剩余空间,确认空间够再传。我遇到过传了一半磁盘满了的情况,前面的文件传上去了,后面的全失败,清理起来很麻烦。
第四个习惯是重要传输做校验。传完之后用md5sum或sha256sum对比两端的文件哈希,确认内容完全一致。对于配置文件、脚本这类不能出错的文件,这一步值得花时间。命令很简单,本地算一次,远程用!前缀或者ssh算一次,对比结果就行。
最后说个心态上的体会:SFTP 命令不多,真正需要记的就那么十几个,但每个都有细节。与其贪多,不如把这十几个练到不用想就能敲出来,遇到问题知道往哪个方向排查。工具的价值不在于功能多,而在于你能不能在需要的时候稳定地用出来。我自己到现在,每次连新服务器还是会先pwd和lpwd确认位置,这个习惯帮我省了无数次传错地方的麻烦。