服务热线:13616026886

技术文档 欢迎使用技术文档,我们为你提供从新手到专业开发者的所有资源,你也可以通过它日益精进

位置:首页 > 技术文档 > JAVA > 新手入门 > 基础入门 > 查看文档

用lpadmin打印多种内容形式

font color="#cc0000">  概述  

  设置一个能够处理多种内容形式的打印服务器,似乎并不是一个困难的任务--实际上也不难,只要考虑一下第三方软件。当你决定只用lpadmin来做时,困难就来了。unix系统管理专家errol fouquet 和robert krumm 通过本文指导你用一个经检验过的方法,来设置一个打印服务器,使其能够成功地处理纯文本、postscript、可打印二进制码(pcl,rtl)等内容形式的文件。  
 
  作为unix系统管理员在与雇主签约时,雇主要求他们在给客户提供的服务中,要有一项是打印与绘图的技术支持。一个特殊的客户,他的打印环境相当复杂,包括八台36英寸hp绘图仪,一台24英寸hp绘图仪,大约20台hp deskjet 1600c绘图仪,以及大约20台hp 3si/4si/5si 打印机。所有的设备运行lpd,有postscript 驱动程序,作为网络打印机使用tcp/ip协议来访问。打印机网卡有hp jetdirect 和xcd xjet 卡各占一半。  
 
  我们在一台sun ultra enterprise 450 上运行zeh graphic systems 的 zps绘图软件,来支持几个输出多种图形格式的应用程序。我们还要支持从几个其他的应用发出的lpd命令,以及用户发自netscape应用程序的打印,还有用户在命令行下发出的打印命令。在我们的环境中,打印客户几乎都是sun的服务器和工作站,数目有近200个。我们还有一台windows nt 服务器,运行citrix winframe,也是一个打印客户。  
 
   通过zeh软件绘图的应用程序使用ultra 450 作为缓冲池,除此以外,所有其他的打印请求都从客户直接传送到网络打印机。从功能上讲,这种方式工作得很好,能够打印所有我们需要的内容形式,包括:文本,postscript,和可打印二进制文件(pcl 和 rtl)。问题是这种设置带来一个管理恶梦。像gif 或 jpeg这样庞大而又不可接受的二进制文件,经常会浪费大量的纸张,而我们又没有简单有效的办法来确定打印请求是从那里发出的。我们只知道大楼内的一台工作站或是服务器,正在向打印机发送有害的工作任务。不幸的是,标题页并不是一个选择,因为用户不愿浪费纸张(反语,哈哈?)。  
 
   我们曾经试图写一个 shell script程序,让它扫描大楼内所有的机器,并把行为报告给特定的打印机。用这些信息我们就可以执行cancel命令。但是这个方法异常缓慢,效率低下。  
 
   客户自己的技术策略显示,解决办法就是为unix环境写一个打 ?务器程序,但是策略本身没有为这种配置提供细节。另外,最近由sprint paranet做的noman(network operations management网络操作管理)评估也认为,应该开发一个打印服务器程序来满足客户的需求。  
 
   我们知道一个打印服务器是正确的选择。如果我们能够设置好它,所有的打印请求都可以从一台机器上管理,这会极大地简化整个过程。 但是这又非常困难,我们难以接受。  
 
   尝试一:试验和错误  
 

  最初,我们想建立一个标准的solaris打印缓冲。我们指定一台测试机器作为测试打印服务器,取名 nolsn099 (一台运行solaris 2.6的ultra 1) ,并开始几个测试。开始用来测试的打印机是一台hp 1600c,在 nis/dns环境中称为no1316p。  
 
   对每一种服务器设置,客户机用命令行 lpadmin -p no1316p -s nolsn099!no1316p 设置来访问打印机/绘图仪。  
 

  服务器设置1:

lpadmin -p no1316p -o protocol=bsd,dest=no1316p -t ps /
-i postscript -v /dev/null -i /usr/lib/lp/model/netstandard 
 
  结果:客户机能够打印 postscript和二进制文件,但是文本文件会出现楼梯效果。  
 
  服务器设置2:  
 

lpadmin -p no1316p -o protocol=bsd,dest=no1316p -t unknown /
-i any -v /dev/null -i /usr/lib/lp/model/netstandard 
 
  结果:客户机能够打印文本文件,但是 postscript和二进制文件打印了成垃圾。  
 
  服务器设置3:  
 
lpadmin -p no1316p -o protocol=bsd,dest=no1316p -t ps -i text /
-v /dev/null -i /usr/lib/lp/model/netstandard  
 
  结果:客户机能够打印文本文件和 postscript文件,但是rtl和pcl文件打印了成垃圾。  
 
  服务器设置4:  
 

lpadmin -p no1316p -o protocol=bsd,dest=no1316p -t unknown /
-i postscript,text,simple -v /dev/null /
-i /usr/lib/lp/model/netstandard
lpadmin -p no1316p -o protocol=bsd,dest=no1316p -t unknown /
-i any,postscript,simple -v /dev/null /
-i /usr/lib/lp/model/netstandard
lpadmin -p no1316p -o protocol=bsd,dest=no1316p -t ps /
-i any -v /dev/null -i /usr/lib/lp/model/netstandard
lpadmin -p no1316p -o protocol=bsd,dest=no1316p -t unknown /
-i text,simple -v /dev/null
-i /usr/lib/lp/model/netstandard
lpadmin -p no1316p -o protocol=bsd,dest=no1316p -t hplaser /
-i text,simple -v /dev/null
-i /usr/lib/lp/model/netstandard 
 

  结果:以上几种设置,客户机能够打印 postscript和二进制文件,但是文本文件会出现楼梯效果。  
 
  服务器设置5:  
 

lpadmin -p no1316p -o protocol=bsd,dest=no1316p -t ps /
-i any -v /dev/null -i /usr/lib/lp/model/netstandard 
 
  结果:客户机能够打印文本文件和 postscript文件,但是rtl和pcl文件打印成垃圾。  
 
   我们试验了几种组合,但是不能成功地打印所有的三种格式。lpadmin的帮助文档建议可以使用多个 -t选项。我们认为真正需要的是 -t unknown,ps选项,但不幸的是,lpadmin 不允许 unknown 同其他的选项同时出现。  
 
   我们得出结论:使用 solaris lpadmin 提供的标准选项,我们可以设置打印机来处理:

  只有文本文件

  文本和postscript

  postscript和可打印二进制文件(pcl和rtl)

  下一步我们打电话到sun的软件支持部门。很幸运我们找到一个能干的技术支持人员,他给了我们一个看起来可行的方案。尽管他承认这是一个平庸的方法,但他确认它能行。方案包括在打印服务器上为每台打印机设立两个打印队列,然后应用两个打印缓冲。第一个设备起过滤器的作用,然后将其输出定向到网络打印机。(我们采用printname printname-r的命名规则)  
 
  以下是具体步骤:  
 
  第一步:在服务器上建立过滤设备  
 
$ lpadmin -p no1316p -v /dev/null -i /usr/lib/lp/model/standard
$ enable no1316p
$ accept no1316p 
 

   第二步:编辑新创建的界面描述文件(/etc/lp/interfaces/no1316p)  
 
  找到以 case $term in 开头的 case 语句
  对 *) 条件,注释掉包含 filter=${lpcat} 的一行
  插入一行 filter=/bin/unix2dos | lp -s -d no1316p-r

  第三步:建立网络服务器(no1316p-r)  
 
$ lpadmin -p no1316p-r -o protocol=bsd,dest=no1316p -v /dev/null /
-i /usr/lib/lp/model/netstandard
$ enable no1316p-r
$ accept no1316p-r 
 
  :没有指定 -t -i 选项,使用缺省的 "unknown" " any"  
 
   完成以上步骤后,似乎打印机正常工作了。命令unix2dos修正了文本文件的楼梯现象,而且我们也能打印postscript, pcl, 和 rtl 文件。我们几乎准备庆祝了,这时我们注意到pcl和rtl 文件的打印质量极其糟糕。命令unix2dos 搞坏了二进制格式!  
 
   我们又打回电话给sun。这一次我们同几位sun的工程师开了一个电话会议,报告我们的需求和发现。他们建议使用一个lpadmin命令(我们曾经使用过)来设置打印机。当我告诉他们这会导致文本文件出现楼梯效果时,其中两个工程师心照不宣地叹息了一声。他们答应查一下。同时,我们也继续寻找答案。  
 
   尝试二:红色磁带  
 

  接下来,我们试着使用hewlett-packard的 jetadmin 软件--sun的推荐方案。我们设置一台机器作为打印服务器,然后安装jetadmin软件。棒极了。我们能够完美无缺地打印这三种文件。我们能够设置客户机,以这台服务器做打印缓冲;而且软件提供了信息,用来管理不必要的打印作业。我们找到了解决方案!错了,我们被告知不能替换xcd卡。  
 
   还没有得到sun公司的反馈信息,我们选择重新试验"double spooling"双缓冲的方案  
 
   尝试三:重试双缓冲  
 

  双缓冲方案已经接近成功,我们不能完全放弃它。只是unix2dos的限制妨碍成功。如果我们能检查输入文件,只对非二进制文件执行unix2dos,那就成了。我们写了一个简单的perl程序(unix2dos.pl)来做这些:
 
#!/opt/bin/perl
# open standard input
open(foo, "-");
# test if binary
if ( -b foo) {
while () {
print $_;
}
}

# if not binary.. perform unix2dos filtering
else {
while () {
s//n//r/n/; print;
}
}
close(foo); 
 
  界面文件中的那一行改成如下: 
 

filter=/opt/utils/unix2dos.pl | lp -s -d no1316p-r 
 
   可以了。我们可以毫无问题地打印这三种文件了。不幸,由于双重缓冲,在 no1316p-r 中的打印作业不再具有有意义的名字和所有者了。例如,当一个客户打印一个文件,比如/etc/passwd;在打印服务器上执行lpq -pno1316p ,会有以下结果:  
 
$ lpq -pno1316p
rank owner job file(s) total size
active dlister 313 /etc/passwd 522 bytes 
 

   但是当no1316p过滤了这个作业,并把它重定向到 lp -s -dno1316p-r后,在打印服务器上执行lpq -pno1316p-r ,我们会有以下结果:  
 
$ lpq -pno1316p-r
rank owner job file(s) total size
active lp 1629 1629-1 536 bytes 
 
   没有恰当指定的文件名和所有者,用户不能跟踪在打印队列里的文件,管理员也不能从队列中删除打印作业。  
 
   尝试4:找一个折衷  
 

  因为我们已经成功地打印这三种文件,现在我们需要的只是网络打印机队列中的有意义的信息。我们知道,当打印作业在过滤队列(如no1316p)中排队时,原始的文件名和用户名是可用的。我们必须找出一个办法,保存这些信息,并把它们与在实际的网络打印机队列(如no1316-r)中排队的打印作业对应起来。  
 
   我们知道,当一个作业通过过滤队列时,lp子系统会返回我们感兴趣的信息。所以显而易见,我们应该能够通过环境变量利用这些信息。我们感兴趣的变量可以用在过滤队列的界面描述文件中:${request_id}, ${user_name},${files}, 和 ${flist}。但是我们仍然面临一个问题:把这些信息同实际的网络打印机队列的lpq联系起来。我们需要一种手段,记录那些进入过滤队列的请求;还需要一个方法,把网络打印机队列中的各个项映射回初始的信息。  
 
   我们最终采用的策略是,将输入过滤到一个临时文件,文件名中包含初始的${request_id}。然后重定向该临时文件的lp,现在网络打印机队列的lpq 信息中以文件名的形式包含了初始的${request_id} 。只剩下log文件了。我们进一步修改过滤队列的界面描述文件,在if [ -z "${filter}" ]后加上以下程序:  
 
# write current request_id user_name and flist to logfile
# if the flist variable is null, we are printing a single file,
# therefore echo ${files}
# else echo ${flist}
if [ "$flist" = "" ];
then
echo "${request_id} ${user_name} ${files}" /
$gt; $gt; /var/spool/lp/save_dir/logs/request.log
else
echo "${request_id} ${user_name} ${flist}" /
$gt; $gt; /var/spool/lp/save_dir/logs/request.log
fi 
 
  我们把创建临时文件的工作并入unix2dos.pl程序中。新的程序称为lp.pl: 
 
#!/opt/bin/perl
# open standard input
open(foo, "-");
# create temp file for writing
open(out, "$#@62;/var/spool/lp/save_dir/tmp/$argv[0].$$");
# if input stream is binary do not filter
if ( -b foo) {
while () {

print out $_;
}
}
# else filter the line unix2dos style
else {
while () {
s//n//r/n/; print out;
}
}
close(foo);
close(out);
# send print request
system("/bin/lp -s -d $argv[1] /var/spool/lp/save_dir/tmp/$argv[0].$$"); 
   
  下一步,我们向lp.pl传递两个参数:${request_id}, 和网络打印机。这个程序生成一个名字为:

${request_id}."进程的pid号"的过滤后的临时文件,并lp这个临时文件。然后,我们把过滤队列界面文件中的:  
 
filter=/opt/utils/unix2dos.pl | lp -s -d no1316p-r

  这一行,替换成: 
 
filter="/var/spool/lp/save_dir/sbin/lp.pl ${request_id} no1316p-r" 
 
  让我们总结一下到了现在会发生什么:

  用户(dlister)从客户机器发送一个lp作业:lp -d no1316p /var/adm/messages

  打印服务器接受该作业,通过过滤队列处理这个请求

  过滤队列记录当前的作业号(${request_id}),用户(dlister),以及文件名(/var/adm/messages)
 
  输入送到lp.pl程序,该程序创建一个临时文件,并利用网络打印机队列执行一个lp命令。

  到此,如果在网络打印机上执行lpq,结果会如下:

$ lpq -pno1316p-r
rank owner job file(s) total size
active lp 1629 /var/spool/lp/save_dir/tmp/no1316p-1625.2974 536 bytes 
 
  现在,我们只需要一个程序来索引log文件,找到no1316p-1625,然后返回初始的所有者和文件名。我们写了如下程序:  
 
#!/bin/ksh
typeset -i n
typeset -i n2
# set n2 equal to the default number of fields per log file entry
n2=3
if [ $# -ne 1 ]
then

echo "usage: $0 network_printer_queue"
exit 1
fi
echo "rank owner job file(s) total size"
/usr/ucb/lpq -p $1 | grep bytes | while read line
do
rank=`echo $line | awk "{print $1}"`
job=`echo $line | awk "{print $3}"`
size=`echo $line | awk "{print $5}"`
temp=`echo $line | awk "{print $4}"`
grepstuff=`basename $temp | awk -f. "{print $1}"`
owner=`grep -w $grepstuff /var/spool/lp/save_dir/logs/request.log /
| awk "{print $2}"`
# count the number of fields in the log file: 3 means a single file
# printed more than
# 3 means that several files were sent at once.
n=`grep -w $grepstuff /var/spool/lp/save_dir/logs/request.log /
| awk "{print nf}"`
if [ $n -gt 3 ]; then
file=`grep -w $grepstuff /var/spool/lp/save_dir/logs/request.log /
| awk "{print //$$n2}"`
echo "$rank $owner $job $file $size bytes"
n2=n2+1;
else

n2=3
file=`grep -w $grepstuff /var/spool/lp/save_dir/logs/request.log /
| awk "{print $3}"`
echo "$rank $owner $job $file $size bytes"
fi
done 
 
   这段程序只要一个参数:打印服务器队列的名字。在我们的例子中,我们只需要输入 $ lpq.ksh no1316p-r 就可以显示以下结果:  
 
rank owner job file(s) total size
active dlister 1629 /var/adm/messages 536 bytes 
 
用这段程序,就可以给用户提供用来跟踪他们的打印作业的信息,打印服务支持人员可以在打印服务器上更好地管理打印作业。  


  清尾:  
 
  最后,我们需要一个手段来删除在/var/spool/lp/save_dir/tmp中创建的临时文件。我们注意到:一旦一个打印作业通过lp子系统成功执行,在/var/spool/lp/logs/requests.log文件中会出现一项 t filename。  
 
   为清除临时文件,我们写了一个程序称为cleanup.ksh:  
 
#!/bin/ksh

cd /var/spool/lp/save_dir/tmp
unalias ls
# our temp files are all begin with "no"
if [ -f no* ];
then
for i in `ls no*`
do
grep "^t $i" /var/spool/lp/logs/requests $#@62; /dev/null 2$#@62;&1
if [ $? -eq 0 ];
then
rm -f $i
fi
done
fi 
  
  我们在打印服务器上设置一个corn作业,每小时运行一次以上程序。  
 
  结论  
 
  我们完成了最初的目标,尽管该解决办法可以进一步精炼:

  通过一个中央服务器进行缓冲

  使用单一的打印机名,三种内容形式

  (文本,postscript,pcl/rtl)的文件都可以正确打印

  能够在整个过程中跟踪打印请求
  
  能生成有意义的lpq信息

  集中并简化打印环境

  没有任何额外花费就完成了任务

  另外,我们相信本文描述的改进完全可以融合到solaris 2.6的打印子系统中,使用户不再需要第三方的软件和硬件解决方案。

扫描关注微信公众号