SFTP 这个工具,很多人第一次接触都是在“要把文件传到服务器上”这个场景里。有人用图形化客户端拖拽,有人用 scp 一行命令搞定,但真正到了需要频繁登录、批量操作、排查权限问题的时候,SFTP 的交互式命令行才是效率最高的方式。这篇内容就围绕 SFTP 的登录流程和命令行用法展开,把从连接建立、目录导航、文件上传下载,到权限处理、批量脚本、常见报错排查这一整套东西讲透。不管你是刚接触 Linux 运维的新手,还是平时偶尔需要传文件的后端开发,都能从里面找到可以直接抄作业的操作步骤和踩坑经验。
1. 先搞清楚 SFTP 到底站在什么位置上
1.1 SFTP 和 FTP、SCP 的本质区别
很多人把 SFTP 和 FTP 混为一谈,觉得就是“带加密的 FTP”,这个理解只对了一半。FTP 是独立的一套协议,走的是自己的控制连接和数据连接,而 SFTP 实际上是 SSH 协议的一个子系统。也就是说,SFTP 复用了 SSH 的加密通道和认证机制,所有数据——包括你输入的密码、传输的文件内容、执行的目录列表命令——全部跑在一条加密连接里。
这一点带来的直接好处是:你不需要额外开放端口。SSH 默认监听 22 端口,SFTP 作为它的子系统,天然就挂在这个端口上。而传统 FTP 需要开 21 端口做控制,还得再开一段被动端口范围做数据传输,防火墙配置起来相当麻烦。
和 SCP 相比,两者都基于 SSH,区别在于使用方式。SCP 更适合“一条命令传完就走”的场景,比如scp file.txt user@host:/path/。但 SCP 有个明显短板:它不支持断点续传,也不支持在传输过程中做目录浏览和交互式操作。SFTP 则是交互式的,你登录进去之后可以像操作本地文件系统一样,用ls、cd、pwd、mkdir这些命令来导航,然后再决定传什么、传到哪里。
我个人的选择习惯是这样的:临时传一两个文件用 SCP,需要批量操作、需要先看看目录结构再决定、或者需要传大量小文件的时候,一律用 SFTP 交互式命令行。
1.2 为什么命令行 SFTP 比图形化工具更值得掌握
图形化 SFTP 客户端确实上手快,拖拽就行。但它有几个绕不开的问题。第一,很多生产环境不允许你随便装第三方软件,尤其是跳板机、堡垒机这类环境,你只能用终端。第二,图形化工具在传输大量小文件时性能往往不如命令行,因为它们要维护文件列表的 UI 渲染。第三,也是最关键的——命令行可以脚本化。
举个实际场景:你需要每天凌晨把本地生成的日志文件上传到备份服务器,并且只上传当天新增的文件。这个需求用图形化工具根本没法自动化,但用 SFTP 的批处理模式,写一个几行的脚本配合定时任务就能搞定。这种“一次配置、长期受益”的能力,才是命令行 SFTP 真正的价值所在。
1.3 SFTP 登录的几种认证方式
SFTP 登录本质上就是 SSH 登录,认证方式主要有三种:密码认证、密钥认证、以及键盘交互认证。密码认证最直观,输入用户名和密码就行,但安全性最低,而且没法自动化。密钥认证是生产环境的主流做法,把本地私钥和服务器上的公钥配对,登录时不需要手动输密码。键盘交互认证则是一些特殊场景下的补充,比如需要输入动态验证码的情况。
后面我会分别讲这几种方式的具体操作,以及在实际使用中哪种场景该选哪种。
2. 登录 SFTP 的完整流程与参数拆解
2.1 最基础的登录命令长什么样
最基础的 SFTP 登录命令格式是这样的:
sftp username@hostname比如你要登录到一台 IP 为 192.168.1.100 的服务器,用户名是 deploy,那就是:
sftp deploy@192.168.1.100回车之后,系统会提示你输入密码。输入正确后,你会看到提示符从本地的$变成了sftp>,这就说明你已经进入了 SFTP 的交互式会话。
这里有个细节很多人没注意:SFTP 默认走 22 端口,但如果目标服务器的 SSH 服务改到了其他端口,你就需要用-P参数指定。注意是大写 P,不是小写 p。小写 p 在 SFTP 里是“preserve”的意思,用来保留文件的时间戳和权限,两者完全不同。
sftp -P 2222 deploy@192.168.1.100这个大小写的问题我见过太多人踩坑了,输错了之后连接超时,还以为是网络问题,排查半天才发现是参数写错了。
2.2 用密钥登录时需要注意的细节
密钥登录的命令稍微复杂一点,需要指定私钥文件:
sftp -i ~/.ssh/id_rsa deploy@192.168.1.100如果你用的是非默认名称的密钥,比如~/.ssh/mykey,那就把-i后面的路径改掉。这里有个常见问题:私钥文件的权限必须是 600,也就是只有所有者可读写。如果权限太开放,SSH 会直接拒绝使用这个密钥,报错信息大概是“Permissions 0644 for 'xxx' are too open”。解决办法很简单:
chmod 600 ~/.ssh/id_rsa另外,如果你在生成密钥时设置了 passphrase,每次登录都需要输入这个 passphrase。想免输的话,可以用 ssh-agent 把密钥加载进去:
eval "$(ssh-agent -s)" ssh-add ~/.ssh/id_rsa这样在当前会话里就不用反复输入了。但要注意,ssh-agent 是会话级的,关掉终端就失效了,下次还得重新加载。
2.3 通过跳板机登录 SFTP 的正确姿势
生产环境里,目标服务器往往不直接对外暴露,需要经过一台跳板机中转。SFTP 本身没有类似 SSH 的-J参数来指定跳板,但可以通过 SSH 的 ProxyJump 配置来实现。
最直接的方式是在~/.ssh/config里写好配置:
Host jump-host HostName 10.0.0.1 User jumper Port 22 Host target-server HostName 192.168.1.100 User deploy Port 22 ProxyJump jump-host配置好之后,直接sftp target-server就能自动经过跳板机连到目标服务器。这个配置方式的好处是,你不需要在命令行里写一长串参数,而且 scp、ssh 等其他工具也能复用这套配置。
如果没有权限改 ssh config,也可以用-o参数临时指定:
sftp -o "ProxyJump jumper@10.0.0.1" deploy@192.168.1.100这种方式适合临时用一次的场景,但每次都要敲这么长一串,实际用起来还是挺麻烦的。
2.4 登录后第一件事:确认你在哪、对面是谁
登录成功之后,很多人直接就cd到目标目录开始操作了。但我建议你先做两件事:确认本地当前目录和远程当前目录。
SFTP 会话里有两个“当前目录”的概念:一个是本地的,一个是远程的。lpwd显示本地当前目录,pwd显示远程当前目录。刚登录时,远程目录通常是用户的家目录,本地目录则是你执行 sftp 命令时所在的目录。
sftp> lpwd Local working directory: /home/user sftp> pwd Remote working directory: /home/deploy这个确认动作看起来多余,但我遇到过好几次这样的情况:本地在/tmp目录下执行 sftp,登录后直接put一个文件,结果提示“No such file”。原因就是本地当前目录不对,文件根本不在/tmp里。先lpwd看一眼,能省掉很多莫名其妙的报错。
3. 交互式会话里的核心命令分类详解
3.1 目录导航与查看类命令
SFTP 的目录导航命令和 Linux shell 非常像,但有一个关键区别:所有针对远程的操作都是不带前缀的,针对本地的操作都要加l前缀。
| 远程操作 | 本地操作 | 作用 |
|---|---|---|
| pwd | lpwd | 显示当前目录 |
| cd | lcd | 切换目录 |
| ls | lls | 列出目录内容 |
| mkdir | lmkdir | 创建目录 |
| rmdir | lrmdir | 删除目录 |
ls命令支持一些常用参数,比如ls -l显示详细信息,ls -a显示隐藏文件。但要注意,SFTP 的ls不支持通配符过滤,比如ls *.txt是不行的。想按模式筛选,得用ls -l看完之后自己找,或者退出 SFTP 用 shell 的 glob 展开。
cd命令支持相对路径和绝对路径,也支持..返回上级。但 SFTP 里没有cd -这种返回上一次目录的快捷方式,需要自己记住路径。
3.2 文件传输:put 和 get 的参数细节
put是上传,get是下载。最基本的用法:
sftp> put localfile.txt sftp> get remotefile.txt如果不指定远程路径,put会把文件传到远程当前目录,文件名保持不变。get同理,下载到本地当前目录。
但实际使用中,往往需要指定目标路径或重命名:
sftp> put localfile.txt /remote/path/newfile.txt sftp> get /remote/path/remotefile.txt /local/path/localfile.txt这里有个容易忽略的点:如果目标路径是一个已存在的目录,put会把文件放进那个目录里,文件名不变。但如果目标路径是一个不存在的路径,且最后一段看起来像文件名,put就会把它当作新文件名。这个行为有时候会让人困惑,建议在不确定的时候,先ls确认一下目标位置的情况。
传输目录需要加-r参数:
sftp> put -r localdir sftp> get -r remotedir但要注意,-r递归传输时,如果远程目录不存在,put -r会报错,需要先手动mkdir创建目标目录。这个和cp -r的行为不太一样,cp -r会自动创建目标目录,但 SFTP 不会。
3.3 文件权限与属性操作
SFTP 里可以查看和修改文件权限,用的是chmod命令:
sftp> chmod 755 script.sh sftp> chmod 644 config.txt但 SFTP 的chmod只支持数字模式,不支持u+x这种符号模式。想用符号模式,得退出 SFTP 用 ssh 执行。
查看文件详细信息用ls -l,输出格式和 Linux 的ls -l基本一致,包含权限、所有者、大小、修改时间。但 SFTP 里没有stat命令,看不了更详细的 inode 信息。
删除文件用rm,删除目录用rmdir。注意rmdir只能删除空目录,如果目录里有文件,需要先删文件再删目录。SFTP 没有rm -rf这种递归删除的命令,这是出于安全考虑的设计,避免误操作把整个目录树删掉。
3.4 本地命令的执行:! 和 l 前缀的配合
SFTP 会话里可以执行本地 shell 命令,用!开头:
sftp> !ls sftp> !pwd sftp> !mkdir localdir这个功能在需要临时查看本地文件或创建本地目录时很有用,不用退出 SFTP 再重新登录。
但要注意,!执行的命令是在本地 shell 里跑的,环境变量和当前 SFTP 会话是独立的。比如你在 SFTP 里lcd切换了本地目录,!pwd显示的会是这个新目录,因为lcd改变的就是本地会话的工作目录。
!和l前缀命令的区别在于:l前缀命令是 SFTP 内置的,执行效率高,输出格式统一;!执行的是外部命令,功能更灵活,但输出可能比较杂乱。日常操作优先用l前缀命令,需要复杂操作时再用!。
4. 批处理模式:把重复劳动交给脚本
4.1 用 -b 参数执行批处理文件
SFTP 支持从文件读取命令序列,用-b参数指定:
sftp -b commands.txt deploy@192.168.1.100commands.txt里写的就是你平时在交互式会话里敲的命令,一行一条:
cd /remote/upload lcd /local/data put file1.txt put file2.txt ls -l bye这个方式适合把固定的操作流程固化下来。比如每天都要上传同一批文件到同一个目录,写一次脚本,以后直接执行就行。
但批处理模式有个限制:它不支持交互式输入。也就是说,如果登录需要输密码,批处理模式就没法用。所以批处理模式通常配合密钥认证使用。
4.2 批处理脚本中的错误处理
默认情况下,批处理模式遇到错误会继续执行后面的命令。比如cd到一个不存在的目录失败了,脚本不会停下来,后面的put可能会传到错误的位置。
要改变这个行为,可以在脚本开头加一行:
-errors不对,正确的写法是在 sftp 命令上加-o参数:
sftp -o BatchMode=no -b commands.txt deploy@192.168.1.100实际上,SFTP 批处理模式默认就是遇到错误继续执行。想让它在出错时停止,需要在脚本里用-前缀标记“允许失败”的命令,其他命令一旦失败就会终止。但更常见的做法是在脚本里显式检查关键步骤的结果,比如:
cd /remote/upload ls put file1.txt如果cd失败,后面的put会传到默认目录,可能导致文件放错位置。稳妥的做法是在脚本开头先pwd确认位置,或者用绝对路径避免依赖当前目录。
4.3 结合 shell 脚本实现自动化传输
批处理模式最强大的用法是和 shell 脚本结合。比如这样一个场景:每天把本地/data/logs目录下当天生成的.log文件上传到备份服务器。
#!/bin/bash TODAY=$(date +%Y%m%d) LOGDIR="/data/logs" REMOTE_DIR="/backup/logs/$TODAY" # 生成 SFTP 命令文件 cat > /tmp/sftp_commands.txt << EOF mkdir $REMOTE_DIR cd $REMOTE_DIR lcd $LOGDIR put *.log bye EOF # 执行 SFTP 批处理 sftp -b /tmp/sftp_commands.txt deploy@192.168.1.100 # 清理临时文件 rm -f /tmp/sftp_commands.txt这个脚本的关键点在于:用 heredoc 动态生成命令文件,把日期变量嵌进去。这样每天执行时,远程目录会自动按日期创建,不会覆盖之前的数据。
但要注意put *.log这个写法。SFTP 的put不支持通配符展开,*.log会被当作字面文件名,导致报错。正确的做法是在 shell 里先展开文件列表,或者用mput命令(如果 SFTP 版本支持)。
更稳妥的方式是用循环逐个上传:
for f in $LOGDIR/*.log; do echo "put $f" >> /tmp/sftp_commands.txt done这样每个文件一条put命令,虽然命令文件长一点,但兼容性最好。
5. 那些文档里不会写的踩坑经验
5.1 路径中的空格和特殊字符处理
文件名里有空格是家常便饭,但在 SFTP 命令行里处理起来比较麻烦。比如要上传一个叫my report.txt的文件:
sftp> put my report.txt这样写会报错,因为 SFTP 把空格当成了参数分隔符。正确的做法是用引号包起来:
sftp> put "my report.txt"或者用反斜杠转义:
sftp> put my\ report.txt但如果是批处理模式,引号的处理又不一样。在命令文件里,引号会被当作文件名的一部分,导致找不到文件。这种情况下,建议在生成命令文件时就把文件名处理好,或者用!在本地先重命名成不含空格的名字再传。
5.2 大文件传输中断后的续传问题
SFTP 本身不支持断点续传。如果传一个几 GB 的文件传到一半网络断了,只能重新传。这是 SFTP 相比 rsync 的一个明显劣势。
如果经常需要传大文件,建议用rsync替代:
rsync -avz --partial --progress localfile deploy@192.168.1.100:/remote/path/--partial参数让 rsync 保留中断的部分,下次传输时可以续传。但 rsync 需要两端都安装,有些受限环境可能没有。
如果只能用 SFTP,一个变通方案是先把大文件切分成小块,传完之后在远程合并:
# 本地切分 split -b 100M bigfile.tar.gz part_ # 上传所有分片 sftp -b upload_parts.txt deploy@192.168.1.100 # 远程合并(通过 ssh 执行) ssh deploy@192.168.1.100 "cat /remote/path/part_* > /remote/path/bigfile.tar.gz"这个方式比较笨,但在网络不稳定又只能用 SFTP 的环境下,确实能解决问题。
5.3 权限不足时的排查思路
上传文件时遇到“Permission denied”是最常见的报错之一。排查思路可以按这个顺序来:
第一,确认远程目标目录是否存在。如果目录不存在,put会报错,但报错信息可能不够明确。先用ls确认目录存在。
第二,确认当前用户对目标目录有没有写权限。用ls -ld查看目录权限:
sftp> ls -ld /remote/path drwxr-xr-x 2 root root 4096 Jan 1 00:00 /remote/path如果目录所有者是 root,权限是 755,那普通用户就没有写权限。这种情况需要联系管理员调整权限,或者换一个有写权限的目录。
第三,确认目标文件是否已存在且不可覆盖。如果文件已存在但当前用户没有写权限,put也会失败。可以先rm删掉旧文件再传,或者用put -f强制覆盖(如果 SFTP 版本支持)。
第四,检查磁盘空间。有时候权限没问题,但磁盘满了,也会报类似的错误。用df -h查看远程磁盘使用情况。
5.4 中文文件名乱码的根源与解决
中文文件名乱码是 SFTP 使用中的一个经典问题。根源在于本地和远程的字符编码不一致。本地终端通常是 UTF-8,但有些服务器的 locale 设置可能是 GBK 或其他编码。
表现是:本地看到的中文文件名正常,上传到远程后变成乱码;或者远程的中文文件名在本地显示为问号。
解决办法分两种情况。如果服务器支持 UTF-8,把服务器的 locale 改成 UTF-8:
export LANG=en_US.UTF-8 export LC_ALL=en_US.UTF-8如果服务器不支持改 locale,那就尽量避免在文件名里用中文。用英文或拼音命名,虽然不够直观,但能避免很多麻烦。
还有一个临时方案:在 SFTP 会话里用!执行本地命令时,确保本地终端的编码和远程一致。但这个方案不太可靠,因为涉及终端模拟器、SSH 客户端、服务器 locale 多个环节,任何一个不一致都会出问题。
6. 从 SFTP 到更高效的传输方案
6.1 什么时候该放弃 SFTP 换别的工具
SFTP 适合交互式操作和小批量文件传输,但以下几种情况建议换工具:
需要传输大量小文件时,SFTP 的效率很低,因为每个文件都要单独执行一次传输命令。这种情况用rsync或者先打包再传会快很多。
需要断点续传时,SFTP 无能为力,只能用rsync或lftp。
需要双向同步时,SFTP 只能手动对比和传输,rsync的增量同步能力更合适。
需要在传输过程中做压缩时,SFTP 不支持传输时压缩,rsync -z或者scp -C可以。
但 SFTP 也有它的不可替代性:几乎所有 SSH 服务器都默认支持 SFTP,不需要额外安装配置。在受限环境里,SFTP 往往是唯一可用的文件传输方式。
6.2 用 ssh 配置文件简化日常操作
如果你经常需要连接同一批服务器,把配置写到~/.ssh/config里能省很多事:
Host backup HostName 192.168.1.100 User deploy Port 22 IdentityFile ~/.ssh/id_rsa Host webserver HostName 192.168.1.101 User www Port 2222配置好之后,sftp backup就能直接连到备份服务器,不用再敲用户名、IP、端口、密钥路径这一长串。
这个配置文件还支持通配符,比如:
Host 192.168.1.* User admin IdentityFile ~/.ssh/admin_key这样所有 192.168.1.x 网段的服务器都用同一个用户名和密钥,进一步简化操作。
6.3 把常用操作封装成 shell 函数
如果你发现自己反复执行同一套 SFTP 操作,可以把它封装成 shell 函数,放到~/.bashrc里:
sftp_upload() { local local_file="$1" local remote_dir="$2" local host="$3" sftp -b - "$host" << EOF cd $remote_dir put $local_file bye EOF }用的时候直接sftp_upload ./data.csv /upload backup就行。这个函数把命令文件通过标准输入传给 sftp,省去了创建临时文件的步骤。
但要注意,heredoc 里的变量展开是在本地 shell 里做的,如果变量值包含特殊字符,可能会出问题。稳妥的做法是对变量做转义,或者用单引号 heredoc 再手动处理变量。
7. 几个真实场景下的操作复盘
7.1 场景一:从服务器批量下载日志文件
需求是这样的:服务器上/var/log/app/目录下有一批按日期命名的日志文件,需要把最近 7 天的下载到本地分析。
首先登录 SFTP,看看目录里有什么:
sftp deploy@192.168.1.100 sftp> cd /var/log/app sftp> ls -l假设文件命名格式是app-2024-01-01.log这样的。要下载最近 7 天,需要先确定日期范围。SFTP 里没法直接做日期计算,所以得在本地 shell 里算好,再生成命令文件。
#!/bin/bash for i in $(seq 0 6); do DATE=$(date -d "-$i days" +%Y-%m-%d) echo "get /var/log/app/app-$DATE.log /local/logs/" done > /tmp/download_commands.txt echo "bye" >> /tmp/download_commands.txt sftp -b /tmp/download_commands.txt deploy@192.168.1.100这个脚本的关键在于用date -d做日期偏移计算,生成 7 天的文件名列表。注意get命令的目标路径要以/结尾,表示这是一个目录,否则 SFTP 会把最后一个文件下载成名为logs的文件。
7.2 场景二:上传网站静态资源并设置权限
网站更新时,需要把本地构建好的静态资源上传到服务器,并且设置正确的权限。静态文件通常是 644,目录是 755。
#!/bin/bash LOCAL_DIR="/local/build/static" REMOTE_DIR="/var/www/html/static" cat > /tmp/deploy_commands.txt << EOF cd $REMOTE_DIR lcd $LOCAL_DIR put -r * chmod 644 *.css chmod 644 *.js chmod 755 images bye EOF sftp -b /tmp/deploy_commands.txt deploy@192.168.1.100这里有几个点要注意。put -r *会递归上传所有文件和目录,但*的展开是在本地 shell 做的,所以命令文件里会列出所有实际的文件名。如果文件很多,命令文件会很长,但这是必要的,因为 SFTP 的put不支持通配符。
chmod命令在 SFTP 里只支持数字模式,而且不能递归。所以需要逐个文件类型设置权限。如果目录结构复杂,建议在服务器端用find命令批量设置,而不是在 SFTP 里一个个改。
7.3 场景三:跨服务器中转文件的迂回方案
有时候需要在两台不能直接通信的服务器之间传文件,只能通过本地中转。比如从服务器 A 下载,再上传到服务器 B。
最直接的方式是分两步:
# 从 A 下载 sftp -b - deploy@server-a << EOF get /data/file.tar.gz /tmp/ bye EOF # 上传到 B sftp -b - deploy@server-b << EOF put /tmp/file.tar.gz /backup/ bye EOF但如果文件很大,本地磁盘空间不够,这个方案就行不通了。变通方案是用管道直接转发,不落盘:
ssh deploy@server-a "cat /data/file.tar.gz" | ssh deploy@server-b "cat > /backup/file.tar.gz"这个命令通过 SSH 管道直接把 A 的文件流式传输到 B,本地只做中转,不占磁盘空间。但要注意,这种方式没有进度显示,传输大文件时不知道进度。可以配合pv命令查看进度:
ssh deploy@server-a "cat /data/file.tar.gz" | pv | ssh deploy@server-b "cat > /backup/file.tar.gz"pv需要本地安装,但它能显示传输速度、已传输大小和预计剩余时间,对于大文件传输很有用。
8. 关于 SFTP 命令行的一些个人体会
用了这么多年 SFTP,我最大的感受是:它的价值不在于单个命令有多强大,而在于组合起来能解决很多看似麻烦的问题。交互式会话适合探索和临时操作,批处理模式适合固化和自动化,两者配合使用,基本能覆盖日常文件传输的所有需求。
几个我觉得值得记住的点:第一,lpwd和pwd一定要养成登录后就确认的习惯,能避免大部分“文件找不到”的问题。第二,批处理模式配合密钥认证是自动化的基础,值得花时间配置好。第三,遇到权限问题先看目录权限和所有者,大部分情况都是这两个原因。第四,中文文件名能避则避,省下来的排查时间够干很多正事了。
SFTP 的命令行用法不算复杂,但细节很多。真正掌握它的方式不是背命令,而是在实际场景里反复用,遇到问题就查、就试。用得多了,自然就知道什么场景该用什么命令,什么参数能解决什么问题。