Skip to content
bili

Output formats

Every output format, how to narrow columns, and how to template rows.

Every list command renders through the same formatter. Pick a format with -o, or let bili choose: a table when writing to a terminal, JSONL when piped.

Formats

bili search lofi -o table     # aligned columns for reading
bili search lofi -o list      # one short named section per record
bili search lofi -o markdown  # a pipe table that pastes into an issue
bili search lofi -o jsonl     # one JSON object per line, for piping
bili search lofi -o json      # a single JSON array
bili search lofi -o csv       # spreadsheet friendly
bili search lofi -o tsv       # tab-separated
bili search lofi -o url       # just the URL column
bili search lofi -o raw       # the underlying record as pretty-printed JSON
Format Best for
table Reading on a terminal
list Reading one record top to bottom, and it streams as records arrive
markdown Pasting into an issue or a README
jsonl Piping into another tool, one object at a time
json Loading a whole result as an array
csv / tsv Spreadsheets and quick column math
url Feeding URLs into other commands
raw The full record, pretty-printed

There is no yaml. It was an undocumented alias and was removed in v0.1.1; pipe -o jsonl through yq if that is what you want.

Rich, lossless records

bili keeps the fields the API returns rather than flattening them. The table, list, markdown, csv and url views are readable projections; json, jsonl and raw are the complete record. When in doubt about what a command knows, ask for JSON:

bili video BV17x411w7KC -o json | jq 'keys'

Narrowing columns

Keep only the fields you want, in the order you list them:

bili search lofi --fields bvid,title,view_count

--no-header drops the header row in table and csv output, which is handy when a downstream tool expects bare rows.

Templating rows

For full control over each line, apply a Go text/template. The template runs against the record as JSON, so the names are the JSON keys exactly as they appear in -o json, lower case and underscored, not the Go field names:

bili search lofi --template '{{.bvid}} {{.title}}'
bili video BV17x411w7KC --template '{{.title}} has {{.view_count}} views'

A name that is not a key renders as <no value> rather than failing, so check a template against -o json | jq keys when a line comes out empty.

The envelope

Every record carries an envelope describing the reading rather than the thing read: which endpoint answered, whether the request was signed, what state the response was sorted into, when, and the size of the body it came out of.

bili user 946974 -o jsonl | jq .envelope

It is hidden from table and csv by default, because provenance is worth having on every record and worth a column on none of them. Ask for it by name when you want it:

bili user 946974 -o table --fields name,envelope

Fields that are not there

envelope.missed names the fields the record does not carry and says why for each. This matters most on bili user, which is four requests behind one row: the identity comes from one endpoint and the counts come from three others, and any of them can refuse while the rest answer.

A refused count is left out of the record rather than printed as zero. In JSON the key is simply absent; in a table or a csv the cell is empty. A count that really is zero is still a zero, which is the whole point of the distinction.

$ bili user 946974 -o csv --fields name,follower_count,total_view
name,follower_count,total_view
影视飓风,17140391,

$ bili user 946974 -o jsonl | jq '.envelope.missed.total_view'
"x/space/upstat refused_silent: code 0 with no payload"

An absent field with no entry in missed was absent because there was nothing to put in it. An absent field with an entry was stopped by something, and the entry is what stopped it.

Why auto-detection helps

Because the default adapts to the destination, the same command reads well by hand and parses cleanly in a pipe:

bili search lofi            # a table, because this is a terminal
bili search lofi | wc -l    # JSONL, because this is a pipe

You only reach for -o when you want something other than that default.