前幾章我們學習了如何操控文本,下面要做的是將文本呈于紙上。在這章中,我們將會著手用于打印文件和控制打印選項的命令行工具。通常不同發行版的打印配置各有不同且都會在其安裝時自動完成,因此這里我們不討論打印的配置過程。本章的練習需要一臺正確配置的打印機來完成。
我們將討論一下命令:
> * pr —— 轉換需要打印的文本文件
> * lpr —— 打印文件
> * lp —— 打印文件(System V)
> * a2ps —— 為 PostScript 打印機格式化文件
> * lpstat —— 顯示打印機狀態信息
> * lpq —— 顯示打印機隊列狀態
> * lprm —— 取消打印任務
> * cancel —— 取消打印任務(System V)
## 打印簡史
為了較好的理解類 Unix 操作系統中的打印功能,我們必須先了解一些歷史。類 Unix 系統中的打印可追溯到操作系統本身的起源,那時候打印機和它的用法與今天截然不同。
### 早期的打印
和計算機一樣,前 PC 時代的打印機都很大、很貴,并且很集中。1980年的計算機用戶都是在離電腦很遠的地方用一個連接電腦的終端來工作的,而打印機就放在電腦旁并受到計算機管理員的全方位監視。
由于當時打印機既昂貴又集中,而且都工作在早期的 Unix 環境下,人們從實際考慮通常都會多人共享一臺打印機。為了區別不同用戶的打印任務,每個打印任務的開頭都會打印一張寫著用戶名字的標題頁,然后計算機工作人員會用推車裝好當天的打印任務并分發給每個用戶。
### 基于字符的打印機
80年代的打印機技術有兩方面的不同。首先,那時的打印機基本上都是打擊式打印機。打擊式打印機使用撞針打擊色帶的機械結構在紙上形成字符。這種流行的技術造就了當時的菊輪式打印和點陣式打印。
其次,更重要的是,早期打印機的特點是它使用設備內部固定的一組字符集。比如,一臺菊輪式打印機只能打印固定在其菊花輪花瓣上的字符,就這點而言打印機更像是高速打字機。大部分打字機都使用等寬字體,意思是說每個字符的寬度相等,頁面上只有固定的區域可供打印,而這些區域只能容納固定的字符數。大部分打印機采用橫向10字符每英寸(CPI)和縱向6行每英寸(LPI)的規格打印,這樣一張美式信片紙就有橫向85字符寬縱向66行高,加上兩側的頁邊距,一行的最大寬度可達80字符。據此,使用等寬字體就能提供所見即所得(WYSIWYG,What You See Is What You Get)的打印預覽。
接著,一臺類打字機的打印機會收到以簡單字節流的形式傳送來的數據,其中就包含要打印的字符。例如要打印一個字母a,計算機就會發送 ASCII 碼97,如果要移動打印機的滑動架和紙張,就需要使用回車、換行、換頁等的小編號 ASCII 控制碼。使用控制碼,還能實現一些之前受限制的字體效果,比如粗體,就是讓打印機先打印一個字符,然后退格再打印一遍來得到顏色較深的效果的。用 nroff 來產生一個手冊頁然后用 cat -A 檢查輸出,我們就能親眼看看這種效果了:
~~~
[me@linuxbox ~]$ zcat /usr/share/man/man1/ls.1.gz | nroff -man | cat -A | head
LS(1) User Commands LS(1)
$
N^HNA^HAM^HME^HE$
ls - list directory contents$
$
S^HSY^HYN^HNO^HOP^HPS^HSI^HIS^HS$
l^Hls^Hs [_^HO_^HP_^HT_^HI_^HO_^HN]... [_^HF_^HI_^HL_^HE]...$
~~~
^H(ctrl-H)字符是用于打印粗體效果的退格符。同樣,我們還可以看到用于打印下劃線效果的[退格/下劃線]序列。
### 圖形化打印機
圖形用戶界面(GUI)的發展催生了打印機技術中主要的變革。隨著計算機的展現步入更多以圖形為基礎的方式,打印技術也從基于字符走向圖形化技術,這一切都是源于激光打印機的到來,它不僅廉價,還可以在打印區域的任意位置打印微小的墨點,而不是使用固定的字符集。這讓打印機能夠打印成比例的字體(像用排字機那樣),甚至是圖片和高質量圖表。
然而,從基于字符的方式到轉移到圖形化的方式提出了一個嚴峻的技術挑戰。原因如下:使用基于字符的打印機時,填滿一張紙所用的字節數可以這樣計算出來(假設一張紙有60行,每行80個字符):60 × 80 = 4800字節。
相比之下,用一臺300點每英寸(DPI)分辨率的激光打印機(假設一張紙有8乘10英寸的打印區域)打印則需要 (8 × 300) × (10 × 300) / 8 = 900,000字節。
當時許多慢速的個人電腦網絡無法接受激光打印機打印一頁需要傳輸將近1兆的數據這一點,因此,很有必要發明一種更聰明的方法。
這種發明便是頁面描述語言(PDL)。PDL 是一種描述頁面內容的編程語言。簡單的說就是,“到這個地方,印一個10點大小的黑體字符 a ,到這個地方。。。” 這樣直到頁面上的所有內容都描述完了。第一種主要的 PDL 是 Adobe 系統開發的 PostScript,直到今天,這種語言仍被廣泛使用。PostScript 是專為印刷各類圖形和圖像設計的完整的編程語言,它內建支持35種標準的高質量字體,在工作是還能夠接受其他的字體定義。最早,對 PostScript 的支持是打印機本身內建的。這樣傳輸數據的問題就解決了。相比基于字符打印機的簡單字節流,典型的 PostScript 程序更為詳細,而且比表示整個頁面的字節數要小很多。
一臺 PostScript 打印機接受 PostScript 程序作為輸入。打印機有自己的處理器和內存(通常這讓打印機比連接它的計算機更為強大),能執行一種叫做 PostScript 解析器的特殊程序用于讀取輸入的 PostScript 程序并生成結果導入打印機的內存,這樣就形成了要轉移到紙上的位(點)圖。這種將頁面渲染成大型位圖(bitmap)的過程有個通用名稱作光柵圖像處理器(raster image processor),又叫 RIP。
多年之后,電腦和網絡都變得更快了。這使得 RIP 技術從打印機轉移到了主機上,還讓高品質打印機變得更便宜了。
現在的許多打印機仍能接受基于字符的字節流,但很多廉價的打印機卻不支持,因為它們依賴于主機的 RIP 提供的比特流來作為點陣打印。當然也有不少仍舊是 PostScript 打印機。
## 在 Linux 下打印
當前 Linux 系統采用兩套軟件配合顯示和管理打印。第一,CUPS(Common Unix Printing System,一般 Unix 打印系統),用于提供打印驅動和打印任務管理;第二,Ghostscript,一種 PostScript 解析器,作為 RIP 使用。
CUPS 通過創建并維護打印隊列來管理打印機。如前所述,Unix 下的打印原本是設計成多用戶共享中央打印機的管理模式的。由于打印機本身比連接到它的電腦要慢,打印系統就需要對打印任務進行調度使其保持順序。CUPS 還能識別出不同類型的數據(在合理范圍內)并轉換文件為可打印的格式。
## 為打印準備文件
作為命令行用戶,盡管打印各種格式的文本都能實現,不過打印最多的,還是文本。
### pr - 轉換需要打印的文本文件
前面的章節我們也有提到過 pr 命令,現在我們來探討一下這條命令結合打印使用的一些選項。我們知道,在打印的歷史上,基于字符的打印機曾經用過等寬字體,致使每頁只能打印固定的行數和字符數,而 pr 命令則能夠根據不同的頁眉和頁邊距排列文本使其適應指定的紙張。表23-1總結了最常用的選項。
表23-1: 常用 pr 選項
| 選項 | 描述 |
|-------|----------|
| +first[:last] | 輸出從 first 到 last(默認為最后)范圍內的頁面。 |
| -columns | 根據 columns 指定的列數排版頁面內容。 |
| -a | 默認多列輸出為垂直,用 -a (across)可使其水平輸出。 |
| -d | 雙空格輸出。 |
| -D format | 用 format 指定的格式修改頁眉中顯示的日期,日期命令中 format 字符串的描述詳見參考手冊。 |
| -f | 改用換頁替換默認的回車來分割頁面。 |
| -h header | 在頁眉中部用 header 參數替換打印文件的名字。 |
| -l length | 設置頁長為 length,默認為66行(每英寸6行的美國信紙)。 |
| -n | 輸出行號。 |
| -o offset | 創建一個寬 offset 字符的左頁邊。 |
| -w width | 設置頁寬為 width,默認為72字符。 |
我們通常用管道配合 pr 命令來做篩選。下面的例子中我們會列出目錄 /usr/bin 并用 pr 將其格式化為3列輸出的標題頁:
~~~
[me@linuxbox ~]$ ls /usr/bin | pr -3 -w 65 | head
2012-02-18 14:00 Page 1
[ apturl bsd-write
411toppm ar bsh
a2p arecord btcflash
a2ps arecordmidi bug-buddy
a2ps-lpr-wrapper ark buildhash
~~~
## 將打印任務送至打印機
CUPS 打印體系支持兩種曾用于類 Unix 系統的打印方式。一種,叫 Berkeley 或 LPD(用于 Unix 的 Berkeley 軟件發行版),使用 lpr 程序;另一種,叫 SysV(源自 System V 版本的 Unix),使用 lp 程序。這兩個程序的功能大致相同。具體使用哪個完全根據個人喜好。
### lpr - 打印文件(Berkeley 風格)
lpr 程序可以用來把文件傳送給打印機。由于它能接收標準輸入,所以能用管道來協同工作。例如,要打印剛才多列目錄列表的結果,我們只需這樣:
~~~
[me@linuxbox ~]$ ls /usr/bin | pr -3 | lpr
~~~
報告會送到系統默認的打印機,如果要送到別的打印機,可以使用 -P 參數:
~~~
lpr -P printer_name
~~~
printer_name 表示這臺打印機的名稱。若要查看系統已知的打印機列表:
~~~
[me@linuxbox ~]$ lpstat -a
~~~
注意:許多 Linux 發行版允許你定義一個輸出 PDF 文件但不執行實體打印的“打印機”,這可以用來很方便的檢驗你的打印命令。看看你的打印機配置程序是否支持這項配置。在某些發行版中,你可能要自己安裝額外的軟件包(如 cups-pdf)來使用這項功能。
表23-2顯示了 lpr 的一些常用選項
表23-2: 常用 lpr 選項
| 選項 | 描述 |
|-------|--------|
| -# number | 設定打印份數為 number。 |
| -p | 使每頁頁眉標題中帶有日期、時間、工作名稱和頁碼。這種所謂的“美化打印”選項可用于打印文本文件。 |
| -P printer | 指定輸出打印機的名稱。未指定則使用系統默認打印機。 |
| -r | 打印后刪除文件。對程序產生的臨時打印文件較為有用。 |
### lp - 打印文件(System V 風格)
和 lpr 一樣,lp 可以接收文件或標準輸入為打印內容。與 lpr 不同的是 lp 支持不同的選項(略為復雜),表23-3列出了其常用選項。
表23-3: 常用 lp 選項
| 選項 | 描述 |
|-------|--------|
| -d printer | 設定目標(打印機)為 printer。若d 選項未指定,則使用系統默認打印機。 |
| -n number | 設定的打印份數為 number。 |
| -o landscape | 設置輸出為橫向。 |
| -o fitplot | 縮放文件以適應頁面。打印圖像時較為有用,如 JPEG 文件。 |
| -o scaling=number | 縮放文件至 number。100表示填滿頁面,小于100表示縮小,大于100則會打印在多頁上。 |
| -o cpi=number | 設定輸出為 number 字符每英寸。默認為10。 |
| -o lpi=number | 設定輸出為 number 行每英寸,默認為6。 |
| -o page-bottom=points
-o page-left=points
-o page-right=points
-o page-top=points | 設置頁邊距,單位為點,一種印刷上的單位。一英寸 =72點。 |
| -P pages | 指定打印的頁面。pages 可以是逗號分隔的列表或范圍——例如 1,3,5,7-10。 |
再次打印我們的目錄列表,這次我們設置12 CPI、8 LPI 和一個半英寸的左邊距。注意這里我必須調整 pr 選項來適應新的頁面大小:
~~~
[me@linuxbox ~]$ ls /usr/bin | pr -4 -w 90 -l 88 | lp -o page-left=36 -o cpi=12 -o lpi=8
~~~
這條命令用小于默認的格式產生了一個四列的列表。增加 CPI 可以讓我們在頁面上打印更多列。
### 另一種選擇:a2ps
a2ps 程序很有趣。單從名字上看,這是個格式轉換程序,但它的功能不止于此。程序名字的本意為 ASCII to PostScript,它是用來為 PostScript 打印機準備要打印的文本文件的。多年后,程序的功能得到了提升,名字的含義也變成了 Anything to PostScript。盡管名為格式轉換程序,但它實際的功能卻是打印。它的默認輸出不是標準輸出,而是系統的默認打印機。程序的默認行為被稱為“漂亮的打印機”,這意味著它可以改善輸出的外觀。我們能用程序在桌面上創建一個 PostScript 文件:
~~~
[me@linuxbox ~]$ ls /usr/bin | pr -3 -t | a2ps -o ~/Desktop/ls.ps -L 66
[stdin (plain): 11 pages on 6 sheets]
[Total: 11 pages on 6 sheets] saved into the file `/home/me/Desktop/ls.ps'
~~~
這里我們用帶 -t 參數(忽略頁眉和頁腳)的 pr 命令過濾數據流,然后用 a2ps 指定一個輸出文件(-o 參數),并設定每頁66行(-L 參數)來匹配 pr 的輸出分頁。用合適的文件查看器查看我們的輸出文件,我們就會看到圖23-1中顯示的結果。

圖 23-1: 瀏覽 a2ps 的輸出結果
可以看到,默認的輸出布局是一面兩頁的,這將導致兩頁的內容被打印到一張紙上。a2ps 還能利用頁眉和頁腳。
a2ps 有很多選項,總結在表23-4中。
表23-4: a2ps 選項
| 選項 | 描述 |
|------|-------|
| --center-title text | 設置中心頁標題為 text。 |
| --columns number | 將所有頁面排列成 number 列。默認為2。 |
| --footer text | 設置頁腳為 text。 |
| --guess | 報告參數中文件的類型。由于 a2ps 會轉換并格式化所有類型的數據,所以當給定文件類型后,這個選項可以很好的用來判斷 a2ps 應該做什么。 |
| --left-footer text | 設置左頁腳為 text。 |
| --left-title text | 設置頁面左標題為 text。 |
| --line-numbers=interval | 每隔 interval 行輸出行號。 |
| --list=defauls | 顯示默認設置。 |
| --list=topic | 顯示 topic 設置,topic 表示下列之一:代理程序(用來轉換數據的外部程序),編碼,特征,變量,媒介(頁面大小等),ppd(PostScript 打印機描述信息),打印機,起始程序(為常規輸出添加前綴的代碼部分),樣式表,或用戶選項。 |
| --pages range | 打印 range 范圍內的頁面。 |
| --right-footer text | 設置右頁腳為 text。 |
| --right-title text | 設置頁面右標題為 text。 |
| --rows number | 將所有頁面排列成 number 排。默認為1。 |
| -B | 沒有頁眉。 |
| -b text | 設置頁眉為 text。 |
| -f size | 使用字體大小為 size 號。 |
| -l number | 設置每行字符數為 number。此項和 -L 選項(見下方)可以給文件用其他程序來更準確的分頁,如 pr。 |
| -L number | 設置每頁行數為 number。 |
| -M name | 使用打印媒介的名稱——例如,A4。 |
| -n number | 每頁輸出 number 份。 |
| -o file | 輸出到文件 file。如果指定為 - ,則輸出到標準輸出。 |
| -P printer | 使用打印機 printer。如果未指定,則使用系統默認打印機。 |
| -R | 縱向打印。 |
| -r | 橫向打印。 |
| -T number | 設置制表位為每 number 字符。 |
| -u text | 用 text 作為頁面底圖(水印)。 |
以上只是對 a2ps 的總結,更多的選項尚未列出。
注意:a2ps 目前仍在不斷的開發中。就我的測試而言,不同版本之間都多少有所變化。CentOS 4 中輸出總是默認為標準輸出。在 CentOS 4 和 Fedora 10 中,盡管程序配置信紙為默認媒介,輸出還是默認為 A4紙。我可以明確的指定需要的選項來解決這些問題。Ubuntu 8.04 中,a2ps 表現的正如參考文檔中所述。 另外,我們也要注意到另一個轉換文本為 PostScript 的輸出格式化工具,名叫 enscript。它具有許多相同的格式化和打印功能,但和 a2ps 唯一的不同在于,它只能處理純文本的輸入。
## 監視和控制打印任務
由于 Unix 打印系統的設計是能夠處理多用戶的多重打印任務,CUPS 也是如此設計的。每臺打印機都有一個打印隊列,其中的任務直到傳送到打印機才停下并進行打印。CUPS 支持一些命令行程序來管理打印機狀態和打印隊列。想 lpr 和 lp 這樣的管理程序都是以 Berkeley 和 System V 打印系統的相應程序為依據進行排列的。
### lpstat - 顯示打印系統狀態
lpstat 程序可用于確定系統中打印機的名字和有效性。例如,我們系統中有一臺實體打印機(名叫 printer)和一臺 PDF 虛擬打印機(名叫 PDF),我們可以像這樣查看打印機狀態:
~~~
[me@linuxbox ~]$ lpstat -a
PDF accepting requests since Mon 05 Dec 2011 03:05:59 PM EST
printer accepting requests since Tue 21 Feb 2012 08:43:22 AM EST
~~~
接著,我們可以查看打印系統更具體的配置信息:
~~~
[me@linuxbox ~]$ lpstat -s
system default destination: printer
device for PDF: cups-pdf:/
device for printer: ipp://print-server:631/printers/printer
~~~
上例中,我們看到 printer 是系統默認的打印機,其本身是一臺網絡打印機,使用網絡打印協議(ipp://)通過網絡連接到名為 print-server 的系統。
lpstat 的常用選項列于表23-5。
表23-5: 常用 lpstat 選項
| 選項 | 描述 |
|------|-------|
| -a [printer...] | 顯示 printer 打印機的隊列。這里顯示的狀態是打印機隊列承受任務的能力,而不是實體打印機的狀態。若未指定打印機,則顯示所有打印隊列。 |
| -d | 顯示系統默認打印機的名稱。 |
| -p [printer...] | 顯示 printer 指定的打印機的狀態。若未指定打印機,則顯示所有打印機狀態。 |
| -r | 顯示打印系統的狀態。 |
| -s | 顯示匯總狀態。 |
| -t | 顯示完整狀態報告。 |
### lpq - 顯示打印機隊列狀態
使用 lpq 程序可以查看打印機隊列的狀態,從中我們可以看到隊列的狀態和所包含的打印任務。下面的例子顯示了一臺名叫 printer 的系統默認打印機包含一個空隊列的情況:
~~~
[me@linuxbox ~]$ lpq
printer is ready
no entries
~~~
如果我們不指定打印機(用 -P 參數),就會顯示系統默認打印機。如果給打印機添加一項任務再查看隊列,我們就會看到下列結果:
~~~
[me@linuxbox ~]$ ls *.txt | pr -3 | lp
request id is printer-603 (1 file(s))
[me@linuxbox ~]$ lpq
printer is ready and printing
Rank Owner Job File(s) Total Size
active me 603 (stdin) 1024 bytes
~~~
### lprm 和 cancel - 取消打印任務
CUPS 提供兩個程序來從打印隊列中終止并移除打印任務。一個是 Berkeley 風格的(lprm),另一個是 System V 的(cancel)。在支持的選項上兩者有較小的區別但是功能卻幾乎相同。以上面的打印任務為例,我們可以像這樣終止并移除任務:
~~~
[me@linuxbox ~]$ cancel 603
[me@linuxbox ~]$ lpq
printer is ready
no entries
~~~
每個命令都有選項可用于移除某用戶、某打印機或多個任務號的所有任務,相應的參考手冊中都有詳細的介紹。
- 第一章:引言
- 第二章:什么是shell
- 第三章:文件系統中跳轉
- 第四章:研究操作系統
- 第五章:操作文件和目錄
- 第六章:使用命令
- 第七章:重定向
- 第八章:從shell眼中看世界
- 第九章:鍵盤高級操作技巧
- 第十章:權限
- 第十一章:進程
- 第十二章:shell環境
- 第十三章:VI簡介
- 第十四章:自定制shell提示符
- 第十五章:軟件包管理
- 第十六章:存儲媒介
- 第十七章:網絡系統
- 第十八章:查找文件
- 第十九章:歸檔和備份
- 第二十章:正則表達式
- 第二十一章:文本處理
- 第二十二章:格式化輸出
- 第二十三章:打印
- 第二十四章:編譯程序
- 第二十五章:編寫第一個shell腳本
- 第二十六章:啟動一個項目
- 第二十七章:自頂向下設計
- 第二十八章:流程控制 if分支結構
- 第二十九章:讀取鍵盤輸入
- 第三十章:流程控制 while/until 循環
- 第三十一章:疑難排解
- 第三十二章:流程控制 case分支
- 第三十三章:位置參數
- 第三十四章:流程控制 for循環
- 第三十五章:字符串和數字
- 第三十六章:數組
- 第三十七章:奇珍異寶