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

Paths are usually assembled rather than typed, and .Q.dd is the tool for it. 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 it 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 that catch people out

  • No trailing slash on the left. The slash that tells set to splay a table produces a doubled separator when a component is appended: .Q.dd[`:/tmp/db/;`trade] gives `:/tmp/db//trade. It works on POSIX, 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 reference describes it as joining symbols rather than paths.

File size

hcount returns a file's size in bytes.

q)hcount `:test.txt
12

The uncompressed length is reported 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. Used on a directory, it lists the entries:

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

Used on a file, it 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 rather than returning a flag when the target is not there:

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