Let grog Work Out Your groff Command Line

grog reads a roff file and hands you the exact groff command it needs, macros and preprocessors included. The examples use GNU grog 1.23.0 from the installed groff-base package, version 1.23.0-3build2. Allow about fifteen minutes. You need a shell, a readable roff input file, and the grog command itself. You do not need sudo, and running grog edits nothing and installs nothing.

1. Check the installed command

Confirm which binary will run and record its version. These are read-only checks:

$ command -v grog
/usr/bin/grog
$ grog --version
GNU grog (groff) 1.23.0
$ dpkg-query -W groff-base
groff-base 1.23.0-3build2

The version matters because grog is an inference tool, not a complete roff parser. Its detection rules and the surrounding groff installation can change between releases.

Checkpoint: If command -v grog finds nothing, stop here and install the package through your normal system-management process. Do not copy a command from another machine and assume its preprocessors are installed locally.

2. Ask grog to inspect a document

Pass the input file as an operand. grog prints the inferred command; it does not format the document at this stage:

$ grog /path/to/document.roff
groff -ms /path/to/document.roff

The result depends on the document: ms macros typically produce groff -ms, a man page produces groff -man, and a table, equation or picture adds -t, -e or -p respectively. Treat the output as a proposed command, not a verdict, and read it before you run it.

For a small local test, create a file without touching any system path:

$ printf '.TH DEMO 1\n.SH NAME\ndemo \\- test\n.SH DESCRIPTION\nA small test.\n' > /tmp/demo.1
$ grog /tmp/demo.1
groff -man /tmp/demo.1

Here grog recognised the man macros straight from the file. Its output is plain text, so you can drop it into a shell script, a build rule or a review comment once you have checked the path and options.

3. Include groff options explicitly

Options placed before the file operand are copied straight into the inferred command, which matters when the output device or another groff setting is part of what you actually want:

$ grog -Tascii /tmp/demo.1
groff -Tascii -man /tmp/demo.1

The -- marker makes the boundary clear when a file name begins with a hyphen:

$ grog -Tascii -- /path/to/-draft.roff
$ grog -k -Tascii /path/to/utf8-document.roff

That does not convert the file or touch its encoding; it only changes the command grog proposes.

4. Check preprocessors hidden behind .so

grog works by matching recognisable strings in the input. It does not recursively open every file named by a .so request, so a preprocessor used only in an included file can slip past it entirely.

Compare the direct inspection with a version passed through soelim first:

$ grog /path/to/top-level.roff
groff -ms /path/to/top-level.roff
$ soelim /path/to/top-level.roff | grog
groff -t -ms -

If the second result adds an option such as -t, the source tree probably needs soelim in the real build too. Check how your build handles included paths before turning this comparison into a permanent command; soelim is never added automatically just because a file contains .so.

5. Use standard input deliberately

With no file operand, or with an operand of -, grog reads standard input and represents it as - in the proposed command:

$ printf '.PP\nstdin test\n' | grog -
groff -ms -

Handy in pipelines, but remember the inferred command also expects its input from standard input. A command copied out and run later will not see the original pipeline unless you reproduce it or save the intermediate file.

6. Run the inferred command only after review

--run writes the inferred command to standard error and then executes it. This is the point where output files, diagnostics and any shell-visible side effects from groff start to matter:

$ grog --run -Tascii /tmp/demo.1 > /tmp/demo.txt
$ head -n 8 /tmp/demo.txt
DEMO(1)                    General Commands Manual                    DEMO(1)

Check the inferred command first with ordinary grog. Do not run --run on untrusted input without considering included files, output paths and the groff options in your invocation. Shell redirection can truncate an existing destination before groff even starts, so write to a new or temporary file and rename it only after inspection.

The man page notes that groff's exit status is discarded when --run is used. If status matters for automation, run and check the printed command yourself rather than trusting a successful grog invocation as proof of a complete render.

7. Read failures as inference limits

Status 1 means grog thinks a macro package is in use but cannot tell which one. Status 2 means trouble handling an option or operand. Status 0 can also just mean no preprocessor or macro package was needed, which is not proof the input is valid or that the output looks right.

grog does not parse every roff control structure. Conditional requests, line continuations, changed control characters and some preprocessor regions can all hide the macros it is looking for. If the proposed command looks unexpectedly bare, inspect the source near those constructs and supply the missing groff option yourself. Keep the original file unchanged while you diagnose it.

Done means