说实话,我第一次在Linux里用lpr打印文件的时候,是被同事“教育”过的——他在终端敲了一行命令,办公室那台老打印机就吐出了一份报表,而我还在翻菜单找打印按钮。这让我意识到,Linux下打印的便利性从来不在图形界面里,而在命令行。lpr就是那个从命令行提交文件到打印机打印的命令,别小看它,至今在服务器、脚本和自动化场景里,它依然是很多打印任务的第一入口。
这篇文章就把lpr怎么用、有哪些实用选项、怎么配合打印队列管理、以及我实际踩过的各种坑,完整梳理一遍。适合系统管理员、运维工程师,以及任何需要在无图形界面环境里处理打印任务的人。无论你用的是Ubuntu、Debian、CentOS还是macOS,只要底子是Unix/Linux,lpr这套逻辑基本通用。
1. 为什么现在还值得学lpr:从管道打印说起
1.1 一个服务器上的真实场景
我最早大量使用lpr,是在一台没有桌面环境的服务器上。当时的需求很朴素:每周要交一份网络设备巡检记录,但机器上既没有WPS也没有图形化打印管理,唯一能把结果变成纸质文档的方式,就是把巡检脚本的最终输出直接送进打印机。
当时我遇到的场景是这样的:巡检脚本跑完之后,会生成一个/tmp/check_result.txt,里面是设备状态、端口状态、链路流量之类的文本摘要。我需要把这个文件打印出来归档。在没有lpr之前,我得把文件下载到本地电脑,再打开、再选打印机、再点打印——来回折腾五分钟。而用lpr的话:
bash复制lpr /tmp/check_result.txt
一行命令,打印机直接把文件打出来了。后来我甚至把这一步直接写进了巡检脚本末尾,跑完自动打印,连手动操作都省了。
1.2 lpr到底做了什么:提交、队列、后台的三角关系
要理解lpr,得先理解Unix打印系统的基本模型。打印不是“命令直接把数据发给打印机硬件”,而是有一条完整的链路:
- 提交:lpr把文件或标准输入数据提交给打印队列。
- 排队:打印系统后台守护进程(如CUPS的cupsd)把任务挂到对应打印机的队列里。
- 产出:后台进程按顺序处理任务,调用过滤器、驱动,最终把数据发到打印机物理设备。
所以lpr的核心动作只有一个:把数据“扔”给打印系统。至于数据怎么变成纸上的内容,那是过滤器、驱动和后台守护进程的事,lpr本身不关心。理解了这个模型,你就能明白为什么lpr有很多-o选项——因为队列里的后续环节需要知道“用什么纸、打几份、横打还是竖打”。
1.3 到底适合谁来学
lpr的适用人群,明显比图形界面打印工具更窄,但场景更硬核:
- 服务器管理员:机器没有图形界面,只能通过终端管理打印。
- 脚本自动化场景:报表生成、日志归档、批量打印,需要把打印动作嵌入到Shell脚本或定时任务里。
- 远程运维:通过SSH连到远程主机,想用远程打印机输出文档。
- 日常习惯命令行的人:一条
lpr能解决的事,就不想打开文件管理器。
哪怕你平时主要用图形界面,我也建议掌握lpr的基本用法,因为在排查打印问题时,命令行能给你的信息远比“打印机无响应”这个弹窗多得多。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. lpr的基础操作:文件、管道与打印机选择
2.1 最直接的用法:lpr加文件名
最基础也是最常见的用法,就是把文件路径直接跟在lpr后面:
bash复制lpr report.txt
lpr /home/user/document.pdf
lpr /path/to/photo.jpg
一次提交多个文件也是支持的,命令会把这些文件按顺序当成同一个打印任务处理:
bash复制lpr file1.txt file2.txt file3.txt
这里的文件类型没有硬性限制。文本文件、PDF、PostScript、图片都可以,但能不能正确打印,取决于系统里有没有对应的过滤组件。比如打印PDF依赖cups-filters和poppler-utils,如果缺了,打印机出来的可能就是一堆乱码或者空白页。这个坑我后面专门讲。
还有一个小细节:文件名里有中文时,大部分情况能正常工作,但在一些老旧的打印队列配置下可能会出现处理异常。我的习惯是先把它改成英文文件名再打印,省得排查问题的时候多个变量。
命令执行完没有任何输出,通常代表提交成功。你可以紧接着用lpstat查看队列状态来确认:
bash复制lpstat -p -d
2.2 管道打印:命令输出直接上纸
lpr最有魅力的地方,是它能从标准输入读取数据。这意味着任何命令的输出,都可以直接变成打印内容,不需要先落盘再打印。这是命令行打印的核心竞争力,图形界面反而做不到这么顺滑。
来看几个例子:
bash复制cat /etc/hosts | lpr
ps aux | lpr
echo "这是测试页" | lpr
这背后是Unix管道机制在起作用。你写完cat /etc/hosts | lpr之后,cat的输出不会显示在屏幕上,而是作为标准输入被lpr接收,lpr再把它送进打印队列。
我记得有一次领导临时要一份php配置文件的纸质版用于评审,我直接敲了:
bash复制cat /etc/php/8.1/cli/php.ini | lpr -P office-printer
十秒钟后文件就在打印机上了,旁边同事一脸“你做了什么”的表情。这个工作流最适合日志、报表、配置文件这类纯文本内容,即打即走,不用生成临时文件。
2.3 多打印机环境下的-P选择
如果你所在的网络环境只有一台打印机,那不带任何选项敲lpr file就够了。但如果办公室里有多台设备(比如一台激光打印机、一台喷墨打印机、一台针式打印机),就必须让lpr知道你想用哪一台。
先用这个命令看看当前系统认到了哪些打印机:
bash复制lpstat -p -d
输出会类似:
code复制printer laserjet is idle. enabled since ...
printer inkjet is idle. enabled since ...
system default destination: laserjet
system default destination这一行,表示当前默认打印机是laserjet。不带-P参数时,lpr就默认往这个队列提交。
指定打印机则用-P参数:
bash复制lpr -P inkjet report.txt
这里有个容易卡住新手的细节:如果你在打印服务器上配置了很多打印机,-P后面的名字不是打印机的IP或主机名,而是打印系统里注册的队列名(通常就是lpstat -p输出里那个名字)。换句话说,-P参数对应的是“逻辑打印机名”,不是“物理设备IP”。
如果你没有设置默认打印机,直接敲lpr file,通常会遇到这样的报错:
code复制lpr: Error - no default destination available.
这说明系统不知道把任务交给谁。要么显式指定-P,要么设置一个默认打印机:
bash复制lpadmin -d laserjet
设置之后再运行lpstat -d,就能看到默认打印机已经变更。日常使用中,我建议哪怕只有一台打印机,也显式加-P,一方面习惯养成了不容易出错,另一方面在脚本里也方便后续扩展。
3. 容易被忽略的lpr选项:份数、横竖、纸张、双面
3.1 份数用 -# 而不是重复提交
打印两份以上,不用在命令行里写两遍文件名,直接加个次数参数就行:
bash复制lpr -# 3 meeting_slides.pdf
这会把同一个任务提交3份到队列。在CUPS环境里,等价的写法是:
bash复制lpr -o copies=3 meeting_slides.pdf
两种写法效果基本一样,但-#的兼容性更好一些,尤其在老的BSD风格打印系统里,-#是标准选项。需要注意:有些打印机驱动会在驱动层拦截“份数”设置,导致你在打印任务属性里看到的份数和实际打出来的不符。如果遇到这种情况,检查一下驱动设置里是否也设置了份数,两边不要叠加。
3.2 横向打印与旋转方向
想把一份默认纵向的内容变成横向打印,用-o landscape:
bash复制lpr -o landscape annual_report.txt
如果打印机驱动不识别landscape这个关键字,可以改用更底层的旋转参数:
bash复制lpr -o orientation-requested=6 file.txt
这里6代表横向旋转90度。这个参数属于CUPS的标准选项,多数现代驱动都能识别。不同的值代表不同的旋转方向,常用的有:
3:180度旋转4:纵向5:顺时针90度6:逆时针90度(通常用于横向打印)
实际场景里,横向打印最常见的是表格、宽页面报表和PPT打印。我印象最深的一次,是部门年终数据表列数太多,纵向打印字挤成一团,改成横向后瞬间清爽。
3.3 纸张大小设置
纸张规格是另一个高频选项。默认纸张可能跟随打印机驱动设置,可能是A4也可能是Letter,如果你需要明确指定,用-o media:
bash复制lpr -o media=A4 report.txt
lpr -o media=Letter report.txt
lpr -o media=A5 flyer.txt
常见规格就是A4、A5、Letter、Legal这些。命名格式如果记不住,可以用lpoptions查看指定打印机支持的媒体类型:
bash复制lpoptions -p laserjet -l | grep media
系统会把支持的列表打出来,类似media: A4 Letter Legal ...。这里有一个容易翻车的点:如果你设置的纸张和打印机纸盒里的实际纸张不一致,打印机会经常弹出“缺纸”或者“纸张不匹配”的提示,卡住队列。办公场景里最有效的做法,是在打印服务器层面把默认纸张固定成办公室常用规格(比如A4),而不是每次敲参数。
3.4 双面打印、页码范围与奇偶页
双面打印在办公室几乎是刚需。CUPS下用-o sides控制:
bash复制lpr -o sides=two-sided-long-edge report.pdf
两个常用取值:
two-sided-long-edge:沿长边翻转,适合A4纵向文档,翻页像翻书。two-sided-short-edge:沿短边翻转,适合横向内容的双面打印。
如果打印机不支持自动双面,这个选项会被忽略,效果可能变成单面打印,或者干脆打印出乱码。遇到这种情况,最好手动设置驱动,开启“双面单元”等硬件配置。
页码范围也可以直接控制:
bash复制lpr -o page-ranges=1-10 report.pdf
只打印第1到第10页。更有意思的是结合奇偶页用:
bash复制lpr -o page-set=odd report.pdf
lpr -o page-set=even report.pdf
这个技巧在手动双面打印时特别有用:第一次打印奇数页,第二次把纸张翻过来再打偶数页。很多老式打印机没有自动双面模块,我当年就是这么帮同事完成双面打印的。
另外,-o outputorder=reverse可以逆序输出,适合那些出纸时面朝上、希望打完就是正确顺序的打印机——做手动双面打印时,这个选项搭配奇数/偶数页有奇效。
到这里,列一张lpr常用选项速查表,方便对照:
| 选项 | 作用 | 示例 |
|---|---|---|
-P 打印机名 |
指定打印机队列 | lpr -P laserjet report.txt |
-# 份数 |
打印份数 | lpr -# 2 file.txt |
-o media=A4 |
设置纸张规格 | lpr -o media=A4 file.txt |
-o landscape |
横向打印 | lpr -o landscape report.txt |
-o orientation-requested=6 |
逆时针旋转90度 | lpr -o orientation-requested=6 file.txt |
-o sides=two-sided-long-edge |
长边双面打印 | lpr -o sides=two-sided-long-edge doc.pdf |
-o page-ranges=1-10 |
打印指定页码范围 | lpr -o page-ranges=1-10 doc.pdf |
-o page-set=odd |
只打奇数页 | lpr -o page-set=odd doc.pdf |
-o outputorder=reverse |
逆序输出 | lpr -o outputorder=reverse doc.pdf |
4. lpr与打印系统底层的关系:/usr/bin/lpr到底是谁
4.1 历史上的双轨:BSD lpr与System V lp
Unix打印命令有两条历史脉络。BSD系统用的是lpr,System V系统用的是lp。由于Linux早期同时继承了两边的设计习惯,很多发行版上这两个命令都是存在的,而且都向用户提供。
两者的功能基本一样:提交打印任务。只是参数风格不同:
- BSD的
lpr用-P printer指定打印机。 - System V的
lp用-d printer指定打印机。
在老的Unix世界里,它们背后对应不同的守护进程和不同的队列格式。如果你当年接手过Solaris机器,会发现lp那边能设置的选项,lpr这边可能完全没有;反过来也一样。
4.2 CUPS把两个命令都保留了下来
现在Linux发行版上广泛使用的是CUPS(Common Unix Printing System)。CUPS是一个现代打印系统,采用IPP协议与客户端通信,它同时提供了lp和lpr两个客户端命令,目的就是兼容老用户的习惯。
所以在今天的Ubuntu或CentOS上,lpr其实不是曾经那个BSD LPD客户端,而是CUPS的兼容层命令。它做的事情和lp几乎一样,都是把打印任务通过IPP协议交给本机的cupsd守护进程,再由cupsd和打印机通信。
这也意味着: 如果CUPS服务没有运行,lpr是会失败的。报错多半是“Unable to connect to cups server”之类。所以排查lpr问题时,第一件事不是查打印机网络,而是确认cups服务是活的:
bash复制systemctl status cups
如果服务没起来,不用研究参数,先启动它:
bash复制sudo systemctl start cups
4.3 拆看自己的lpr到底指向哪里
想确认你系统里的lpr到底是“谁是本尊”,用这几条命令拆开看:
bash复制which lpr
ls -l $(which lpr)
readlink -f $(which lpr)
在多数发行版上,输出会显示lpr实际上是一个指向lpr-cups或cups相关二进制文件的符号链接。我见过一些朋友看到/usr/bin/lpr -> /usr/bin/lpr-cups之后有点懵,其实这就是CUPS提供的兼容客户端。
顺带说一句,macOS也是走CUPS的,所以macOS终端里的lpr用法和Linux基本一致。很多Mac用户不知道这一点,其实man lpr里提到的选项,在macOS上同样可用。
4.4 后台链路:客户端、守护进程、过滤器
lpr提交任务之后,数据流的路径大致是:
- lpr通过IPP协议把任务信息发送到
localhost:631上的cupsd。 - cupsd把任务放入对应打印机的队列,分配一个job ID。
- 轮到该任务时,cupsd根据文件类型选择合适的过滤器链。
- 过滤器把源文件转换成打印机可接受的数据格式。
- 后端模块把最终数据送到打印机(USB网络本地端口等)。
理解这条链路,对排查问题非常有用。比如你打印PDF出来空白,你就能明白问题大概率出在第3步的过滤器环节,而不是lpr本身。后面我在避坑章节会展开。
5. 打印任务管理与取消:lpq/lprm/lpstat的组合用法
5.1 提交之后怎么看任务状态
lpr提交任务后没有反馈信息,很多人心里没底。这时候用lpq看一眼队列:
bash复制lpq
输出会列出队列里的任务、job ID、所有者、文件大小和状态:
code复制Rank Owner Job File(s) Total Size
1st zhangsan 42 report.txt 1024 bytes
2nd zhangsan 43 annual_report.pdf 204800 bytes
如果耐心不够,加上-P参数只查某台打印机:
bash复制lpq -P laserjet
还有一个更详细的状态命令lpstat:
bash复制lpstat -p -d # 查看打印机状态和默认设备
lpstat -a # 查看所有打印队列是否接受任务
lpstat -t # 查看整体打印系统状况
这里我自己的习惯是:任务提交完,先敲lpq确认已经到了队列里;如果lpq里什么都没显示,再敲lpstat -t看系统级状态。这个顺序能快速定位问题是在“提交”环节还是“队列接纳”环节。
5.2 取消任务:lprm和cancel
手滑提交错了文件,或者想取消一个排了很久的大任务,用lprm:
bash复制lprm 42
这里的42就是lpq里看到的Job号。也可以指定打印机和Job号,更精确:
bash复制lprm -P laserjet 42
在CUPS环境里,等价的取消命令是cancel:
bash复制cancel laserjet-42
注意CUPS的job ID格式通常是“打印机名-数字”,比如laserjet-42。如果任务正在打印中,取消命令通常也能终止,只不过打印机可能已经吐了一半纸,这属于物理无法挽回的部分。
5.3 取消失败时怎么办
lprm并不是每次都顺利。我遇到过的典型情况有:
- 任务状态已经是completed:任务已经打完了,自然删不掉。这种情况是正常的,不用纠结。
- 权限不足:取消其他用户提交的任务时,普通用户可能没有权限。要么找管理员,要么用sudo执行。
- 队列卡住:任务一直显示
processing但实际没动作,cancel也删不掉。这通常是后台过滤器或驱动挂了。解决思路是重启CUPS服务:
bash复制sudo systemctl restart cups
重启后打印机会重新初始化队列,卡住的任务一般会被清掉。如果重启后队列还是一直挂起,再查/var/log/cups/error_log定位具体驱动问题。
5.4 队列暂停与恢复:临时hold住所有任务
还有一种更精细的操作:不是取消任务,而是暂停整个打印机的队列。比如某台打印机卡纸了,需要先处理硬件问题,但不想删除已经排队的任务,可以:
bash复制cupsdisable laserjet
等处理完卡纸,再恢复队列:
bash复制cupsenable laserjet
队列暂停期间,新提交的任务会正常进入队列、正常排队,只是不会实际发送到打印机。这比我以前用的老办法——把所有排队任务删光——要友好得多。打印机故障总会有,但没必要让整个队列跟着陪葬。
6. 实战避坑:中文乱码、PDF打印、远程打印机的处理
6.1 中文乱码:最经典的编码问题
新手用lpr打印中文文本时,最容易撞到一面墙:打印出来的汉字全是方块、问号,或者完全错乱的东西。这种情况十有八九是文件编码与打印系统期望的编码不一致。
CUPS在处理纯文本文件时,会按系统locale默认编码去解码。如果你的Linux系统locale是UTF-8,而文件是GBK/GB2312编码的(比如从Windows机器上拷贝过来的txt),打印系统解码失败,输出自然就是乱码。
解决办法是先用iconv把编码转换成UTF-8,再交给lpr:
bash复制iconv -f GBK -t UTF-8 old_report.txt | lpr
这里-f指定源编码,-t指定目标编码。如果你的文件本身是GB18030(新版Windows中文txt常见编码),同样处理:
bash复制iconv -f GB18030 -t UTF-8 chinese.txt | lpr
不确认原文件编码时,可以用file命令查看:
bash复制file -i report.txt
输出里会带charset=us-ascii或charset=utf-8或charset=iso-8859-1之类的信息,一目了然。
另外,如果你的文件内容既有中文又有格式排版需求,纯文本过滤器往往搞不定。我建议用enscript或a2ps先转成PostScript再打印,效果会好很多:
bash复制enscript -p - -B -f Courier10 report.txt | lpr
这会把文本排版成PostScript格式,中文的显示依赖字体是否安装。如果打印出来的中文变成空白而不是乱码,通常是系统缺少中文字体,安装fonts-noto-cjk这类字体包即可。
6.2 打印PDF出现空白或乱码
另一个高频问题:lpr xxx.pdf提交后,打印机输出的不是文档内容,而是空白页或者一堆看不懂的符号。
这个问题的根源在过滤器链。CUPS打印PDF时,需要经过pdftopdf、pdftops或pdftocairo等过滤器,把PDF转换成打印机支持的数据格式。如果系统缺少这些过滤组件,cupsd就无法正确解析PDF,甚至会把PDF当成文本直接发给打印机——结果就是乱码。
解决方法是安装CUPS的过滤器和PDF处理工具。Debian/Ubuntu上:
bash复制sudo apt install cups-filters poppler-utils
CentOS/RHEL上:
bash复制sudo yum install cups-filters poppler-utils
装完之后重启CUPS:
bash复制sudo systemctl restart cups
再打一次PDF试一下。如果在提交PDF后想确认过滤器工作是否正常,可以提交任务后立即跟踪错误日志:
bash复制sudo tail -f /var/log/cups/error_log
日志里如果出现Filter "pdftopdf" not found之类的提示,基本就能断定是缺过滤器,补装即可。
6.3 远程打印机的指定方式
服务器上没有本地打印机,但办公室有共享打印机,这可能是lpr最实用的场景之一了。远程打印机有两种常见接入方式。
第一种,是对方已经通过CUPS共享了一台打印机。你在本机添加远程队列:
bash复制lpadmin -p remote-print -E -v ipp://print-server-ip/printers/laserjet
添加之后,直接使用:
bash复制lpr -P remote-print report.txt
第二种,是传统Unix/LPD环境。某些老打印服务器只提供LPR协议(端口515),这种情况下CUPS也支持。添加方式:
bash复制lpadmin -p old-lpd -E -v lpd://print-server-ip/lp1
lp1是LPD服务器上暴露的队列名。这两种方式加出来的远程打印机,对lpr来说和本地打印机没有区别,-P指定队列名即可。
需要注意的是,远程打印最容易出现的坑是打印服务器防火墙没放行。CUPS使用的IPP端口是631,传统LPD端口是515。如果你连接超时或者“connection refused”,优先检查这两个端口。我自己排查远程打印机问题时,经常先用nc测试端口连通性:
bash复制nc -vz print-server-ip 631
比反复调参数快得多。
6.4 查日志定位打印问题,别乱猜
lpr打印失败时,系统不会像图形界面那样弹窗告诉你发生了什么。但CUPS的日志其实已经把原因写得明明白白。
主日志位置:
bash复制/var/log/cups/error_log
用tail跟踪最新输出:
bash复制sudo tail -f /var/log/cups/error_log
日志默认记录的是error级别。想看更详细的调试日志,可以临时调整日志级别:
bash复制sudo lpadmin -p laserjet -o log-level=debug
但要注意,debug级别日志量很大,不建议长期开启。排查完问题后恢复默认:
bash复制sudo lpadmin -p laserjet -o log-level=error
我见过不少同事遇到打印问题第一反应是重装驱动,其实先看一眼日志,往往一分钟就能定位是过滤组件缺失、设备路径错误还是权限问题。命令行工具的好处就是信息透明,千万别浪费这个优势。
最后再分享一个小技巧:在bash里给lpr设置一个别名,把默认打印机和常用纸张带上,能省掉很多重复输入:
bash复制alias myprint='lpr -P laserjet -o media=A4'
写完这行加到~/.bashrc里,以后打印只要myprint somefile.pdf就行了。命令行打印这东西,熟练之后真的比图形界面顺手得多,尤其是和其他命令组合成管道一条龙的时候,那种“一个命令直接从数据到纸张”的爽快感,用过一次就回不去了。
