Using a Printer from the Terminal
Using the command line to interact with a printer is surprisingly straightforward. You configure your printer by IP address, and then use it. Simplicity itself. Surprisingly, I'm even finding it more convenient than fighting with GUI printer setup systems. It feels like I have a lot more control, and direct access to a lot more useful information. Not to mention it opens the door to using my printer as an output for scripts or commands!
Assign a static IP address to the printer
Before we configure our computer to talk to the printer over the command line, the first thing you should do is configure your printer with a static IP address. While this step isn't strictly necessary to get things working, if you skip it, things will eventually break. We'll be configuring our printer using its IP address, and so if the printer is on DHCP there is always the chance that its IP address may change, which will break things.
I can't go into the details on how to set a static IP for your printer, as it will vary from device to device. In general, you'll want to go into your printer's network settings, and manually set its IP address in there. You should also log into your router and ensure that the IP address you gave the printer is not in the range of addresses that your router can assign for DHCP. If you don't do this, your router may assign a different device this IP address as well, which can lead to some very annoying, and very difficult to diagnose, network problems. I typically configure my router to not use addresses greater than 192.168.0.199 for DHCP, which leaves 192.168.0.200 and above safe for static assignments.
Install the necessary packages
Your Linux distribution may or may not have printing-related packages
pre-installed. If it doesn't, you'll need to install them. There are two
common systems used for printing, the original lpd print
daemon, or the more modern CUPS (Common Unix Printing System). We'll use
CUPS here, as this is readily available on most Linux distributions, as
well as BSDs.
On Debian, the necessary packages can be installed using,
sudo apt install cups cups-browsed
Additionally, if you are using an older device that doesn't support
driverless printing, you can install the printer-driver-all
package to get an assortment of drivers, one of which will hopefully
work for you. I'm not going to go into drivers in this post, though; my
focus is strictly on driverless printers.
Configuring your Printer
Once you have the necessary packages installed, you'll need to configure the environment for printing and add your printer. This can all be done from the command-line using a variety of utilities that were installed alongside CUPS.
These tools are,
lpadmin(8)-- Used to add and configure printerslpstat(8)-- Used to view CUPS status informationlpoptions(8)-- Used to configure the default options for a printerlp(1)-- Used to print files
First off, you'll want to confirm that CUPS is running. You can do
this using the lpstat command, with the -t
flag to make it show all available information.
lpstat -t
scheduler is running
Next, you need to add your network printer. You can do this via its IP address. If you need to look it up, the printer will usually have this information within its network settings.
Once you have the address, you can add the printer using
lpadmin as root,
lpadmin -p PRINTER_NAME -E -v ipp://IP_ADDRESS/ipp/print -m everywhere
The -p flag in the above command specifies the "name" of
the printer. This is the name you will use to specify the printer in
future commands, and is also the name that will appear in graphical
print dialogs. The -E flag enables the printer and sets it
up to accept new jobs. The -v flag specifies the URI of the
printer (i.e., where it is on the network), and the -m flag
specifies the driver; here we use the "everywhere" flag, which specifies
driverless printing.
Now, you should be able to run lpstat -t again and see
the printer you added on the list,
lpstat -t
scheduler is running
device for PRINTER_NAME: ipp://IP_ADDRESS/ipp/print
PRINTER_NAME is accepting requests since ...
printer PRINTER_NAME is idle. enabled since ...
Finally, we want to set this printer as the default. There are
several different ways to do this, however the simplest way is to use an
environment variable. When you use lp(1), it will first
examine the PRINTER environment variable, before checking
the default configured using lpoptions(8), and the finally
the default configured by lpadmin(8).
To set the printer as your default, add this line to your shell's profile/rc file,
export PRINTER=PRINTER_NAME
You can also run this command manually in your current terminal to
temporarily change the default printer. If you want to override the
default for a single command, most of the printer-related commands that
need a specified printer will allow you to state the printer by name,
using the -d <name> flag.
Issuing a print job
Now that we have a printer configured, let's use it to print
something. The lp(1) command is used to print files. With a
default printer configured, it's simplest usage is,
lp FILE_TO_PRINT
It will also accept text on a pipeline, so you can pipe the output of a command directly to your printer,
ls -lah | lp
Print options
Of course, the default print settings may not be what you want. So you are able to specify options for print quality, paper size, whether the document should be printed single or double sided, etc.
The options that are available will vary from printer to printer. The
lpoptions(8) command can be used to list them for your
default printer (or -d can be used to specify a different
one, by name),
lpoptions -l
PageSize/Media Size: 3x5 A4 A5 A6 Env10 EnvC5 EnvDL EnvMonarch Executive FanFoldGermanLegal ISOB5 Legal *Letter Custom.WIDTHxHEIGHT
InputSlot/Media Source: *Auto Manual Tray1
MediaType/Media Type: *Stationery StationeryLightweight StationeryHeavyweight StationeryCover Envelope EnvelopeHeavyweight EnvelopeLightweight StationeryRecycled Labels StationeryBond
cupsPrintQuality/cupsPrintQuality: Draft *Normal High
ColorModel/Output Mode: *Gray
Duplex/Duplex: *None DuplexNoTumble DuplexTumble
OutputBin/OutputBin: *FaceDown
This listing shows the available configurable parameters, the possible values for them, and the current default (indicated with an asterisk).
You can also use the lpoptions(8) command to change the
default values. For example, to change the default print quality to
"High" (which you almost certainly want to do, the lower quality
settings are pretty poor, especially for PDF documents), you would
run,
lpoptions -o cupsPrintQuality=High
These options can also be specified at print time using the
lp command directly, with the same
-o option=value syntax. So to print a document two-sided,
with the print settings above, you could run the following [1]
lp -o Duplex=DuplexNoTumble document.pdf
I've noticed that it will sometimes take a shockingly long amount of time for the printer to actually print in Duplex, but it does eventually get there. I haven't looked into why this occurs; if you happen to know, feel free to shoot me an email, I'd appreciate it.
Formatting text for printing
Linux comes with some utilities that are very helpful for pre-processing text for printing. For example, let's say we wanted to print out the source code of a program that we've been working on. We could do this directly, using
lp program.c
But the resulting output won't be super pretty. The margins will be extremely tight, it might not handle wrapping lines the way that you expect, etc.
The pr(1) utility is designed for preprocessing text
files being directed to a printer. It has a huge number of
features--I highly suggest that you check out its manual. For our
purposes here, let's use it to add a slight margin, and line numbers
(always helpful when looking at code). [2]
pr -n -o 5 -f program.c | lp
This will add a header, page numbers, a margin of 5 spaces, as well
as line numbers, to the file. The -f flag tells
pr(1) to send a formfeed, rather than 5 blank lines, at the
end of every page. The default behavior of adding the blank lines will
often cause the printer to spit out an extra blank page, so the
-f flag helps avoid this issue.
However, you will still see ugliness in the output when a line needs
to wrap, as the wrapped part of the line will ignore the margin.
Additionally, these wrapped lines aren't accounted for by
pr(1) when it is paginating, and so can result in the pages
output by the printer not lining up with the pages laid out by
pr(1). You can fix this manually, or use another helpful
utility, fmt(1) [3], to automatically break long lines
prior to passing the code file into pr(1) [4].
fmt -s -w70 program.c | pr -n -o 5 -F | lp
The -s flag here tells fmt to only split
long lines. Otherwise, it will try to merge short lines together as
well, which is fine for prose but not at all what we want for code. The
-w flag is used to specify the maximum width of the
output.
The reason for picking 70 for the line width here is because the
standard width of a sheet of paper is 80 characters. We are using a 5
character margin, and the -n flag in pr(1)
also uses 5 characters, so our content can be at most 70 characters
long.
Conclusion
And that's really all there is to it. Just a couple commands, and
you're off to the races. I just started playing around with this myself,
and I'm finding it to be surprisingly convenient compared to the
graphical interfaces. Plus, it's fun to finally have an excuse to play
around with some of the text-file processing commands like
pr(1) and fmt(1) that I'd read about but never
needed to use.
I've been playing around the command-line interfaces to SANE too, for scanning documents. That one requires a little more manual work, so I may do a post or video about using them at some point too.