How to Develop Scripts
This page details how to write, load, and manage q scripts to build reusable, maintainable code.
Scripts are text files containing code that allow you to capture interactive commands as reusable code modules.
Use scripts to achieve many things, such as:
- Create reusable code, distribute it easily, and use it as building blocks for an application.
- Adapt KDB-X to serve as real-time databases (RDB), historical databases (HDB), client gateways, analytics schedulers, and more.
Tip
The .q
file extension is used to denote scripts containing q code. KDB-X also processes k code, which uses the .k
file extension.
To edit scripts, use your favorite editor such as Vim, Emacs, Visual Studio, and many others.
Overview of steps
- Load scripts
- Write scripts
- Print output
- Avoid name clashes
- Multiline expressions
- Comments
- Script information
- Run as a shebang
- Hide code for safe distribution
1. Load scripts
Load scripts at different times, either at startup or during runtime; or define a script to load another script.
Startup
For KDB-X to execute a script at startup, pass its filename as the first command-line parameter to the q executable. For example:
q
q testscript.q
Provide any command-line parameter(s) required after the script name, not before.
For example, to load/execute the script testscript.q
while enabling quiet mode
you would use:
q
q testscript.q -q
Runtime
To load scripts at runtime, use the system command \l
. For example:
q
q)\l test.q
Refer to scripts using absolute or relative paths. If no path is provided and the script isn't in the current directory, KDB-X looks in the directory specified by the QHOME
environment variable.
Using the q language, you may also dynamically construct the path to a script before loading it.
The example below demonstrates an alternative approach where the script path is built from an environment variable (retrieved using getenv
and appended using sv
q
q)system ["l ","/" sv (getenv `TMPTEST;"tmptest.q")]
Scripts loading scripts
Because scripts execute as q code, one script may load another. This is useful to reuse common code across multiple scripts. For example, the contents of a script myscript.q
could load multiple scripts:
q
\l utils.q
\l common.q
/ code goes here
Loading once
Depending on your objective, you might need to run or initialize a script once. Loading the same script multiple times can cause issues, especially when it defines shared variables, functions, or state. This often happens with utility scripts loaded by multiple other scripts.
The q language can solve this in a few ways.
The example below shows a script, test.q
, that defines variables but protects them from being re-initialized by checking whether the script has already been loaded.
It uses a namespace (.myscript
) and checks for the existence of a key (version
) before running the initialization block:
q
0N!"Loaded script";
if[not `version in key `.myscript;
0N!"Running code once";
.myscript.version:22;
/ further code to be executed once goes here
]
When you load this script multiple times, the protected block executes once, preventing duplicate setup or state definition.
q
q)\l test.q
"Loaded script"
"Running code once"
q)\l test.q
"Loaded script"
q)\l test.q
"Loaded script"
2. Write scripts
Script execution
The following example script (basic.q
), uses show
to print information displaying the order of execution. It creates a variable to show it;s accessible after the script runs and creates a function that is called after the script is loaded.
q
show"first line"
a:33
myfunc:{x+x}
show"last line"
KDB-X executes scripts as it loads them, from top to bottom. When you load a script, it prints each line in order, creates the variable and function, and makes them available for use after execution:
q
q)\l basic.q
"first line"
"last line"
q)a
33
q)myfunc[22]
44
Run scripts with bad syntax
If KDB-X encounters incorrect code in a script, it immediately throws an error and stops processing the rest of the script. The following bad.q
contains a syntax error on the second line:
q
show"about to run some bad code"
bad_code_here
show"running after bad code"
Loading the script shows that the following line(s) after the bad code is not executed. Debug information is printed on where the error occurred and execution is suspended.
To abort the suspended function and return to the normal q session, use \
:
q
q)\l bad.q
"about to run some bad code"
'bad_code_here
[3] /private/tmp/bad.q:2: bad_code_here
^
q))
3. Print output
Printing strings to an output is useful for logging. You can print strings to stdout
, stderr
or files using the relevant file handles.
For example, output.q
:
q
-1 "this prints with a return character appended to stdout";
-2 "this prints with a return character appended to stderr";
1 "this prints without a return character appended to stdout";
Running the script prints:
q
q)\l output.q
this prints with a return character appended to stdout
this prints with a return character appended to stderr
this prints without a return character appended to stdout)
Use -1
to print a list of strings, followed by .z.P
to get the current local time and .Q.s1
to format data as a string:
q
-1 "Current time ",.Q.s1 .z.P;
Running the script prints the current time:
q
q)\l output.q
Current time 2025.05.28D11:29:57.452093632
4. Avoid name clashes
When developing reusable code in scripts, ensure the script does not overwrite or alter code that exists outside its scope. Other languages address this using constructs like Java package naming, C++ namespaces, and Python modules. q provides similar isolation using custom namespaces, which allow you to encapsulate variables and functions.
Important
Avoid using single-character namespaces, as they are reserved for KX system use.
Refer to a namespace to create it. The following script test.q
creates code within .mycode
:
q
.mycode.myvar:22
.mycode.myfunc:{.mycode.myvar+x}
Loading the script shows that the functions have been created within the .mycode
namespace. Outside its containing namespace, an object is known by the full name of its containing namespace followed by a dot and its own name.
q
q)\l test.q
q).mycode.myfunc[22]
44
As namespaces are implemented as dictionaries, view their contents by entering the namespace name:
q
q).mycode
| ::
myvar | 22
myfunc| {.mycode.myvar+x}
Tip
Namespaces can contain other namespaces.
Use the system command \d
to set the current namespace. The following script test.q
creates code within the .mycode
namespace before returning to the root namespace at the end:
q
\d .mycode
myvar:22
myfunc:{myvar+x}
\d .
Loading the script shows that the functions have been created within the .mycode
namespace:
q
q)\l test.q
q).mycode.myfunc[22]
44
5. Multiline expressions
Scripts let you split long expressions across multiple lines to improve readability or avoid exceeding the maximum line length. Continuation lines must be contiguous (no empty lines between them) and indented with one or more spaces. For example:
q
jt:.[!] flip(
(`first; "Jacques");
(`family; "Tati");
(`dob; 1907.10.09);
(`dod; 1982.11.05);
(`spouse; "Micheline Winter");
(`children; 3);
(`pic; "https://en.wikipedia.org/wiki/Jacques_Tati#/media/File:Jacques_Tati.jpg") )
portrait:{
n:" "sv x`first`family; / name
i:.h.htac[`img;`alt`href!(n;x`pic);""]; / img
a:"age ",string .[-;jt`dod`dob]div 365; / age
c:", "sv(n;"d. ",4#string x`dod;a); / caption
i,"<br>",.h.htac[`p;.[!]enlist each(`style;"font-style:italic");c] }
6. Comments
In a q session, add comments by prefixing a line or statement with /
, as shown below:
q
a:22 / this is a comment
Comments in scripts offer greater flexibility compared to interactive q sessions.
Multiline comments
Include comments that span multiple lines by starting the block with a forward slash (/
) and ending it with a backslash (\
):
q
/
This is a comment block.
Q ignores everything in it.
And I mean everything.
2+2
\
Trailing multiline comments
However, when closing a comment block, using a line with a single backslash (\
) starts a trailing comment block, causing the interpreter to ignore all subsequent lines. There is no way to terminate a trailing comment block once it begins.
In the example below, the expressions that call the main
and exit
functions are placed after a trailing comment block. This prevents them from executing immediately, allowing the script to load while keeping the environment available for exploration.
q
foo:42
bar:"quick brown fox"
main:{x,y}
\
main[foo;bar]
exit 0
7. Script information
Sometimes you need to inspect the scripts to find some information.
Retrieve the name of current script
Use .z.f
within a script to retrieve the name of the script that is currently being executed.
For example, myscript.q
containing show "Executing script ",string .z.f;
would print the following when loaded:
q
q)\l myscript.q
"Executing script myscript.q"
Use value to find information on functions
Use value
to inspect functions, including where they have been defined. For example, a function f
that was created when loading script /private/tmp/test.q
would show:
q
q)value f
0x6261410003
`x`y
`symbol$()
,`
5 3 4 2 2
"..f"
"/private/tmp/test.q"
1
"{x+y}"
Use index notion to view the sixth element and the name of the script where it was previously defined:
q
q).[value f;enlist 6]
"/private/tmp/test.q"
8. Run as a shebang script
It is common practice to load a script into q, as described earlier.
If you need to create a standalone executable that runs KDB-X with a script, execute it as a shebang
:
q
$ more ./test.q
#!/usr/bin/env q
2+3
\\
$ chmod +x ./test.q
$ ./test.q
KDB+ 3.1 2013.11.20 Copyright (C) 1993-2013 Kx Systems
l64/ ...
5
9. Hide your code
You may need to distribute code without exposing the source. Use the system command \_
to hide or protect parts of the script from being viewed.
For example, using a script called hide.q
containing a function and a variable:
q
hiddenFunc:{val+x+y};
val:55;
Anyone with access to hide.q
can open the file to read its contents or inspect the code at runtime. Retrieve information about functions using value
. For example:
q
q)\l hide.q
q)hiddenFunc
{val+x+y}
q)value hiddenFunc
0x62614181410003
`x`y
`symbol$()
``val
24 22 23 12 21 11 11
"..hiddenFunc"
"/private/tmp/hide.q"
1
"{hiddenVal+x+y}"
Create a new file from the existing script. By default, the filename uses a trailing underscore to indicate the version with hidden code:
q
q)\_ hide.q
`hide.q_
Important
Do not delete the original file if you wish to retain the source code. You can now distribute the new hidden version.
Viewing the contents of the file no longer shows the source code:
q
$ cat hide.q_
6???fc?G6????k
Loading the hidden file prevents the user from viewing the source code:
q
q)\l hide.q_
q)hiddenFunc
locked
q)value hiddenFunc
`byte$()
`x`y
`symbol$()
,`
`long$()
"..hiddenFunc"
""
-1
"locked"
The functions remain callable and behave as expected:
q
q)hiddenFunc[2;5]
62
Although the functions are hidden, you can still redefine them:
q
q)hiddenFunc[2;5]
62
q)val:0
q)hiddenFunc[2;5]
7
q)hiddenFunc:{y-x}
q)hiddenFunc[2;5]
3
Summary
In this guide, you learned how to:
- Define and load q scripts at startup and runtime
- Organize code with nested loads and namespaces
- Protect one-time initialization blocks
- Handle errors, printed logs, and inspected metadata
- Leverage comments, multi-line syntax, and shebang execution
- Hide code for safe distribution
Next steps
- Build a reusable utility library and package it for team use
- Automate scheduled jobs with q scripts and cron integration
- Explore advanced error-handling patterns (
.Q.err
)
Feel free to refer back as you develop robust, maintainable q applications.