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.
The route
Jump straight to the step you need, or tick off Done means at the end.
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)
- Do not expect a fixed first font. Output order and pattern count depend on the directory contents and installed configuration.
- Save the report before you overwrite it. Shell redirection with
>truncates an existing destination beforefc-scaneven starts; use a new name or copy the old report first.
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
- Confirmed the executable.
fc-scan --versionidentified it and the Fontconfig version. - Scanned a known font. The pattern contained the expected family, style and path.
- Treated a directory scan as recursive. Output was saved without blindly overwriting a useful report.
- Kept scripts stable. A quoted
--formatstring was used instead of relying on verbose field order. - Checked failures properly. A failed or empty scan was diagnosed by exit status, path and permissions before any configuration change.