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
setto 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 asksetfor 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¶
- Read and write the formats themselves: text, binary, and KDB-X files.
- Browse the file system keywords for the complete set of one-shot and handle-based operations.