macOS iTerm2使用trzsz上传文件到服务器或从服务器下载文件
Table of Contents
刚写完macOS iTerm2使用lrzsz上传文件到服务器或从服务器下载文件,就了解到有个trzsz,它的原理与lrzsz完全相同,它的主要优点有四个:
- 1、有上传进度条;
- 2、能上传文件夹;
- 3、支持tmux;
- 4、不依赖于特定终端:rz/sz必须依赖于集成了rz/sz功能的终端,或者依赖于像iTerm2这样的可以设置触发器的终端,如果一个终端没有集成rz/sz功能,又没有iTerm2这样的触发器功能,那就无法使用rs/sz,而trz/tsz就不受此限制。而且对于iTerm2,就算它支持触发器,但每个profile的触发器都是单独的,所以如果你的profile多的话,每个profile都得重复添加添加触发器也挺麻烦的。
我们知道原始工具叫lrzsz(对应rz/sz两个命令),而现在这个工具叫trzsz(对应trz/tsz两个命令),多了个t,为什么用t这个字母?因为作者当时是为了在tmux中用,这个t正是来自于tmux首字母。
传输原理
trzsz是利用当前ssh已经建立的连接的stdin和stdout来传输数据的。
当我们用ssh user@x.x.x.x
登录服务器后,在服务器上敲字符(只是敲字符,没有按回车),其实是字符先传到服务器,服务器再回传回来,最终才显示在终端上的。这也能解释为什么当你的服务器比较慢时(比如国外服务器),你在服务器上敲命令总有一种“延迟感”,敲的很不得劲的原因。
由于是通过stdin和stdout来传输,所以只要你能登录上服务器,那就一定能传输,不存在跳板机的问题。
上传
即本地电脑向服务器传输数据,相当于tsz
一段一段的读取你文件的二进制码,并帮你把它些码压缩、base64编码后“敲”到终端上,按前面所说,这些码自然是被通过ssh传输到了服务器,正常情况下在终端敲的字符传到服务器后服务器会回传到终端并显示出来,但是用trzsz
传输时,是首先在服务器端运行trz
,所以tsz
在终端上“敲”的字符传到服务器后,都被服务器端的trz
“吞”掉了,它并没有回传,当tsz
“敲”完字符后会告诉服务器的trz
,于是trz
就把它接收的二进制码保存成一个文件,于是二进制码就“还原”为文件了。
下载
即服务器向本地电脑传输数据,服务器端的tsz
把文件二进制码发送回你本地电脑的终端,按道理终端应该显示出这些码的,但由于终端运行了trz
,于是本应回显的码全部被trz拦截接收了,最终接收完后保存成一个文件,于是服务器端的文件就被“下载”下来了。
与传统传输对比
scp/rsync传输文件
由于scp和rsync除了选项不同外,其它的操作都几乎一样,所以这里以scp为例。
上传:
假设你正在服务器上配置一些东西,当做到某一步时,突然需要把电脑中的某个文件/文件夹上传到服务器当前目录下,上传步骤如下:
- 1、输入pwd输出当前路径,并复制路径;
- 2、终端切换到另一个本地Tab,并在里面输入:
scp /path/to/file user@xx.xx.xx.xx:
,然后粘贴刚刚复制的路径,变成完整路径scp /path/to/fileorfolder user@xx.xx.xx.xx:/path/to/folder
,回车,输入密码,开始上传,当然如果你上传文件夹,还要加个-r
; - 3、更简单的做法,你配置了公钥免密登录以及在
~/.ssh/config
中配置了服务器别名,则2中的步骤简化为:终端切换到另一个本地Tab,并在里面输入:scp /path/to/file 服务器别名:
,然后粘贴刚刚复制的路径,变成完整路径scp /path/to/file 服务器别名:/path/to/folder
,回车,开始上传,当然如果你上传文件夹,还要加个-r
;
但是这里有一个问题,有时候服务器太久没操作,你可能忘了服务器别名,于是你还要去找…
下载:
假设你正在服务器上配置一些东西,当做到某一步时,突然需要把当前目录下一个文件/文件夹下载到你电脑中,下载步骤如下:
- 1、pwd输出当前路径,并复制路径;
- 2、终端切换到另一个本地Tab,并在里面输入:
scp user@xx.xx.xx.xx:
,然后粘贴刚刚复制的路径,scp user@xx.xx.xx.xx:/path/to/folder/
,然后再回去复制一下文件名,再回来粘贴,最终变成这样scp user@xx.xx.xx.xx:/path/to/folder/filename .
,回车,终于开始下载了; - 3、更简单的做法,你配置了公钥免密登录以及在
~/.ssh/config
中配置了服务器别名,则2中的步骤简化为:终端切换到另一个本地Tab,并在里面输入:scp 服务器别名:
,然后粘贴刚刚复制要下载的文件在哪个目录下,变成scp 服务器别名:/path/to/folder/
(其中最后一个斜杠是手输的),然后再回去复制一次文件名,变成完整路径scp 服务器别名:/path/to/folder/filename .
,回车,开始下载,当然如果你下载文件夹,还要加个-r
;
但是这里有一个问题,有时候服务器太久没操作,你可能忘了服务器别名,于是你还要去找…
缺点:每次都需要从服务器窗口切换到本地窗口,都要输入很多命令,要在命令中指定路径等等;
优点:能传输的文件比较大,传输比较稳定,并且可以从一台服务器传到另一台服务器。
trz/tsz传输文件
上传:
假设你正在服务器上配置一些东西,当做到某一步时,突然需要把电脑中的某个文件/文件夹上传到服务器当前目录下,上传步骤如下:
方法一:在当前终端上输入trz
,按回车,弹出选择文件窗口,用鼠标在文件选择窗口找到并选中要上传的文件,确定,开始上传;
方法二:在访达中找到文件,拖上终端上,稍等一会儿,上传完毕!
下载:假设你正在服务器上配置一些东西,当做到某一步时,突然需要把当前目录下一个文件/文件夹(假设是文件,名称为“filename”)下载到你电脑中,下载步骤如下:
输入tsz fil
(如果下载目录就trz -d fil
),按Tab补全文件名,回车,稍等一会儿,下载完毕,文件保存在Mac下的“下载”目录中(当然也可以是自定义的目录,配置可以改);
缺点:
- 1、只能用于本地电脑到服务器之间文件的传输,无法用于服务器与服务器之间的文件传输;
- 2、遇到过报错,可能是我的服务器的网络状况比较不好吧,不过以后遇到提提issue,应该慢慢能修复。
优点:操作超级方便,特别是上传文件,鼠标一拖就上传了,超级方便有木有,下载也只需要简单的敲几下键盘,比scp/rsync简单的多的多。
安装trzsz
trzsz与ltrsz一样,有两个工具,一个tsz
用于发送,一个trz
用于接收,当服务器端接收时(运行trz
),本地电脑就要对应运行tsz
来发送文件,反之亦然,只不过本地电脑上的都自动化了,所以并不需要在本地电脑上手动执行trz
或tsz
(你也不能手动执行,否则会报错)。
所以服务器端和客户端都要安装trzsz。
不同语言版本介绍
trzsz项目有三个版本:
- 1、trzsz:python语言版,为最初版本,用于支持macOS中的iTerm2 tmux Integration功能,如果你在macOS上不用iTerm2的这个tmux功能,就可以不安装该版本,而是安装trzsz-go;
- 2、trzsz.js:用于支持electerm、tabby,ttyd等这种基于网页(或Electron)的终端集成trzsz功能,像electerm和ttyd就集成了trzsz功能,其实就是trzsz作者自己提交的pr: electerm-trzsz-pr、ttyd-trzsz-pr,tabby我倒是没找到有pr。集成的意思就是你不用自己在客户端安装trzsz/trzsz-go了,它本身已经自带这功能了,只有那些没有集成的终端才需要自己安装;
- 3、trzsz-go:go语言版本,是最晚写的,也是支持最广泛的版本,除了通过网页打开的那种webshell需要trzsz.js外,其它所有终端都可以使用trzsz-go版。
前面对三个版本的解释都是指在本地电脑上安装时的选择,而对于服务器端,全部用trzsz-go就行,因为trzsz需要本地电脑和服务器上都安装了才要以使用。
当然服务器端也是可以安装python版本或trzsz.js版本的,python版本其实就类似于python3 trzsz.py
这么执行的,只是它封装成一个命令了,事实上并不是可执行文件;
trzsz.js版本同理,它本质上只是nodejs脚本(而不是可执行文件),只是封闭成了命令,它本质上是类似于node trzsz.js
这么执行的(就像npm
命令,它并不是一个可执行文件,而只是一个nodejs写的命令,它的运行依赖于node);
不过虽然服务器端可以安装python版和trzsz-js版本,但个人感觉没必要,因为各种包管理器apt,yum,scoop等等默认安装的都是trzsz-go,说明作者自己就默认认为安装go版比较好(毕竟go版本不需要额外的python或node环境支持)。
而对于trzsz-ssh,你可以认为它就是个壳,一个套了trzsz-go的ssh终端,可以看到trzsz-ssh代码中是引用了trzsz-go这个包的
package tssh
import (
"errors"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/kevinburke/ssh_config"
"github.com/manifoldco/promptui"
"github.com/trzsz/trzsz-go/trzsz"
"golang.org/x/crypto/ssh"
"golang.org/x/term"
)
服务器端安装
# Ubuntu
sudo apt update && sudo apt install software-properties-common
sudo add-apt-repository ppa:trzsz/ppa && sudo apt update
sudo apt install trzsz
# Debian
sudo apt install curl gpg
curl -s 'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x7074ce75da7cc691c1ae1a7c7e51d1ad956055ca' \
| gpg --dearmor -o /usr/share/keyrings/trzsz.gpg
echo 'deb [signed-by=/usr/share/keyrings/trzsz.gpg] https://ppa.launchpadcontent.net/trzsz/ppa/ubuntu jammy main' \
| sudo tee /etc/apt/sources.list.d/trzsz.list
sudo apt update
sudo apt install trzsz
# CentOS
echo '[trzsz]
name=Trzsz Repo
baseurl=https://yum.fury.io/trzsz/
enabled=1
gpgcheck=0' | sudo tee /etc/yum.repos.d/trzsz.repo
sudo yum install trzsz
服务器安装的名称虽然是trzsz,但实际上安装的是trzsz-go,如果你的系统不是以上列出的系统,你可以直接下载releases,解压后有三个文件,分别为:trz
, tsz
, trzsz
,把它们丢进/usr/local/bin/
即可,用上面的包管理器安装也是安装的trz
, tsz
, trzsz
这三个命令。
本地电脑安装
本地电脑是macOS
brew install trzsz
安装好之后有trz
,tsz
和trzsz-iterm2
三个命令。
也可以安装trzsz-go(前面不同语言版本介绍中解释了这两个版本的不同)
brew install trzsz-go
本地电脑是Linux
# Ubuntu
sudo apt update && sudo apt install software-properties-common
sudo add-apt-repository ppa:trzsz/ppa && sudo apt update
sudo apt install trzsz
安装好之后有trz
,tsz
和trzsz
命令。如果你的系统不是以上系统,可以自己下载releases,解压后有三个文件,分别为:trz
, tsz
, trzsz
,把它们丢进/usr/local/bin/
即可。
本地电脑是Windows
scoop bucket add extras
scoop install trzsz
也可以不用命令安装,而是自己去下载releases,解压后有三个文件,分别为:trz.exe
, tsz.exe
, trzsz.exe
,把它们放到合适的地方,并添加到环境变量中,让你在windows Terminal下可以直接运行:trzsz -h
,则说明配置成功了。
其实trzsz项目(地址)最初只有python版本,只支持macOS+iTerm2的,因为作者用的是macOS+iTerm2。后来由于很多人提issue希望支持electerm,tabby,所以有了js版,再后来又有人希望支持Linux(如Ubuntu)自带的终端和windows Terminal,于是又写了go版(因为go版只有一个可执行文件,比较方便,无需依赖python环境)。
在iTerm2上配置(macOS)
只有使用macOS的iTerm2需要配置,如果本地电脑是Linux桌面系统或Windows,则无需配置。
其实iTerm2也可以不配置,只是这样的话,你就要换成安装trzsz-go(brew install trzsz-go
)了,但trzsz-go无法支持iTerm2 tmux Integration功能(如果你用不到这个功能,那就不用管)。
而且如果安装trzsz-go有一个好处,就是不需要配置iTerm2的触发器和拖放上传了,因为触发器需要在每个Profile中都配置一遍,所以还是挺麻烦的,我建议如果你用不到iTerm2 tmux Integration功能,那就安装trzsz-go(brew install trzsz-go
),别安装trzsz(brew install trzsz
)。
启用python API
iTerm2 -> Preferences… -> General -> Magic,选中Enable Python API
iTerm2 -> Preferences… -> Advanced -> 搜索“cookie”,选择“Yes”
根据图中所说,启用cookie后,会允许iTerm2调用python时不需要用户确认。
配置triggers
iTerm2左上角菜单→Preferences…→Profiles→Default→Advanced→Triggers的“Edit”
左下角+
号添加一条,然后填入图中内容,添加完就可以关掉了
图片不好复制,我做了个表格可以直接复制(注意最后不要多复制回车)
Regular Expression | Action | Parameters | Instant | Enabled |
---|---|---|---|---|
:(:TRZSZ:TRANSFER:[SRD]:\d+\.\d+\.\d+:\d+) |
Run Silent Coprocess… | /usr/local/bin/trzsz-iterm2 -p text -d '/Users/你Mac的用户名/Downloads' \1 |
勾选 | 勾选 |
注意每个profile的Trigger是独立的,前面是在default中设置的,如果你有其它profile(如下图),则每个profile都应该设置一遍
触发器原理
添加触发器后,iTerm2会检测显示的每个字符串(这个字符串可能是你手动输入的、手动粘贴的、命令输出的),当检测到字符串刚好匹配上前面触发器中填写正则,则对应的命令就会被执行。
我们来看一下,当我们要上传时,在服务器运行trz
(或trz -d
用于上传文件夹),它会输出一串字符串,而下载文件时,则是在服务器上运行tsz filename
,它也会输出一串字符串。
总之,服务器运行trz
, trz -d
, tsz
三个命令,输出的字符串是不一样的,分别如下:
# trz
::TRZSZ:TRANSFER:R:1.1.0:8474374665700
# trz -d
::TRZSZ:TRANSFER:D:1.1.0:8474437510600
# tsz
::TRZSZ:TRANSFER:S:1.1.0:8474379056700
# regexp
:(:TRZSZ:TRANSFER:[SRD]:\d+\.\d+\.\d+:\d+)
我们注意正则(regexp)中的括号,正则中有括号表示捕获,这个捕获会被传到执行的命令中
/usr/local/bin/trzsz-iterm2 -p text -d '/Users/你Mac的用户名/Downloads' \1
可以看到触发器自动调用的命令为trzsz-iterm2
(它其实是python脚本,当然不是单文件的),最后一个参数\1
就是正则捕获的结果。
也就是说,trzsz-iterm2
命令是可以通过\1
传进来的正则捕获结果,来判断到底要做什么操作,其实主要区别就在“TRANSFER”后面的那个字母,它可能是R,D,S,分别表示Receive(对应trz
), Directory(对应trz -d
)以及Send(对应tsz
)。
当为R或D时:说明服务器处于接收状态,则本地应该给它发送文件(换种说法就是应该“上传”文件),所以trzsz-iterm2
就会弹窗让你选择要上传什么文件,选择完,trzsz-iterm2
就会调用tsz
,把你选择的文件通过stdin发送到服务器,服务器的trz
或trz -d
就会接收Mac端用tsz
发送过来的文件。
当然它这个trz -d
你也可以不选择文件夹,所以它是兼容文件和文件夹的,个人认为这个-d
不应该存在,以减少操作复杂度,你也可以自己添加一个alias trz="trz -d"
来始终使用trz -d
,这样就不用考虑上传文件还是文件夹了。
当为S时:说明服务器处于发送状态,则本地应该接收服务器发送过来的文件,所以iTerm2调用trzsz-iterm2
,trzsz-iterm2
调用trz
来接收服务器传过来的文件或文件夹。。。等等,如果传过来的是文件夹,不是应该用trz -d
来接收吗?但其实服务器端tsz
和tsz -d
输出的字符串并没有区分是发送文件还是文件夹,所以我猜测本地始终是用trz -d
来接收的,这样无论服务器发送来的是文件还是文件夹都能正确的接收。
-p text
:p是progress,进度条,text
表示使用文本进度条(直接在终端显示进度),当然还可以是-p zenity
,表示zenity进度条(弹出一个框显示上传进度),默认就是-p zenity
,所以如果你要用zenity进度条,可以不写这个选项(但要安装zenity:brew install ncruces/tap/zenity
)。
-d '/Users/你Mac的用户名/Downloads'
这个如果你不填,则下载时,会弹窗让你选择要把文件保存到哪儿,而填了,它会直接保存到你填的目录,这样更方便,不需要每次都选保存到哪儿。
启用拖放上传
拖放上传主要是使用了iTerm2的拖放事件,iTerm2支持拖放后,自动调用你指定的脚本,iTerm2版本必须在iTerm2-3_5_20230503-nightly.zip以上才有该功能。
打开:iTerm2→Preferences→Advanced→搜索 “dropp”
把命令粘贴到上图文本框中(注意最后不要多复制回车)
/usr/local/bin/trzsz-iterm2 -p text dragfiles \(filenames)
拖放上传的原理:拖放文件/文件夹后,自动在终端中输入并运行trz
,如果拖的是文件夹就自动输入并运行trz -d
,到了这里,就跟手动上传类似了,只不过由于你已经拖了文件(夹)进去,它已经知道你要上传的文件(夹)路径了,所以不会再弹窗要求你选择文件了。
开始使用
在macOS上使用
注:本方法只适用于你安装的是trzsz(brew install trzsz
),如果你在macOS上安装的是trzsz-go(brew install trzsz-go
),那么使用方法请参考:在Linux或Windows上使用。
首先登录到服务器
ssh user@xx.xx.xx.xx
然后在服务器上操作(无论上传还是下载的命令,都是在服务器操作,本地不用操作):
- 上传文件:上传文件就是服务器接收文件,所以服务器要运行
trz
(r表示receive)或trz -d
(用于接收文件夹),但trz -d
也可接收文件,所以你可以统一用trz -d
(或者干脆设置个alias trz="trz -d"
); - 下载文件:下载文件就是服务器发送文件(客户端接收),所以服务器要运行
tsz filename
(或tsz -d foldername
),但tsz -d
不仅能发送文件夹,也可以发送文件,所以你可以设置个alias tsz="tsz -d"
。
上传文件更简单的方式:直接把要上传的文件或文件夹拖到已连接服务器的终端窗口上,即会自动上传。
在Linux或Windows上使用
如果本地电脑是Linux(如Ubuntu)或Windows,则什么都不用设置,直接用以下命令登录服务器即可(即在传统的ssh登录命令前面加个trzsz
)
trzsz -d ssh user@x.x.x.x
登录服务器后,在已登录的服务器终端窗口输入trz
,回车运行,即会弹出窗口让你选择要上传的文件,用trz -d
可以允许上传文件夹(当然也能文件,所以也可以alias trz=trz -d
)。
trzsz -d
中的“d”是dropfile,所以它是指允许你拖放文件,而不是上传目录,所以你可以设置alias ssh="trzsz -d ssh"
来避免每次都要在ssh前面手写trzsz -d
,但是这种方式无法被iTerm2的Profile中的配置读取,所以如果是在iTerm2的profile中,就需要自己在ssh前面加上/usr/local/bin/trzsz -d
(注意trzsz
要写全路径),如下图所示
原理:这种使用方式为什么也能通过在服务器终端输入trz
,客户端就弹窗让你选择文件呢?因为在ssh前面有个trzsz,trzsz接管了ssh的数据,所以它能识别到服务器端运行trz
后输出的那个字符串,然后它自然就能根据这个字符串判断你是要上传文件,于是就去客户端调用相应的tsz
命令来发送你选择的文件了。
报错调试
如果遇到传输报错,先使用以下命令登录服务器(-t
是tracelog的意思)
trzsz -t ssh user@x.x.x.x
-t, –tracelog eanble trace log for debugging
然后在服务器上执行以下命令用于启用日志追踪(它会输出路径告诉你在哪儿)
echo -e '<ENABLE_TRZSZ_TRACE_LOG\x3E'
特别注意:以上命令输出如下所示,虽然它是在服务器端输出的,但该路径实际上是你本地电脑上的路径而不是服务器上的路径,所以如果你在服务器上找,找半天也是找不到的
Writing trace log to /var/folders/b8/m8lcy7wx235d02cwqsjphzjm0000gn/T/trzsz_2318401492.log
其实回想一下前面说过的trzsz的传输原理,这也没什么神奇的,毕竟trzsz
已经接管了你的ssh流量,它只不过是把本地电脑的日志路径通过stdin发送到服务器上在服务器上显示出来给你看而已,并不是说它个路径就是服务器的路径。
然后开始上传文件进行测试,上传完成后,就可以在本地看到这个文件路径了
可以看到日志文件中有[svrout]
(服务器输出)和[stdin]
(客户端输入),其中的内容看上去是base64 encode过的,decode出来就是那种看上去乱码的内容,其实就是你直接cat
一个非文本文件输出的内容(看上去就是乱码)。
测试完之后,记得把日志追踪给禁用回去
echo -e '<DISABLE_TRZSZ_TRACE_LOG\x3E'
禁用后也会输出提示
Closed trace log at /var/folders/b8/m8lcy7wx235d02cwqsjphzjm0000gn/T/trzsz_2318401492.log
得到日志后,你可以复制出来,然后在对应项目下提issue:trzsz项目,也可以发邮件到作者邮箱:lonnywong@qq.com。
