Skip to content

Include

An include directive reads another file into the ledger, so a ledger can be split into several files, for example one per year or month. A pattern with * includes every matching file.

include "<Path>"
Part Required Description
"<Path>" yes The file to read, in double quotes. A relative path is relative to the directory of the file that holds the include. Any part of it may contain *, see Wildcards.

An include has no date and no metadata. It can be written in any file of the ledger, at any position.

include "accounts.zhang"
include "data/2024/*.zhang"
include "data/*/*.zhang"

With a main file at the ledger root, these lines read accounts.zhang at the root, every file of data/2024 whose name matches *.zhang, and the matching files of every directory under data.

  • A relative path is resolved against the directory of the file that holds the include: include "sibling.zhang" in data/2024.zhang reads data/sibling.zhang.
  • Every included file is read in the format of the main file, whatever its own extension: a ledger whose main file is main.zhang reads every file as zhang text, one whose main file is main.bean reads every file as Beancount text.
  • A file that does not exist is read as an empty file, without an error. It still appears in the file list of the web UI. Check the path when the entries of an included file do not show up.

* stands for any run of characters other than /, within one part of the path, and can be used in any part, more than once in a part if needed (2024-*-*.zhang). Every other character is taken literally, and a part matches a whole name: *.zhang matches 01.zhang, not 01.zhang.bak, and report(*).zhang matches report(1).zhang.

  • data/*.zhang matches the files directly in data, and *.zhang in the main file at the ledger root matches the files next to it.
  • data/*/*.zhang matches the files one directory below data. A part without * can come before or after a part with one, as in data/*/archive/*.zhang or data/*/accounts.zhang.
  • The last part names files, the parts before it directories. A * at the start of a part does not match a hidden name, one starting with ., as in a shell: *.zhang leaves an editor’s .#01.zhang out.
  • The matching files are read in the order of their names.
  • A pattern that matches no file includes nothing, without an error.

Each file is read once, however many times it is included. A file that includes the main file, or two files that include each other, are not a problem.

Directives are ordered by their dates, so the order of files only matters for undated directives. Zhang reads the main file first, then the files it includes in order, then the files those include, and so on. When the same option is set in several files, the value read last wins.

With a ledger on the local disk, zhang serve watches the ledger root and reloads the ledger when one of its files changes. A new file that matches a pattern, or a missing included file that is created, is read at the next reload: when a file of the ledger changes, or when you choose Reload ledger in the web UI. Ledgers on S3, WebDAV or GitHub are not watched; reload them from the web UI. Includes and patterns work the same on every data source.

The web UI writes new entries into the file that the directive_output_path option selects. If that file is not part of the ledger yet, Zhang creates it and appends an include of it to the main file.

An include produces no ledger error. A file that cannot be parsed stops the ledger from loading, with an error naming the file, line and column.

Beancount’s include has the same syntax and also accepts patterns. Zhang differs:

  • Beancount reports an include that matches no file. Zhang reads a missing file as empty, without an error.
  • Beancount’s patterns follow Python’s glob rules. Zhang’s * works the same way, but ? and [...] are not special in Zhang: they match themselves.