Scan Font Directories Safely with fc-scan

fc-scan digs through a font file or a whole directory tree and prints a pattern for every face it finds. Allow about ten minutes. You need Fontconfig and a readable font path; none of the commands below needs sudo, and the scan never edits the input.

Worth knowing before you start: this machine's local fc-scan(1) page is an older generated page dated 15 January 2009. It documents recursive scanning and -f, but not every option the installed 2.15.0 binary actually has. Where the two disagree, trust the installed help and behaviour.

1. Check the installed command

Start with read-only version and help checks, to confirm which executable and package you are about to use:

$ command -v fc-scan
/usr/bin/fc-scan
$ fc-scan --version
fontconfig version 2.15.0
$ dpkg-query -W -f='${Package} ${Version}\n' fontconfig
fontconfig 2.15.0-1.1ubuntu2
$ fc-scan --help
usage: fc-scan [-bcVh] [-f FORMAT] [-y SYSROOT] [--brief] [--format FORMAT] [--version] [--help] font-file...

Checkpoint: If command -v finds nothing, install or repair Fontconfig through your normal package-management process. A manpage on disk does not prove the matching executable is on your PATH.

2. Scan one known font

Give the command a font file and it prints a Fontconfig pattern for each face it finds. A TrueType collection or another multi-face file can produce more than one pattern from a single path:

$ fc-scan /usr/share/fonts/truetype/dejavu/DejaVuSans.ttf
Pattern has 28 elts (size 32)
        family: "DejaVu Sans"(s)
        style: "Book"(s)
        file: "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"(s)
        index: 0(i)(s)
        outline: True(s)
        scalable: True(s)

The complete pattern is deliberately verbose: family and style names, weight, width, language coverage, file path, face index and format-specific properties can all show up. Treat it as diagnostic output, not a stable report; fields vary with the font, the Fontconfig version and the configuration.

Checkpoint: Confirm the path and face you expected. file is the scanned path; index identifies a face within a collection or other multi-face resource.

3. Scan a directory recursively

Pass a directory to inspect a whole font tree, subdirectories included:

$ fc-scan /usr/share/fonts/truetype/dejavu > /tmp/dejavu-font-scan.txt
$ sed -n '1,12p' /tmp/dejavu-font-scan.txt
Pattern has 28 elts (size 32)
        family: "DejaVu Math TeX Gyre"(s)
        familylang: "en"(s)
        style: "Regular"(s)

None of this touches configuration. The scan reads the supplied path and prints patterns; it does not install a font or make anything available to applications.

4. Select fields with a format string

Use --format or its short form -f when another program needs specific pattern fields. Put a newline in the format so each face lands on one line:

$ fc-scan --format='%{family}\t%{style}\t%{file}\n' /usr/share/fonts/truetype/dejavu/DejaVuSans.ttf
DejaVu Sans    Book    /usr/share/fonts/truetype/dejavu/DejaVuSans.ttf

%{family}, %{style} and %{file} ask for pattern properties. Quote the format so the shell passes the braces, tabs and newline marker through unchanged. If a property is absent or multi-valued, the result depends on that pattern; do not read an empty field as proof the font lacks metadata.

For a quick readable check, the installed 2.15.0 binary also accepts --brief:

$ fc-scan --brief /usr/share/fonts/truetype/dejavu/DejaVuSans.ttf
Pattern has 26 elts (size 32)
        family: "DejaVu Sans"(s)
        style: "Book"(s)

Brief output is still a Fontconfig pattern, not a promise of one line or a fixed field set. Check fc-scan --help on older systems before using this option in a portable script; the local older manpage does not mention it at all.

5. Use a sysroot only when the path is real

Fontconfig 2.15.0 includes -y and --sysroot. It prepends a root to paths for scanning, handy when examining a mounted or staged filesystem, but it does not turn an arbitrary host directory into a valid target:

$ fc-scan --sysroot=/mnt/staged-root /usr/share/fonts/truetype/dejavu/DejaVuSans.ttf

Before running that, replace /mnt/staged-root with a directory that actually contains the requested path, and check it without elevation:

$ test -r /mnt/staged-root/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf && echo readable
readable

--sysroot is not the same thing as changing the host's font directories. It affects only this scan invocation. A wrong root usually leads to an empty result or an error, not a scan of the host file you meant to avoid.

6. Diagnose an empty or failed scan

Check the input path and permissions first.

$ ls -ld /path/to/font-or-directory
$ test -r /path/to/font-or-directory && echo readable

Then verify the status right after the command. The local manpage says the return code is 0 if at least one font was found and 1 otherwise:

$ fc-scan /tmp/definitely-no-such-font >/tmp/fc-scan-output
$ status=$?
$ printf 'fc-scan status: %s\n' "$status"
fc-scan status: 1

Status 1 under that documented contract means the scan found no font. It can mean a wrong path, unreadable input, a non-font file, or a font excluded by Fontconfig's own scanning rules. Read the path and diagnostics before touching configuration. If a directory scan unexpectedly skips a file, test that file directly and check its permissions and format.

For scripts, keep machine-readable output separate from diagnostics. Redirecting standard output to a report is fine, but never parse the full pattern as if field order were an API. Prefer a small explicit --format template, and record the Fontconfig version alongside the report.

Done means