为什么你还需要这个“古老”的技巧?
说实话,我也曾以为“自动打印”是十几年前的技术了。但当你真在办公室面对一抽屉PDF合同、上百份报表要打印,或者服务器定时需要把生成的日志文件自动打印归档时,Windows自带的那个打印对话框简直就是一场灾难——鼠标点到手酸,还要担心选错打印机、打错份数。
我见过太多行政同事下班前还在一台台点“打印”,心里默默祈祷别卡纸。其实,用一段简单的批处理脚本,配合Windows自带的工具或免费开源软件,你完全可以实现:把文件夹里所有PDF扔进去,一键全自动按顺序打印,甚至还能指定双面打印、指定特定打印机。
今天这篇教程,不整那些虚的,直接从最基础的原理讲起,一步步带你写出能真正干活儿的脚本。内容很干,但保证你能听懂,哪怕你是电脑小白。
第一部分:核心原理——让电脑“知道”怎么打印
在写任何代码之前,你得先明白一件事:Windows系统本身并不原生支持直接从命令行“静默打印”PDF文件。这是什么意思呢?就是你在CMD里直接敲print my.pdf,系统会告诉你“命令不可识别”。
为什么?因为PDF是一种复杂的容器格式,里面可能有矢量图、嵌入字体、扫描图片等等。普通的print命令只能处理纯文本文件(比如.txt、.doc)。要打印PDF,你必须借助一个“翻译官”——也就是能够解析PDF并发送给打印机的软件。
目前主流的方案有三种:
- 使用Ghostscript(开源免费,功能最强):这是技术圈最推崇的方案。Ghostscript是一个强大的PostScript和PDF处理工具,它可以直接把PDF转换成打印机能理解的PCL或PostScript语言,然后通过命令行直接发送到指定打印机。它的优点是免费、稳定、支持批量、支持参数控制(页边距、份数、双面等)。缺点是需要在系统里安装它。
- 使用Adobe Acrobat Reader的命令行接口:Adobe官方提供的
AcroRd32.exe有一个/t参数,可以指定打印机打印文件。但这个方法非常慢,而且每打印一个文件就会弹出Adobe的界面一闪而过,体验不太好,也不适合真正的无人值守自动化。 - 使用Python + PyPDF2/ReportLab:这是程序员的做法,灵活但需要环境配置。
对于大多数办公场景,我强烈推荐方案一:Ghostscript。它虽然听起来有点“极客”,但配置一次后,你的批处理脚本会简洁得像诗一样。而且,Ghostscript是开源的,完全免费,很多专业打印软件底层用的就是它。
第二部分:准备工作——安装Ghostscript
别被这个名字吓到,下载安装非常简单。
- 打开浏览器,访问Ghostscript官方网站(通常搜索“Ghostscript download”即可,认准Aladdin Enterprises或Artifex Software的官方源)。
- 下载对应你系统架构的版本(通常是64位Windows,下载
gs10xxw64.exe或类似名称)。 - 运行安装程序,注意:安装过程中有一个步骤会问你“是否添加到PATH环境变量”,一定要勾选“是”或者手动记住安装路径(默认通常是
C:\Program Files\gs\gs10.xx\bin\gswin64c.exe)。 - 安装完成后,打开CMD,输入
gswin64c -v,如果能输出版本号,说明安装成功。
小贴士:如果你担心PATH配置麻烦,也可以直接记住安装目录下的
gswin64c.exe完整路径,在脚本里用全路径调用,这样最稳妥。
第三部分:基础版脚本——单文件打印测试
在写批量脚本之前,我们先写一个最简单的测试脚本,确保你的Ghostscript和打印机沟通正常。
新建一个文本文件,命名为test_print.bat,用记事本打开,输入以下内容:
@echo off
setlocal enabledelayedexpansion
:: 定义Ghostscript的路径,请根据实际安装情况修改
set GS_PATH=C:\Program Files\gs\gs10.xx\bin\gswin64c.exe
:: 定义要打印的PDF文件路径(可以是相对路径或绝对路径)
set PDF_FILE=D:\Documents\report.pdf
:: 定义目标打印机名称(必须是Windows系统中已安装的打印机名称,完全一致)
set PRINTER_NAME=HP LaserJet Pro M404
echo 正在打印:%PDF_FILE%
echo 打印机:%PRINTER_NAME%
:: 调用Ghostscript打印
"%GS_PATH%" -q -dNOPAUSE -dBATCH -sDEVICE=mswinpr2 -sOutputFile="\\%PRINTER_NAME%" "%PDF_FILE%"
if %errorlevel% equ 0 (
echo 打印任务已提交,成功!
) else (
echo 打印失败,错误代码:%errorlevel%
)
pause
重点解释几行关键命令:
-q:安静模式,不输出多余的Ghostscript信息,让控制台干净点。-dNOPAUSE:打印完一页后不暂停,直接继续下一页。-dBATCH:处理完所有文件后退出,而不是进入交互式命令行。-sDEVICE=mswinpr2:这是关键!mswinpr2是Ghostscript为Windows打印设计的设备驱动,它会直接调用Windows GDI打印接口,把你想要的打印机名字传进去。-sOutputFile="\\%PRINTER_NAME%":这里不是输出到文件,而是指定打印目标。注意Windows网络打印机和本地打印机的名字格式可能不同,本地打印机通常直接用名字,网络打印机可能需要\\服务器名\打印机共享名。
保存后,双击运行这个.bat文件。如果一切正常,你应该能看到“打印任务已提交”的提示,并且打印机开始工作。
第四部分:进阶版脚本——批量打印整个文件夹
现在,我们把脚本升级为真正的“批量处理器”。这个版本会:
- 扫描指定文件夹下的所有PDF文件。
- 按文件名排序(避免乱序打印)。
- 逐个发送打印任务。
- 记录成功和失败的文件,方便你事后检查。
- 支持用户自定义打印机、份数、是否双面。
新建batch_print_all.bat,内容如下:
@echo off
chcp 65001 >nul
setlocal enabledelayedexpansion
:: ============ 用户配置区 ============
set "INPUT_FOLDER=D:\待打印文件"
set "PRINTER_NAME=HP LaserJet Pro M404"
set "COPIES=1"
set "DUPLEX=false" :: 是否双面打印,true或false
set "GS_PATH=C:\Program Files\gs\gs10.xx\bin\gswin64c.exe"
set "LOG_FILE=%~dp0print_log.txt"
:: ====================================
:: 清理旧日志
echo 打印日志 [%date% %time%] > "%LOG_FILE%"
echo ======================================== >> "%LOG_FILE%"
:: 检查文件夹是否存在
if not exist "%INPUT_FOLDER%" (
echo 错误:文件夹 %INPUT_FOLDER% 不存在!
pause
exit /b 1
)
:: 检查Ghostscript是否存在
if not exist "%GS_PATH%" (
echo 错误:Ghostscript未找到,请检查GS_PATH配置。
pause
exit /b 1
)
:: 检查打印机是否存在(通过wmic查询)
set "PRINTER_EXISTS="
for /f "tokens=*" %%a in ('wmic printer where "name='%PRINTER_NAME%'" get name 2^>nul') do (
for /f "tokens=*" %%b in ("%%a") do set "PRINTER_EXISTS=%%b"
)
if "%PRINTER_EXISTS%" neq "%PRINTER_NAME%" (
echo 错误:未找到打印机 "%PRINTER_NAME%",请确认打印机已安装且名称无误。
pause
exit /b 1
)
echo 开始批量打印...
echo 文件夹:%INPUT_FOLDER%
echo 打印机:%PRINTER_NAME%
echo 份数:%COPIES%
echo 双面:%DUPLEX%
echo.
:: 计数器
set "SUCCESS_COUNT=0"
set "FAIL_COUNT=0"
set "FILE_COUNT=0"
:: 遍历所有PDF文件(按名称排序)
for /f "tokens=*" %%F in ('dir /b /a-d "%INPUT_FOLDER%\*.pdf" ^| sort') do (
set /a FILE_COUNT+=1
set "FULL_PATH=%INPUT_FOLDER%\%%F"
echo [%FILE_COUNT%] 正在打印:%%F ...
:: 构建Ghostscript命令
:: 如果需要双面,可以添加-dNOSAFER等参数,但mswinpr2通常不直接支持复杂选项
:: 这里我们主要控制份数,双面需要在打印机属性里设置,或者通过其他方式
"%GS_PATH%" -q -dNOPAUSE -dBATCH -sDEVICE=mswinpr2 -sOutputFile="\\%PRINTER_NAME%" -dNumCopies=%COPIES% "%FULL_PATH%" >> "%LOG_FILE%" 2>&1
if %errorlevel% equ 0 (
echo -> 成功
echo [%date% %time%] 成功:%%F >> "%LOG_FILE%"
set /a SUCCESS_COUNT+=1
) else (
echo -> 失败 (错误码:%errorlevel%)
echo [%date% %time%] 失败:%%F (错误码:%errorlevel%) >> "%LOG_FILE%"
set /a FAIL_COUNT+=1
)
)
echo.
echo ========================================
echo 批量打印完成!
echo 总计:%FILE_COUNT% 个文件
echo 成功:%SUCCESS_COUNT% 个
echo 失败:%FAIL_COUNT% 个
echo 详细日志保存在:%LOG_FILE%
echo ========================================
:: 打开日志文件
if %FAIL_COUNT% gtr 0 (
notepad "%LOG_FILE%"
)
pause
endlocal
这个脚本的几个亮点:
- 用户配置区:开头用注释标出,你只需要修改那几行就能适配自己的环境,不用动核心逻辑。
- 前置检查:在真正开始打印前,脚本会检查文件夹是否存在、Ghostscript是否安装、目标打印机是否已添加到Windows。这避免了打到一半发现打印机没开这种尴尬。
- 日志记录:每个文件的打印结果都会追加到同目录下的
print_log.txt里。如果有任何失败,脚本最后会自动打开日志文件,让你一眼就能看到哪些文件出了问题。 - 错误隔离:某个文件打印失败不会中断整个流程,脚本会继续打印下一个。这对大批量任务非常重要——你总不能因为一个损坏的PDF导致后面99个能打的也打不了。
第五部分:高级技巧——如何处理非PDF文件(Word、Excel、图片)
很多读者可能会问:“我只打印PDF吗?那Word和Excel文档呢?”
这就涉及到另一个痛点:不同文件格式需要不同的“翻译官”。
5.1 Word和Excel文档
Ghostscript对Office原生格式(.docx, .xlsx)支持不佳。最靠谱的方案是利用Office自带的COM接口或命令行导出功能。
如果你安装了Microsoft Office,可以使用Word.Application或Excel.Application的COM对象通过VBScript或PowerShell来驱动打印。但批处理直接调用COM比较麻烦,所以我们换一种思路:先把Office文件“另存为”PDF,然后再用上面的Ghostscript批量打印PDF。
下面是一个简单的VBScript片段,可以嵌入到批处理中,实现Word文档转PDF:
' save_as_pdf.vbs
Set objWord = CreateObject("Word.Application")
objWord.Visible = False
Set objDoc = objWord.Documents.Open(WScript.Arguments(0))
objDoc.ExportAsFixedFormat WScript.Arguments(1), 17 ' 17 = wdExportFormatPDF
objDoc.Close False
objWord.Quit
Set objDoc = Nothing
Set objWord = Nothing
然后在批处理中调用:
cscript //nologo save_as_pdf.vbs "D:\ Documents\report.docx" "D:\Documents\report.pdf"
但这显然太慢了,每个文件都要启动Word。更好的做法是使用PowerShell,它可以直接调用COM对象并批量处理:
$word = New-Object -ComObject Word.Application
$word.Visible = $false
$folder = "D:\待打印Word文件"
$pdfFolder = "D:\PDF缓存"
Get-ChildItem "$folder\*.docx" | ForEach-Object {
$doc = $word.Documents.Open($_.FullName)
$pdfPath = Join-Path $pdfFolder ($_.BaseName + ".pdf")
$doc.ExportAsFixedFormat($pdfPath, 17)
$doc.Close($false)
Write-Host "已转换:$($_.Name)"
}
$word.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($word) | Out-Null
保存为convert_to_pdf.ps1,然后在批处理中调用powershell -ExecutionPolicy Bypass -File convert_to_pdf.ps1。转换完成后,再用之前的Ghostscript脚本批量打印PDF。
5.2 图片文件(JPG, PNG)
图片打印反而比PDF更简单,因为Windows自带mshta和VBS可以调用打印接口,但更简单的方式是使用Ghostscript的img设备,或者直接利用Windows的print命令(对图片支持有限)配合ImageMagick。
如果你安装了ImageMagick,可以用它的convert命令直接打印:
convert image.jpg printer:HP_LaserJet
但考虑到通用性,我建议还是将图片先合并成一个PDF,再用Ghostscript打印。ImageMagick也可以做到:
convert *.jpg combined.pdf
gswin64c -q -dNOPAUSE -dBATCH -sDEVICE=mswinpr2 -sOutputFile="\\%PRINTER_NAME%" combined.pdf
第六部分:真实场景案例——行政部的月度报表自动化打印
让我给你讲一个真实的办公室场景,帮你把知识串联起来。
小李是某公司的行政主管,每个月末需要把财务部门生成的50份Excel月度报表打印出来,装订成册。以前她每天早上提前半小时到办公室,打开电脑,逐个打开Excel,点击打印,选择打印机,确认,等待……一个半小时后,手都酸了。
我们帮她设计了一套自动化流程:
第一步:统一格式
财务部门把报表导出为PDF时,已经统一了命名规范:202310_部门A_报表.pdf、202310_部门B_报表.pdf……文件名里包含了年月和部门,非常适合排序。
第二步:编写批处理脚本
小李的脚本大概长这样:
@echo off
setlocal
set "INPUT=D:\财务报表\202310"
set "PRINTER=财务部-激光打印机"
set "GS=C:\Program Files\gs\gs10.xx\bin\gswin64c.exe"
echo 开始批量打印财务月度报表...
for /f "tokens=*" %%F in ('dir /b /a-d "%INPUT%\*.pdf" ^| sort') do (
"%GS%" -q -dNOPAUSE -dBATCH -sDEVICE=mswinpr2 -sOutputFile="\\%PRINTER%" "%INPUT%\%%F"
)
echo 全部打印完成!
pause
第三步:定时任务
更绝的是,小李还把这个脚本加到了Windows任务计划程序中,设置为每个月30号晚上10点自动执行。这样,她第二天早上到办公室,打印机已经默默地把50份报表全部打印好了,她只需要去装订就行。
结果: 小李每天节省了1.5小时,而且再也不会因为忘记打印或选错打印机而挨骂了。
第七部分:常见问题与排查指南
即使是最完美的脚本,也难免遇到意外。下面列出几个高频问题及解决方案。
问题1:“未找到打印机”错误
原因:打印机名称拼写错误,或者打印机名称包含空格、特殊字符,导致批处理解析出错。
解决:
- 打开“控制面板” -> “设备和打印机”。
- 找到你的打印机,右键“打印机属性”,在“常规”选项卡里复制完整的打印机名称。
- 在批处理的
PRINTER_NAME变量里,用双引号包裹名称,例如:set "PRINTER_NAME="HP LaserJet Pro M404""。 - 如果打印机名称包含空格,Ghostscript的
\\%PRINTER_NAME%可能需要加引号:-sOutputFile="\"\\%PRINTER_NAME%\""。
问题2:打印出来的PDF是空白的
原因:Ghostscript的mswinpr2设备对某些加密PDF或特殊字体支持的PDF处理不佳。
解决:
- 尝试使用
pdf2ps先将PDF转换为PostScript,再用Ghostscript打印PS文件。gswin64c -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=temp.ps input.pdf gswin64c -q -dNOPAUSE -dBATCH -sDEVICE=mswinpr2 -sOutputFile="\\%PRINTER_NAME%" temp.ps - 或者,直接使用Adobe Acrobat Reader的
/t参数(虽然慢,但兼容性最好):"C:\Program Files\Adobe\Acrobat DC\Acrobat\AcroRd32.exe" /t "D:\file.pdf" "HP LaserJet" "" ""
问题3:打印顺序混乱
原因:Windows打印队列对多任务的处理并不总是严格FIFO(先进先出)。
解决:
在批处理脚本中加入timeout延迟,让每个打印任务之间有间隔。例如:
“`batch
for /f “tokens=*” %%F in (‘dir /b /a-d “%INPUT%*.pdf” ^| sort’) do (
"%GS%" -q -dNOPAUSE -dBATCH -
