为什么你还需要这个“古老”的技巧?

说实话,我也曾以为“自动打印”是十几年前的技术了。但当你真在办公室面对一抽屉PDF合同、上百份报表要打印,或者服务器定时需要把生成的日志文件自动打印归档时,Windows自带的那个打印对话框简直就是一场灾难——鼠标点到手酸,还要担心选错打印机、打错份数。

我见过太多行政同事下班前还在一台台点“打印”,心里默默祈祷别卡纸。其实,用一段简单的批处理脚本,配合Windows自带的工具或免费开源软件,你完全可以实现:把文件夹里所有PDF扔进去,一键全自动按顺序打印,甚至还能指定双面打印、指定特定打印机。

今天这篇教程,不整那些虚的,直接从最基础的原理讲起,一步步带你写出能真正干活儿的脚本。内容很干,但保证你能听懂,哪怕你是电脑小白。


第一部分:核心原理——让电脑“知道”怎么打印

在写任何代码之前,你得先明白一件事:Windows系统本身并不原生支持直接从命令行“静默打印”PDF文件。这是什么意思呢?就是你在CMD里直接敲print my.pdf,系统会告诉你“命令不可识别”。

为什么?因为PDF是一种复杂的容器格式,里面可能有矢量图、嵌入字体、扫描图片等等。普通的print命令只能处理纯文本文件(比如.txt、.doc)。要打印PDF,你必须借助一个“翻译官”——也就是能够解析PDF并发送给打印机的软件。

目前主流的方案有三种:

  1. 使用Ghostscript(开源免费,功能最强):这是技术圈最推崇的方案。Ghostscript是一个强大的PostScript和PDF处理工具,它可以直接把PDF转换成打印机能理解的PCL或PostScript语言,然后通过命令行直接发送到指定打印机。它的优点是免费、稳定、支持批量、支持参数控制(页边距、份数、双面等)。缺点是需要在系统里安装它。
  2. 使用Adobe Acrobat Reader的命令行接口:Adobe官方提供的AcroRd32.exe有一个/t参数,可以指定打印机打印文件。但这个方法非常慢,而且每打印一个文件就会弹出Adobe的界面一闪而过,体验不太好,也不适合真正的无人值守自动化。
  3. 使用Python + PyPDF2/ReportLab:这是程序员的做法,灵活但需要环境配置。

对于大多数办公场景,我强烈推荐方案一:Ghostscript。它虽然听起来有点“极客”,但配置一次后,你的批处理脚本会简洁得像诗一样。而且,Ghostscript是开源的,完全免费,很多专业打印软件底层用的就是它。


第二部分:准备工作——安装Ghostscript

别被这个名字吓到,下载安装非常简单。

  1. 打开浏览器,访问Ghostscript官方网站(通常搜索“Ghostscript download”即可,认准Aladdin Enterprises或Artifex Software的官方源)。
  2. 下载对应你系统架构的版本(通常是64位Windows,下载gs10xxw64.exe或类似名称)。
  3. 运行安装程序,注意:安装过程中有一个步骤会问你“是否添加到PATH环境变量”,一定要勾选“是”或者手动记住安装路径(默认通常是C:\Program Files\gs\gs10.xx\bin\gswin64c.exe)。
  4. 安装完成后,打开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文件。如果一切正常,你应该能看到“打印任务已提交”的提示,并且打印机开始工作。


第四部分:进阶版脚本——批量打印整个文件夹

现在,我们把脚本升级为真正的“批量处理器”。这个版本会:

  1. 扫描指定文件夹下的所有PDF文件。
  2. 按文件名排序(避免乱序打印)。
  3. 逐个发送打印任务。
  4. 记录成功和失败的文件,方便你事后检查。
  5. 支持用户自定义打印机、份数、是否双面。

新建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

这个脚本的几个亮点:

  1. 用户配置区:开头用注释标出,你只需要修改那几行就能适配自己的环境,不用动核心逻辑。
  2. 前置检查:在真正开始打印前,脚本会检查文件夹是否存在、Ghostscript是否安装、目标打印机是否已添加到Windows。这避免了打到一半发现打印机没开这种尴尬。
  3. 日志记录:每个文件的打印结果都会追加到同目录下的print_log.txt里。如果有任何失败,脚本最后会自动打开日志文件,让你一眼就能看到哪些文件出了问题。
  4. 错误隔离:某个文件打印失败不会中断整个流程,脚本会继续打印下一个。这对大批量任务非常重要——你总不能因为一个损坏的PDF导致后面99个能打的也打不了。

第五部分:高级技巧——如何处理非PDF文件(Word、Excel、图片)

很多读者可能会问:“我只打印PDF吗?那Word和Excel文档呢?”

这就涉及到另一个痛点:不同文件格式需要不同的“翻译官”

5.1 Word和Excel文档

Ghostscript对Office原生格式(.docx, .xlsx)支持不佳。最靠谱的方案是利用Office自带的COM接口或命令行导出功能

如果你安装了Microsoft Office,可以使用Word.ApplicationExcel.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自带mshtaVBS可以调用打印接口,但更简单的方式是使用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_报表.pdf202310_部门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:“未找到打印机”错误

原因:打印机名称拼写错误,或者打印机名称包含空格、特殊字符,导致批处理解析出错。

解决

  1. 打开“控制面板” -> “设备和打印机”。
  2. 找到你的打印机,右键“打印机属性”,在“常规”选项卡里复制完整的打印机名称。
  3. 在批处理的PRINTER_NAME变量里,用双引号包裹名称,例如:set "PRINTER_NAME="HP LaserJet Pro M404""
  4. 如果打印机名称包含空格,Ghostscript的\\%PRINTER_NAME%可能需要加引号:-sOutputFile="\"\\%PRINTER_NAME%\""

问题2:打印出来的PDF是空白的

原因:Ghostscript的mswinpr2设备对某些加密PDF或特殊字体支持的PDF处理不佳。

解决

  1. 尝试使用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
    
  2. 或者,直接使用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 -