☰
SFTP命令行实战:从登录认证到批量脚本的完整指南
2026/10/19 3:05:41 网站建设 项目流程

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前缀。

远程操作本地操作作用
pwdlpwd显示当前目录
cdlcd切换目录
lslls列出目录内容
mkdirlmkdir创建目录
rmdirlrmdir删除目录

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.100

commands.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 的命令行用法不算复杂,但细节很多。真正掌握它的方式不是背命令,而是在实际场景里反复用,遇到问题就查、就试。用得多了,自然就知道什么场景该用什么命令,什么参数能解决什么问题。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询