Skip to content

OS module

The OS module provides access to low-level, operating-system-dependent functionality.

Load the module with the KDB-X module loading function:

os:use`kx.os

read

Read raw data from a file or handle

os.read[f;o;n]
os.read[h;o;n]

where

  • f is a file symbol
  • h is a file handle opened by hopen
  • o and n are non-negative integers

returns binary data from the source starting at offset o and reading up to n bytes, ignoring kdb+ file compression (files are transparently decompressed by read1).

q)(`:f;16;1;0)set `time$til 1000
`:f
q)`char$os.read[`:f;0;8]
"kxzipped"
q)h:hopen`:f
q)`char$os.read[h;0;8]
"kxzipped"
q)hclose h

sleep

Suspend the process for a given duration

os.sleep i
os.sleep t

where

  • i is a short, int or long indicating a number of milliseconds
  • t is a timespan, time or second indicating a time period

suspends the process for the given time period.

q)\t os.sleep 300
300
q)\t os.sleep 00:00:10
10000
q)\t os.sleep 0D00:00:00.01
10

env

Get environment

os.env[]

returns the current environment variables as a dictionary.

q)os.env[]
QHOME                | "/home/user/kdb"
SHELL                | "/bin/bash"
PWD                  | "/home/user/kdbx"

proc

Interact with subprocesses

proc.start

os.proc.start[cmd;opts]

where

  • cmd is a string containing a command or a string list containing the program name and its parameters (parameters with embedded spaces, backslashes, or quotes are only allowed in a string list)
  • opts is a dictionary of options, as explained below, or () or :: to indicate no options

starts a subprocess that runs the command. opts can include the following keys:

  • async (boolean): if false or missing, wait for the process to exit, and return a dictionary with status, stdout and stderr. If true, only start the process and return a dictionary containing pid and any pipe handles created.
  • stdin (varying type):
    • `pass: standard input is passed through from the console. This is the default in sync mode. Note that on Windows, if you use this option when the function is called from a source other than console input (e.g. from an IPC handle), the first line of input can still be sent to the parent process instead of subprocess.
    • `discard: standard input is redirected from the null device.
    • a string or list of strings: the content is piped into the process and then the standard input handle is closed. In async mode, the default is equivalent to using this option with an empty string.
    • `pipe: open a pipe and return an additional stdin element in the result dictionary containing the pipe handle. Only usable in async mode.
  • stdout and stderr (varying type):
    • `pass: standard output/error is passed through to the console. This is the default.
    • `discard: standard output/error is redirected to the null device.
    • `text: standard output/error is returned as a list of strings (similar to read0).
    • `bin: standard output/error is returned as a byte list (similar to read1).
    • `stdout: for stderr only, the standard error is merged into the same handle as standard output instead of going through two separate handles (equivalent to 2>&1 in shell syntax).
  • env (dictionary): environment variables, replacing the whole environment (use the os.env function to get the current environment to modify or append instead)

Note that in async mode, the text and bin options apply to the return value of os.proc.wait and os.proc.kill.

The return value is a dictionary that can contain the following keys:

  • for sync mode:
    • status (int): the exit code of the process
    • stdout, stderr (byte list or string list): if requested, the contents of the respective stream
  • for async mode:
    • pid (int): process identifier that can be passed into the other proc functions
    • stdin, stdout, stderr (int): if specified in the options, a handle to a pipe that can be written to or read from like a regular file handle (see hopen)

When you start a process in async mode, call proc.wait or proc.kill with the pid in the result. Otherwise the main process can hang on exit, or the subprocess can keep running in the background, depending on the platform.

Reading from the handles returned in stdout or stderr hangs if the child process doesn't print anything on the respective stream. You can interrupt the operation only by forcibly killing the main process.

proc.wait

os.proc.wait p

where

  • p is a pid from the result of proc.start with the async:1b option

waits until the process exits and returns the same dictionary that proc.start would in sync mode.

proc.kill

os.proc.kill p

where

  • p is a pid from the result of proc.start with the async:1b option

terminates the process and returns the same dictionary that proc.start would in sync mode.

proc.running

os.proc.running p

where

  • p is a pid from the result of proc.start with the async:1b option

returns a boolean indicating whether the process is still running (Windows only; on Linux this is always 1b). Always returns 0b if it has already been reclaimed using proc.wait or proc.kill.

Examples

In its simplest form, os.proc.start can act as a substitute for system:

q)os.proc.start["sort";::]
ccc
bbb
aaa
^D
aaa
bbb
ccc
status| 0

Standard input can be passed in programmatically:

q)os.proc.start["sort";([stdin:("bbb";"ccc";"aaa")])]
aaa
bbb
ccc
status| 0

Standard output and error can be captured separately:

q)os.proc.start["ls /bin/bash /bin/foo";([stdout:`text;stderr:`text])]
status| 2i
stdout| ,"/bin/bash"
stderr| ,"ls: cannot access '/bin/foo': No such file or directory"

Or the two streams can be merged:

q)os.proc.start["ls /bin/bash /bin/foo";([stdout:`text;stderr:`stdout])]
status| 2i
stdout| ("ls: cannot access '/bin/foo': No such file or directory";"/bin/bash")

Async mode allows the parent process to continue executing other code while the subprocess runs in the background:

q)os.proc.start["du -h --max-depth=1 /home";([async:1b;stdout:`text;stderr:`text])]
pid   | 7276
stdout| 4
stderr| 6
// ... perform other tasks while du runs in the background
q)os.proc.wait 7276i
status| 0i
stdout| ("57G\t/home/user";"57G\t/home")
stderr| ()

Standard input can be sent via a pipe. This allows interaction if the launched program can print output before it finishes reading all of its input:

q)show p:os.proc.start["q";([async:1b;stdin:`pipe;stdout:`text;stderr:`discard])]
pid   | 2971
stdin | 9
stdout| 10
q)p[`stdin]"\\p 0W\n"
9i
q)p[`stdin]"\\p\n"
9i
q)show childPort:read0 p`stdout
"35473i"
//childPort now can be used to open an IPC handle for more robust interaction
q)p[`stdin]"exit 0\n"
9i
q)os.proc.wait p`pid
status| 0i
stdout| ()

Note

When using the stdin option with a q process, you may need to set the environment variable KX_TTY=1 to ensure that it does not quit in a non-interactive environment.

Environment variables can be passed in:

q)show p:os.proc.start["q";([async:1b;stdin:`pipe;stdout:`text;stderr:`discard;env:os.env[],([TEST_AAAA:"aaaa"])])]
pid   | 16394
stdin | 9
stdout| 10
q)p[`stdin]"getenv`TEST_AAAA\n"
9i
q)os.proc.wait p`pid
status| 0i
stdout| ,"\"aaaa\""