Skip to content

File utilities

This page covers the file operations that are neither reads nor writes: size, listing, existence, deletion, and reaching the shell for the rest.

Building file paths

You usually assemble paths rather than writing them out literally, and .Q.dd does the assembling. It appends one component to a file handle:

q).Q.dd[`:/tmp/db;`trade]
`:/tmp/db/trade

It is shorthand for ` sv x,`$string y, and that string is the useful part: the component does not have to be a symbol already, so a partition date or an index needs no conversion.

q).Q.dd[`:/tmp/db;2026.04.06]
`:/tmp/db/2026.04.06

Paired with each-right, .Q.dd builds the column files of a splayed table in one expression:

q)`:/tmp/db/trade .Q.dd/: `sym`price`time
`:/tmp/db/trade/sym`:/tmp/db/trade/price`:/tmp/db/trade/time

Several components at once

.Q.dd joins exactly one component, so a deeper path means nesting it. sv takes the whole list instead:

q)` sv `:/tmp/db`2026.04.06`trade`sym
`:/tmp/db/2026.04.06/trade/sym

Prefer .Q.dd for a single level — it says what it does, and converts the component for you. Reach for ` sv when joining several at once, rather than nesting .Q.dd inside itself.

Three things to watch for

  • No trailing slash in the left argument. The slash that tells set to splay a table produces a doubled separator when you append a component: .Q.dd[`:/tmp/db/;`trade] gives `:/tmp/db//trade. It works on Linux and macOS, but pass the handle without it.
  • Pass an atom, not a string. A string is a list of characters, so each character becomes its own component: .Q.dd[`:/tmp/db;"trade"] gives `:/tmp/db/t/r/a/d/e. Use `$"trade".
  • An empty right argument adds a trailing slash. .Q.dd[`:/tmp/db/trade;`] gives `:/tmp/db/trade/, which is how you ask set for a directory of column files rather than a single file.

It is not only for paths

The separator depends on the left argument: a file handle joins with /, and a plain symbol with .. So the same function builds dotted names — .Q.dd[`AAPL;"O"] gives `AAPL.O — which is why the .Q.dd reference describes it as joining symbols rather than paths.

Reporting file size

hcount returns a file's size in bytes.

q)hcount `:test.txt
12

hcount reports the uncompressed length for compressed files, so it's not the size on disk. Use -21! for both figures:

q)hcount `:cf
4000000
q)-21!`:cf
compressedLength  | 1383683
uncompressedLength| 4000000
algorithm         | 2i
logicalBlockSize  | 17i
zipLevel          | 9i

Listing a directory and testing existence

key does both jobs. On a directory, key lists the entries:

q)key `:/tmp/textfiles
`s#`emptyfolder`test1.txt`test2.txt

On a file, key returns the handle, and on anything that does not exist, it returns the empty general list. That makes ()~key the existence test, and distinguishes an empty directory — which returns an empty symbol vector — from a missing one:

q)key `:/tmp/textfiles/test1.txt        / a file: the handle itself
`:/tmp/textfiles/test1.txt
q)key `:/tmp/textfiles/emptyfolder      / an empty directory
`s#`symbol$()
q)()~key `:/tmp/textfiles/doesnt_exist.txt
1b
q)()~key `:/tmp/textfiles/doesnt_exist/
1b

Deleting a file

hdel deletes a file or an empty directory. It signals an error rather than returning a flag when the target does not exist:

q)hdel `:test.txt
`:test.txt
q)hdel `:test.txt
'test.txt. OS reports: No such file or directory
  [0]  hdel `:test.txt
       ^

Running system commands

system runs any command-line program, which covers the file operations q has no keyword for:

q)system"mv file1.txt file2.txt"

System commands are not portable

Command names differ between operating systems — Windows lists a directory with dir, not ls. Branch on .z.o to keep such code working on every platform.

Next steps

  • Read and write the formats themselves: text, binary, and q files.
  • Browse the file system keywords for the complete set of one-shot and handle-based operations.