Query Font Faces Safely with fc-query
fc-query opens a font file and prints every face it finds, without installing or altering anything. You will pick a single face by index, pull specific fields for a script, and learn about a documentation trap that catches out the -b option on this machine. The examples use Fontconfig 2.15.0 from the installed fontconfig package.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about ten minutes. You need a readable font file and a normal shell; none of the commands here needs elevated privileges.
1. Confirm the installed command
Check the binary and package version first. A copied example against the wrong Fontconfig release fails silently far more often than it fails loudly:
$ command -v fc-query
/usr/bin/fc-query
$ fc-query --version
fontconfig version 2.15.0
$ dpkg-query -W -f='${Package} ${Version}\n' fontconfig
fontconfig 2.15.0-1.1ubuntu2
Fontconfig describes fc-query as a query over one or more font files: it prints a pattern for each face it finds. It does not install a font, rebuild the cache, or touch the file.
Checkpoint
If command -v prints nothing, install the package through your normal system administration process. Do not reach for sudo just because a font query failed.
2. Query every face in a font file
Pass a font path as the final argument. This example uses a font shipped by the local system:
$ FONT='/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf'
$ fc-query "$FONT"
Pattern has 26 elts (size 32)
\tfamily: "DejaVu Sans"(s)
\tfamilylang: "en"(s)
\tstyle: "Book"(s)
\tstylelang: "en"(s)
\tfullname: "DejaVu Sans"(s)
...
The full pattern carries far more than this excerpt shows, including the file path, language coverage, character set and font-format details. Exact field order and the element count are output details, not an API, so do not parse the pretty-printed form in a script.
Without --index, the command queries every face in each file. That matters for collections and variable or multi-face files, where one path does not mean one result. Give several paths to compare files in one go:
$ fc-query /path/to/regular.ttf /path/to/bold.ttf
Replace both placeholders with real readable paths. A missing file is an error, and the command returns status 1 if any font face could not be opened.
3. Select one face with an index
Use -i or --index when you need one face out of each input file:
$ fc-query --index 0 "$FONT"
Pattern has 26 elts (size 32)
\tfamily: "DejaVu Sans"(s)
\tstyle: "Book"(s)
...
- Indexes are zero-based. The index selects a face within each supplied file, not a global result number.
- A missing index is an error, not an empty result. Query without
--indexfirst, or use a known collection layout, before you test the exact index you intend to use.
$ fc-query --index 1 -f '%{family}\n' "$FONT"
Can't query face 1 of font file /usr/share/fonts/truetype/dejavu/DejaVuSans.ttf
$ printf 'status: %s\n' "$?"
status: 1
4. Ask for stable fields with a format string
For automation, use -f or --format. Fontconfig pattern fields are written as %{field}; put the newline inside the format string:
$ fc-query --index 0 -f '%{family}\t%{style}\t%{file}\n' "$FONT"
DejaVu Sans Book /usr/share/fonts/truetype/dejavu/DejaVuSans.ttf
That is far easier to consume than the human-readable pattern. Shell quoting protects the format string and leaves the percent signs for fc-query. Mind your delimiter: a font family can contain spaces, so splitting on spaces is unsafe.
A field-only query works for a quick check too:
$ fc-query -i 0 -f '%{family}\n%{style}\n%{index}\n' "$FONT"
DejaVu Sans
Book
0
If a field is absent, or differs between Fontconfig releases, the formatted result may not be a complete identity on its own. In a script, check the command status and validate the fields you actually require rather than treating any non-empty output as a match.
5. Use brief output only for a human glance
The installed command accepts -b and --brief to shorten the pattern display:
$ fc-query --brief "$FONT"
Pattern has 26 elts (size 32)
\tfamily: "DejaVu Sans"(s)
\tfamilylang: "en"(s)
\tstyle: "Book"(s)
...
Here is the version-sensitive trap: the installed help for Fontconfig 2.15.0 calls the long option --brief, while this machine's older local fc-query(1) page labels -b as --ignore-blanks. On this installation --ignore-blanks is rejected as an unknown option. Trust fc-query --help and the binary you are actually running over documentation that disagrees with it. The short option -b still works.
6. Handle failures without changing state
Capture the status immediately when a query runs inside a script:
if output=$(fc-query -i 0 -f '%{family}\n%{style}\n' "$FONT"); then
printf '%s\n' "$output"
else
status=$?
printf 'fc-query failed with status %s\n' "$status" >&2
exit "$status"
fi
Status 0 means the requested font data parsed successfully. Status 1 means an error occurred, or at least one face could not be opened. Neither tells you that the font suits a particular application, that it is enabled in a desktop session, or that a cache is current.
For a path problem, check readability and file type without altering anything:
$ test -r "$FONT" && echo readable
readable
$ file "$FONT"
/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf: TrueType Font data
Recovery
If you need to change installed fonts or rebuild Fontconfig's cache, stop here and follow a separate change procedure with a backup and rollback plan. fc-query itself has no undo operation because it never writes anything.
Done means
- Confirmed version and path. You know the local
fc-queryexecutable and its version. - Queried a real font file. No elevated privileges were needed.
- Checked before indexing. You used a zero-based index only after confirming the face exists.
- Used -f for scripts. You did not parse the display pattern.
- Checked the exit status. Status 1 was treated as a query failure, not ignored.
- Kept it read-only. Font installation and cache changes stayed out of this check.