Validate Netpbm Streams Before They Reach a Converter
You will finish with a small pipeline that rejects a broken Netpbm image before a later converter can create a partial output file. The examples use pamvalidate from Netpbm 11.5.2, provided here by Debian package netpbm 2:11.05.02-1.1build1.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about fifteen minutes. You need a shell, a readable Netpbm image such as PPM or PAM, and a destination where you can write test output. The validation itself is an ordinary unprivileged command. Do not use sudo unless a separate file-permission problem genuinely requires it.
1. Confirm the installed command
Check which executable will run and record the local library version. These commands only inspect the installation:
$ command -v pamvalidate
/usr/bin/pamvalidate
$ pamvalidate --version
pamvalidate: Using libnetpbm from Netpbm Version: Netpbm 11.5.2
pamvalidate: Built from source dated 2024-03-31 09:09:47
...
The command has no options specific to pamvalidate. Its input comes from standard input and its validated copy goes to standard output. For the complete option set shared by Netpbm programs, use man pamvalidate. Do not treat an option-looking image filename as an argument: pamvalidate does not take an input path.
Checkpoint
If command -v finds nothing, install Netpbm through your normal package-management process or fix PATH. Do not download a replacement binary into a production pipeline without checking its provenance.
2. Validate an image into a new file
Use shell redirection to save the validated stream. This leaves the source untouched:
$ pamvalidate < /path/to/input.ppm > /tmp/input-validated.ppm
$ status=$?
$ printf 'pamvalidate exit status: %s\n' "$status"
pamvalidate exit status: 0
$ test -s /tmp/input-validated.ppm && echo 'validated output is non-empty'
validated output is non-empty
Exit status zero means that pamvalidate read the stream successfully and copied it. The output is still the same Netpbm image format and data; this command does not resize, repair, optimise or convert it. The temporary destination is deliberate. Shell redirection truncates an existing destination before the program starts, so do not point it at a useful original or final file while testing.
For a quick local smoke test, create a tiny PPM in a disposable directory:
$ printf 'P3\n1 1\n255\n255 0 0\n' > /tmp/red.ppm
$ pamvalidate < /tmp/red.ppm > /tmp/red-validated.ppm
$ cmp --silent /tmp/red.ppm /tmp/red-validated.ppm && echo 'copy matches input'
copy matches input
cmp is a useful check here because validation should not alter a valid stream. Remove these disposable files later with an explicit command if you no longer need them. That deletion is irreversible.
3. Put validation before a converter
The useful case is a pipeline in which the next program should not see any input until the whole source has passed the format checks. For PPM to PNG conversion, use:
$ pamvalidate < /path/to/input.ppm | pnmtopng > /tmp/output.png
$ status=$?
$ printf 'pipeline exit status: %s\n' "$status"
pipeline exit status: 0
On this machine, pnmtopng is the separate Netpbm converter. Replace it with the converter you actually intend to run, and verify that it accepts the input format. pamvalidate does not create PNG output itself.
The protection is about failure before downstream processing. If the source is truncated, pamvalidate fails without producing output, so pnmtopng receives no image to convert. If your shell does not make a pipeline report the first command's status, enable its pipeline-failure option before relying on the final status:
$ set -o pipefail
$ pamvalidate < /path/to/input.ppm | pnmtopng > /tmp/output.png
$ printf 'pipeline exit status: %s\n' "$?"
pipeline exit status: 0
pipefail makes a non-zero status from any pipeline component visible to the shell. It does not remove a file already created by redirection, and a converter might still create an empty or partial destination before it notices an error. For an important output, write to a new temporary name and rename it only after the pipeline succeeds.
4. See what a rejected stream looks like
A truncated raster is one of the failures the command detects. This deliberately incomplete one should fail and leave an empty redirected output:
$ printf 'P3\n1 1\n255\n255 0\n' | pamvalidate > /tmp/truncated.ppm
pamvalidate: EOF / read error reading a byte
$ printf 'exit=%s bytes=%s\n' "$?" "$(wc -c < /tmp/truncated.ppm)"
exit=1 bytes=0
A sample above the declared maximum is rejected too:
$ printf 'P3\n1 1\n10\n11 0 0\n' | pamvalidate > /tmp/out-of-range.ppm
pamvalidate: Plane 0 sample value 11 exceeds the image maxval of 10
$ printf 'exit=%s bytes=%s\n' "$?" "$(wc -c < /tmp/out-of-range.ppm)"
exit=1 bytes=0
The exact diagnostic can vary with the failure and build, but a non-zero status is the reliable signal. Do not replace a rejected source with pamfix automatically: the manual identifies pamfix as a salvage tool, which is a different decision from validation. Keep the original and investigate what produced the invalid stream.
5. Account for multi-image input
Netpbm input can contain more than one image. pamvalidate validates and copies the corresponding multi-image output stream; it does not select the first image or split the stream. If a later stage expects one image, check that stage's contract separately.
$ cat > /tmp/two.ppm <<'EOF'
P3
1 1
255
255 0 0
P3
1 1
255
0 255 0
EOF
$ pamvalidate < /tmp/two.ppm > /tmp/two-validated.ppm
$ cmp --silent /tmp/two.ppm /tmp/two-validated.ppm && echo 'both images copied'
both images copied
The here-document changes only the disposable test file. It does not require elevated privileges or alter Netpbm configuration.
6. Make the final write recoverable
Validation prevents a bad stream reaching the converter, but it cannot make an unsafe destination safe. Before replacing an existing image, use a new path and an explicit backup:
$ cp --preserve=all /path/to/output.png /path/to/output.png.bak
$ set -o pipefail
$ if pamvalidate < /path/to/input.ppm | pnmtopng > /path/to/output.png.new; then
> mv /path/to/output.png.new /path/to/output.png
> else
> printf 'conversion failed; original output remains\n' >&2
> fi
The guarded mv runs only after the pipeline has returned zero. If it fails, leave the original in place and remove the incomplete .new file after checking it is the intended temporary path. To undo a completed replacement, stop and check the paths, then run mv /path/to/output.png.bak /path/to/output.png. Do not delete the backup until the new file has been opened or otherwise verified.
Done means
pamvalidateis the expected Netpbm 11.5.2 executable, or you have recorded your installed version.- Input is supplied on standard input and validated output is captured separately.
- A truncated stream or out-of-range sample produces a non-zero status and no validated bytes.
- Validation runs before the image converter, with
pipefailenabled where pipeline status matters. - Multi-image streams are handled deliberately rather than silently reduced to one image.
- Important replacements use a new destination and retain a recoverable backup.