
UUCP 这套东西在 Linux 上属于活化石级别命令还躺在/usr/bin里man 手册也还能翻出来但真让人说清楚 Linux uuto 命令和 uucp 到底差在哪十个人里九个会卡壳。它要解决的问题其实特别朴素——把本机的文件传送到远端的 UUCP 主机上某个用户手里而且不需要你知道对方家目录长什么样、权限怎么配、文件该落到哪个路径。早年没有 ssh、没有 scp、没有 rsync 的年代跨机交换文件基本靠 UUCP 这一套uuto 就是其中最面向人的那条命令。今天还会碰它的人大致三类维护老工业设备或者老 Unix 小型机的运维、啃 Linux 常用命令大全和面试题的备考党、以及想搞明白邮件和新闻组时代文件是怎么在机器之间跑的这种底层问题的技术爱好者。这篇就把 uuto 从历史定位、环境准备、语法细节、完整的收发流程一直到踩坑排查按我实际折腾一遍的顺序讲透能照着复现。1. uuto 到底是什么从 UUCP 的历史到今天的定位UUCP 全称 Unix-to-Unix Copy最早是贝尔实验室在上世纪七十年代末搞出来的一套机器间通信机制它不只是传文件还能在远端执行命令。整个套件里uucp负责复制、uux负责远端执行、uucico是真正的传输守护、uuxqt负责在远端执行收到的请求而uuto和uupick是一对专门服务于我发给你这个场景。理解这一点非常关键uuto 不是一条独立的传输协议实现它本质上是 uucp 的外壳帮你把文件放到远端的公共接收区剩下的交给对方用 uupick 自己取。1.1 一句话说清 uuto 和 uucp 的区别uucp的目标是一个路径写法是uucp 文件 远端主机!远端路径你得知道对方机器上目录在哪、有没有写权限、uucp 用户能不能以你的身份写进去。uuto的目标是一个人写法是uuto 文件 远端主机!用户名你不需要知道路径远端那一侧由 uupick 来完成最后一步落盘。举个生活化的例子uucp 像是你自己上门把包裹塞进对方家门口得先摸清门牌号还得保证门没锁uuto 像是寄到小区快递柜收件人收到通知后自己挑时间取件双方都省心。这也是为什么很多人第一次看到 uuto 会觉得这命令怎么这么简单——因为它把复杂度推给了接收端。1.2 为什么设计成投递到用户而不是直接投递到路径这个设计选择背后是七十年代末的现实约束。当年机器之间是通过电话拨号加串口连接的链路慢、费用高、随时可能断而且不同站点归属于不同组织权限管理非常粗糙。如果允许任意站点直接往别人家目录写文件就意味着 uucp 这个运行账号必须在远端具备 impersonation 能力能随意切换用户身份这在多租户或者半开放的环境里完全不可接受。所以 uuto 的做法是两段式投递先把文件放到一个所有站点都能写、但权限被严格限制的公共目录通常是/var/spool/uucppublic/receive/用户名/来源主机/接收方再用 uupick 从公共区把文件搬进自己的目录。这样一来发送端能做的只有往信箱里塞信接收端自己决定什么时候、以什么身份、把信拿到哪儿去责任边界非常清楚。1.3 uuto 今天还能在哪些场景派上用场说实话新建项目里用 uuto 是没道理的scp、rsync over ssh、sftp 哪一个都比它强。但有几类现实场景绕不开它一是老产线上的工控机或者嵌入式设备系统还是老 Unix 或者早年定制的 Linux上面只有 UUCP 套件运维得靠它把日志和报表传出来二是教学与考试环境Linux 常用命令大全、面试题库里偶尔会考到这套命令族你至少要知道它是干什么的、和 uucp 怎么区分三是技术考古想真正理解 Usenet 新闻组、UUCP 邮件网关那套东西是怎么运转的亲手跑一遍 uuto 比看十篇文章都管用。下面所有的实操我都会按两台 Linux 通过 TCP 互联来做毕竟拨号猫和串口线现在不好找了。2. 环境准备把 UUCP 套件装起来并跑通基本链路UUCP 在主流发行版仓库里几乎都还在包名通常就叫uucp内容来自 Taylor UUCP 这个维护得比较久的实现。GNU inetutils 里其实也带了一套 uucp 组件但发行版一般不会两套同时装避免二进制冲突。装之前建议先确认一下系统里有没有残留配置尤其是/etc/uucp/这个目录很多老镜像里是预置过的直接覆盖可能把原有站点定义搞丢。2.1 安装与版本确认按发行版选命令装完之后一定要确认二进制到底来自哪个实现因为不同实现的选项会有差异# Debian / Ubuntu 系 apt-get update apt-get install -y uucp # RHEL / CentOS 系可能需要先启用额外仓库 yum install -y uucp # openSUSE zypper install uucp # 装完先看看到底有哪些命令进来了 dpkg -L uucp | grep -E bin|sbin # Debian 系 rpm -ql uucp | grep -E bin|sbin # RHEL 系 # 确认版本与手册可用 uuto --version man uuto | head -40装包的过程本身会创建一些东西这些是后面能不能跑通的基础系统里会多出uucp用户和uucp用户组这个账号就是所有传输动作的运行身份会创建/var/spool/uucp/作为队列工作区还会创建/var/spool/uucppublic/作为公共收发区。有些发行版还会往 cron 里塞一条定时任务周期性地拉起uucico处理积压队列装完最好看一眼crontab -l -u uucp或者/etc/cron.d/uucp里有没有东西避免后面手工调试时和定时任务互相干扰。2.2 关键目录与账号先摸清家底上手之前花两分钟把这几个路径和账号搞清楚能省掉后面一大半的排查时间路径作用常见权限/etc/uucp/主配置目录Taylor UUCP 下含 config、sys、port、dial、passwd、call0644部分文件 0600/usr/lib/uucp/老版本配置位置部分实现仍然读这里0644/var/spool/uucp/队列与工作目录临时文件、状态、日志都在里面属主 uucp:uucp0775/var/spool/uucppublic/公共目录uuto 落文件的根通常 0777 或 1777/var/spool/uucp/.Log/分系统、分类型的日志目录属主 uucp:uucp/var/spool/uucp/.Status/每个远端站点的通信状态记录属主 uucp:uucp关于 uucp 这个账号有个细节值得说它的登录 shell 在不同发行版上可能是/usr/sbin/uucico、/bin/false或者/bin/bash。如果是uucico说明这台机器允许别人拨进来做被动接收如果是false那它纯粹是个服务账号只用于本地文件属主。你要做接收端测试的话得确认这个 shell 配置和你的场景匹配不然远端连上来会被直接踢掉。2.3 最小可用配置两台机器通过 TCP 互联拨号时代过去了现在最省事的做法是让 UUCP 走 TCP。Taylor UUCP 支持把 port 类型定义成tcp协议用t默认端口就是/etc/services里的uucp 540/tcp。下面这套配置我按发送端 mynode192.168.1.10向接收端 remote192.168.1.50投递来写两边都要配。发送端mynode的/etc/uucp/config# 本机在 UUCP 网络里的节点名必须全局唯一 nodename mynode # 队列与工作目录 spool /var/spool/uucp # 调试级别初期建议留 abnormal排查时临时调高 debug abnormal发送端mynode的/etc/uucp/sys描述怎么连 remotesystem remote # 对方要求我们提供的登录名和口令星号表示不限 call-login * call-password * # 允许通信的时间段any 表示全天 time any # 使用名为 tcp 的 port 定义 port tcp # 使用 t 协议适合 8 位干净链路 protocol t # 对方的地址 address 192.168.1.50发送端mynode的/etc/uucp/port定义一条 TCP 链路port tcp type tcp # 服务名或端口号对应 /etc/services 中的 uucp 540/tcp service 540接收端remote的/etc/uucp/config里把nodename改成remote然后在/etc/uucp/sys里加一段表示接受 mynode 的连接再配一份相同的 port 定义最后用监听模式把服务拉起来# 接收端启动监听-l 表示 listen前台运行便于观察 uucico -l # 只想临时跑一轮、处理完就退出可以加 -x 提高调试级别 uucico -l -x 5注意上面这些字段是基于 Taylor UUCP 的常见写法整理的示意配置不同实现、不同发行版打包时的默认值会有出入落盘前一定用man uucp、man uucico对照一遍字段名别直接复制到生产环境。两边配置确认没问题后先在发送端手工触发一次连接测试不要急着发文件。uucico -s remote -x 5会尝试主动呼叫 remote屏幕上会打印出完整的握手过程。看到类似call completelogin successful的日志再往下走否则后面所有 uuto 都会静静地失败你还以为是文件的问题。3. uuto 命令语法与参数逐条拆解uuto 的语法短得有点不真实但它背后牵扯的东西不少。先把格式记住再看每个参数为什么存在最后搞明白你的文件在本地 spool 里经历了什么。3.1 语法格式与地址写法uuto [选项] 源文件... 远端用户这里的远端用户写法有几种变形直接决定了文件会落到哪台机器的哪个收件箱主机名!用户名最常见的形式例如remote!zhangsan表示投递到 remote 这台机器上 zhangsan 的公共接收区。中继主机!目标主机!用户名UUCP 时代大量站点并非直连需要经过中间节点转发感叹号可以串联多级例如gateway!remote!zhangsan。中间每一跳都得在各自的sys文件里有定义否则队列会卡住一直重试。只写用户名不指定主机时命令会按当前节点配置去解析实际使用中很容易踩坑建议永远把主机名写全。几个真实可用的例子# 发送单个文件给 remote 主机上的 zhangsan uuto ./report.pdf remote!zhangsan # 一次发多个文件接收端会逐个出现在 uupick 列表里 uuto ./a.log ./b.log ./c.log remote!ops # 传输完成后给自己发一封邮件通知 uuto -m ./monthly.csv remote!zhangsan # 先把源文件复制到 spool 再排队源文件后续可以被安全删除 uuto -p ./snapshot.tar remote!backup3.2 常用选项与适用场景uuto 的选项不多但每一个都对应一个实际痛点用错了要么传不过去要么传过去之后对方一脸茫然。选项作用什么时候用-p传输前先把源文件复制到本地 spool 目录源文件放在会被清理的临时目录或者你希望发完立刻能删源文件-m传输完成后给发送方发邮件通知批量投递、无人值守场景靠邮件确认结果-n 用户在远端额外通知指定用户收件人平时不收信需要提醒另一个人去 uupick-x 级别设置调试输出级别数值越大越啰嗦排查连接、权限、路径类问题时临时打开关于-p有个常见误解值得说清楚很多人以为不加-p就是直接流式传输不落盘其实不是。uuto 无论如何都会在本地 spool 里建工作文件区别在于源文件本身是被复制一份进去还是被直接移动或者引用。加-p的好处是发送动作和源文件生命周期解耦尤其是在 cron 里跑的时候脚本后面就算执行了清理逻辑也不会影响已经在排队的任务。-m依赖本机的邮件系统。如果你的机器上没装 MTA或者邮件直接扔进黑洞那这个选项开了也白开通知会静默失败。测试环境里我一般直接看/var/spool/uucp/.Status/下的状态文件来判断成败比等邮件靠谱。3.3 本地 spool 里到底发生了什么执行完 uuto 之后文件并不是已经发出去了而是已经排进队列了。理解这一点是排查所有 UUCP 问题的前提。命令执行瞬间本地 spool 里会出现这些东西/var/spool/uucp/远端主机名/目录下多出一个C.开头的工作文件比如C.mynodeN0034。C代表 command里面记录的是要做什么包括源文件路径、目标主机、目标用户、执行哪些后续动作。如果用了-p同一目录下还会出现D.开头的文件D代表 data就是真正要传的内容。.Sequence文件里对应主机的序列号会加一用来保证文件名不冲突。.Status文件里会记录该站点的最近通信状态包括重试次数、上次成功时间。真正的传输发生在uucico运行起来之后。它读sys配置、建链路、把D.文件发过去然后在远端触发uuxqt执行随附的请求由远端把这批文件搬进receive目录。所以在发送端敲完 uuto 立刻去远端找文件大概率是找不到的得等一次uucico跑完。默认情况下 UUCP 不是常驻服务要么靠 cron 定时拉起要么你手工执行uucico -s remote推一把这一点和现代的文件传输工具差别很大第一次用的人经常在这里犯迷糊。4. 实操全流程从本地发送到远端 uupick 取件这一节把完整链路走一遍。我会把命令、现象、验证方式都写出来你可以照着在两台虚拟机之间复现。4.1 单文件与多文件发送先在发送端准备测试文件然后投递# 造两个测试文件 echo hello uucp $(date) /tmp/hello.txt dd if/dev/urandom of/tmp/blob.bin bs1k count64 2/dev/null # 投递单文件指定远端主机和用户 uuto /tmp/hello.txt remote!zhangsan # 投递多个文件一次排队 uuto /tmp/blob.bin /tmp/hello.txt remote!zhangsan # 观察本地队列确认工作文件已经生成 ls -l /var/spool/uucp/remote/执行后如果没有报错说明本地这一侧已经把任务排好了。接下来手工推一次传输uucico -s remote -x 5屏幕上的输出会显示连接、协商、传输、断开的过程。跑完之后到远端机器上用 zhangsan 这个账号执行uupick就能看到待取件的列表。如果远端还没有 zhangsan 这个系统账号uuto 也传得过去但 uupick 会因为找不到家目录而没法正常归档所以测试前先确认用户存在。4.2 通配符、目录与文件名陷阱这一块是新手最容易翻车的地方我自己也踩过好几次。通配符是 shell 先展开的不是 uuto 自己解析的。也就是说uuto *.log remote!ops在 shell 层面就变成了uuto a.log b.log c.log remote!ops如果当前目录下匹配到的文件特别多可能直接撞上命令行长度限制。更稳妥的做法是配合find或者分批处理。文件名里带空格或者特殊字符必须用引号包起来否则会被拆成多个参数最后一个参数还被当成远端用户名报出的错误信息会很莫名其妙。目录不能直接传。uuto 不是 tar给它一个目录路径它会直接拒绝。要传目录得自己先打包到了远端再解开。老实现对文件名长度有限制。System V 那一代的 UUCP 有 14 字符的文件名上限超出部分会被截断两个长名字前缀相同的文件就可能互相覆盖。Taylor UUCP 这一代放松了很多但目标端如果是老设备仍然可能被截断。最保险的办法是发送前把文件名改短比如用日期加序号的方式重新命名。非 ASCII 字符要小心。中文文件名能不能完整传过去取决于链路协议和两端实现。用protocol t走 TCP 时一般没问题但如果是老式的g协议且没开启 8 位支持中文名和内容都可能变成乱码——这跟很多人遇到的Linux 解压文件乱码是同一类问题根源都是编码协商没对齐。稳妥起见跨机器传文件时文件名统一用 ASCII内容里的编码问题在应用层解决。4.3 接收端 uupick 的完整操作接收端登录对应账号直接敲uupick。如果积压的文件来自多个站点可以用-s只看某一个来源# 处理所有站点的待收文件 uupick # 只处理来自 mynode 的文件 uupick -s mynode交互界面的典型样子是逐个文件询问你输入一个字母决定怎么处置from mynode: file hello.txt ?可用的交互命令大致如下具体以man uupick为准输入含义回车跳过当前文件看下一个s [目录]保存到指定目录不带目录则存到默认位置m [目录]移动到指定目录与保存的区别是源文件被移走d删除当前文件p把文件内容打印到终端适合看小文本q退出未处理的文件留在原地!命令执行一条 shell 命令比如!ls -l ~看一下当前目录*对剩余所有文件重复上一个命令批量保存时很好用日常最常用的组合是先用uupick扫一遍列表确认来源都可信然后s ~/inbox逐个保存如果一次来了几十个日志文件就在第一个文件上敲s ~/inbox再敲*剩下的全部按同样方式处理。提示uupick 默认的收件位置通常是用户家目录下的receive结构或者公共目录/var/spool/uucppublic/receive/用户名/。各家实现和打包配置不完全一样第一次用建议先在一个文件上敲s不带目录看它到底存到哪儿去了再决定要不要指定路径。4.4 用 uustat 观察队列与状态发送端和接收端都可以用uustat看队列。这条命令在日常运维里比 uuto 本身还常用# 列出所有站点的队列任务 uustat -a # 只看发往 remote 的任务 uustat -s remote # 查看各站点的队列摘要 uustat -q # 删除某个卡住的任务需要 jobid从上面输出里拿 uustat -k mynodeN0034 # 重新触发某个任务的重试 uustat -r mynodeN0034uustat -a的输出里会带上任务编号、状态、重试次数和最后更新时间。重试次数一直涨但状态不变基本可以断定是链路或者对端配置的问题而不是文件的问题。这时候去看/var/spool/uucp/.Status/remote里面记录的失败原因往往比屏幕上看到的详细得多。5. 常见故障与排查实录UUCP 最让人抓狂的地方在于它默认非常安静。命令返回 0你以为成功了其实任务正躺在队列里反复重试。下面这些是我实际遇到过、并且有明确解法的典型问题。5.1 发送不报错但远端一直收不到这个现象出现频率最高原因几乎都集中在uucico 没跑上。UUCP 的传输不是实时的uuto 只负责排队真正干活的是 uucico。如果你装完没配 cron、也没手工执行任务就会一直待在队列里。排查顺序建议这样走先看本地队列里有没有对应的工作文件ls -l /var/spool/uucp/remote/。如果C.文件在说明排队成功。接着看.Status里的重试次数和最后错误。再手工执行一次uucico -s remote -x 7把详细过程打出来。最后确认接收端的uucico -l是不是还活着防火墙有没有放行 540/tcp。这四步走完绝大多数神秘失踪都能定位到具体环节。5.2 权限与目录相关的报错UUCP 涉及的文件属主和权限比较多任何一个错了都会静默失败。几个高频点/var/spool/uucp/的属主必须是uucp:uucp。如果你手贱用 root 在里面创建过目录或者复制过文件队列就可能因为权限不一致而写不进去。/var/spool/uucppublic/需要让远端站点能写入通常是 0777 或者带 sticky 位的 1777权限收紧到 0755 就会出现能连上但传不进去。目标用户不存在也是常见原因。uuto 允许你发给一个远端不存在的用户名文件会被放到公共目录的某个人名目录下但那个用户登录后执行 uupick 时如果家目录结构对不上取件流程会失败。测试前先确认两端账号对齐能省不少事。还有一个容易被忽略的点.Log和.Status目录如果被清理脚本误删uucico 起不来或者起来就报错。有些发行版的打包脚本会在特定条件下重建这些目录但别指望它出问题了手工mkdir加chown uucp:uucp更直接。5.3 日志的三个入口按顺序看排查 UUCP 一定要养成看日志的习惯它的日志分散在几个地方各管一段日志位置内容什么时候看/var/spool/uucp/.Log/uucico/主机链路建立、登录、传输过程的细节连接失败、传输中断/var/spool/uucp/.Log/uucp/主机uucp 命令层面的记录排队阶段出问题/var/spool/uucp/.Log/uuxqt/主机远端请求执行的记录文件传过去了但没落到 receive 目录/var/log/uucp/Log部分老实现的集中日志找不到上面几个目录时来这里翻journalctl -u uucpsystemd 环境下的服务日志服务拉不起来看日志之前先把调试级别调高。uucico -x 9 -s remote会把每一步协商都打出来包括它尝试了哪个 port 定义、用了什么协议、对端回了什么。这类日志啰嗦是啰嗦但定位问题比猜快十倍。定位完之后记得把级别调回去不然日志会迅速把磁盘吃掉。5.4 常见问题速查表把上面这些整理成一张表出问题的时候按症状查症状可能原因处理方向uuto 成功但队列不减少uucico 未运行检查 cron 或手工执行uucico -s 主机连接超时地址错误、对端未监听、防火墙拦截核对sys里的 address确认uucico -l在跑放行 540/tcp登录被拒绝口令配置不匹配、uucp 账号 shell 被禁用检查sys中的 call-login 与对端passwd配置传输完成但远端没有文件远端uuxqt未执行查.Log/uuxqt/下的记录确认目录权限文件名被截断或覆盖老实现的文件名长度限制发送前重命名为短名避免前缀重复中文乱码协议不支持 8 位或编码不一致改用protocol t文件名统一 ASCII任务反复重试中间跳转节点配置缺失检查多级地址里每一跳的sys定义日志里提示磁盘满.Log或 spool 分区写满清理历史日志设置日志轮转6. 注意事项与实操心得这一节讲点手册上不会写、但实际折腾中最容易吃亏的东西。6.1 安全与信任模型的坑UUCP 的信任模型建立在一个现在完全不成立的前提上网络里所有主机都是可信的。它默认允许远端站点通过配置里的账号登录登录后能做的事情范围不小历史上也确实因为这种宽松设计出过不少安全问题。所以有两条底线第一绝对不要把 UUCP 端口暴露在公网哪怕只是测试第二如果只是为了在两台内网机器之间传文件用 scp 或者 rsync 就够了别为了复古特意去装 UUCP。这玩意儿现在更适合放在隔离的实验网段里玩。配置层面还有个小习惯值得养成能不用的功能就别开。远端执行请求也就是uux那套在很多场景下根本用不到能关就关。sys文件里的时间和权限限制也尽量收紧别一律time any虽然调试方便但也意味着任何时段都可能有人连上来。6.2 时钟、编码与那些不起眼的细节两台机器的系统时间要对齐。UUCP 会用时间戳判断任务是否超时、是否需要重试时间差太大可能出现刚发出去就被判定为过期或者永远不重试这种诡异现象。用 chrony 或者 systemd-timesyncd 同步一下比事后排查便宜得多。编码问题前面提过这里再强调一次文件名保持 ASCII内容编码在应用层处理。跨机器传输时不要指望两端对中文的处理完全一致很多老实现内部还是按字节流处理的遇到多字节字符就容易出问题。还有一点是关于 spool 分区的。UUCP 的队列、日志、临时文件都堆在/var/spool/uucp/下如果这个目录和根分区在一起一次大批量投递就可能把根分区撑满进而影响整个系统。做正式使用的话把/var/spool/uucp/单独挂一个分区或者至少限制一下日志大小是必要的。6.3 现代替代方案该怎么选如果你的目标只是把文件可靠地送到另一台机器的某个用户手里现在的选择比 uuto 好太多直说结论优先 ssh 体系。# 单文件或少量文件直接用 scp scp ./report.pdf zhangsan192.168.1.50:/home/zhangsan/ # 大量文件、需要断点续传和增量同步用 rsync over ssh rsync -avz --partial ./logs/ zhangsan192.168.1.50:/home/zhangsan/logs/ # 需要接收方主动拉取、且机器不能直接互连时用 ssh 反向隧道加 sftp # 或者干脆用经过认证的对象存储做中转这几条路都比 uuto 快、比 uuto 安全、比 uuto 好排查。uuto 唯一还值得学的地方是它那套投递到人、接收方自取的模型——理解了它你会更容易看懂早期邮件网关和新闻组转发为什么要设计成那个样子也能更好地理解今天那些排队式消息系统的设计取舍。最后分享一个我自己的使用习惯每次在任何一台机器上用 uuto 之前先跑一遍uustat -q和uucico -s 目标主机 -x 5的把链路确认一遍确定连接是通的再发文件。就这么一个动作能省掉至少一半文件发出去了但对方没收到的扯皮。链路不通的时候你发多少个文件都是在给 spool 目录囤垃圾。