Intermediate

I/O Operations in MUMPS

Learn console output formatting, terminal input, and sequential file I/O in MUMPS with practical Docker-ready examples using YottaDB

Input and output in MUMPS revolve around a single unifying idea: the current device. Whether you are writing to the terminal, reading a keystroke, or streaming lines to a file, you use the same three commands – WRITE, READ, and USE – and only the device changes underneath them. This device-oriented model dates back to the language’s origins on 1960s hospital terminals, but it maps cleanly onto modern files and pipes.

As an imperative, procedural language, MUMPS keeps I/O explicit and sequential. You OPEN a device, USE it to make it current, then WRITE to it or READ from it, and finally CLOSE it. The terminal (the “principal device”) is open automatically, which is why the Hello World program could WRITE without any setup. Everything else – files in particular – follows the same open/use/close rhythm.

This tutorial covers formatted console output beyond a simple string, reading input from the terminal (including timeouts and single keystrokes), and reading and writing sequential files. Every command has an abbreviation (W, R, U, O, C), and we show both forms so you can read real-world MUMPS code, which leans heavily on the terse variants.

Formatted Console Output

The WRITE command does more than print strings. It understands format control characters! for a new line and ?n to tab to a specific column – and MUMPS ships intrinsic functions like $JUSTIFY and $FNUMBER for aligning and formatting numbers. Because MUMPS is typeless, numbers and strings flow through WRITE identically, with numeric values rendered in their canonical form.

Create a file named ioformat.m:

ioformat ; Formatted output in MUMPS
 write "=== Output Formatting ===",!
 ;
 ; ?n tabs the cursor to a fixed column, useful for tables
 write "Name",?15,"Score",!
 write "Alice",?15,"95",!
 write "Bob",?15,"88",!
 ;
 ; Numbers are coerced to their canonical string form automatically
 set pi=3.14159
 write "Pi is approximately ",pi,!
 ;
 ; $JUSTIFY(value,width,decimals) rounds and right-aligns a number
 write "Justified: ",$justify(pi,10,2),!
 ;
 ; $FNUMBER inserts thousands separators
 write "Formatted: ",$fnumber(1234567,","),!
 ;
 ; Multiple values and arithmetic can share a single WRITE
 set x=10,y=20
 write "Sum of ",x," and ",y," is ",x+y,!
 quit

Key points about output formatting:

  • ! forces a new line. It can appear multiple times (,!,!) to emit blank lines.
  • ?15 moves the cursor to column 15, padding with spaces. If the cursor is already past that column, WRITE does nothing for it.
  • $JUSTIFY(pi,10,2) rounds 3.14159 to two decimals and right-justifies the result in a 10-character field, producing 3.14.
  • $FNUMBER(1234567,",") inserts commas to produce 1,234,567.

Reading Input from the Terminal

The READ command pulls input from the current device into a variable. A string literal placed before the variable acts as a prompt. READ supports a timeout (var:seconds), after which it sets the special variable $TEST to indicate whether input arrived, and a single-character mode (*var) that returns the ASCII code of one keystroke without waiting for Enter.

Create a file named ioread.m:

ioread ; Reading input from the terminal
 ; Read a full line of text into a variable
 read "What is your name? ",name
 write "Hello, ",name,"!",!
 ;
 ; Read with a 5-second timeout; $TEST is 1 if input arrived, 0 if it timed out
 read "Favorite number (5s): ",num:5
 if $test write "You entered: ",num,!
 if '$test write "Timed out - no input.",!
 ;
 ; *var reads a single keystroke and stores its ASCII code
 read "Press any key to continue... ",*char
 write !
 write "You pressed character code ",char,!
 quit

Because this program depends on live keystrokes, it needs an interactive terminal (docker run -it ...). Here is a sample session where the user types Ada, then 42, then presses the a key:

What is your name? Ada
Hello, Ada!
Favorite number (5s): 42
You entered: 42
Press any key to continue... 
You pressed character code 97

Notes on terminal input:

  • READ name reads a full line, stopping at Enter. The prompt string is optional but idiomatic.
  • READ num:5 waits at most 5 seconds. Check $TEST ('$test is the logical NOT) before trusting the value.
  • READ *char reads one keystroke immediately and stores its numeric code – 97 is the ASCII value of lowercase a. Use $CHAR(char) to convert a code back to a character.

Sequential File I/O

Files are just another device. You OPEN a file with deviceparameters describing how you intend to use it (NEWVERSION to create/truncate for writing, READONLY to read), USE it to redirect WRITE/READ, and CLOSE it when finished. Switching back to $PRINCIPAL (the terminal) makes subsequent WRITEs appear on screen again. When reading, the special variable $ZEOF becomes 1 once the last READ reaches end-of-file.

Create a file named iofile.m:

iofile ; File I/O - write lines to a file, then read them back
 set file="/app/notes.txt"
 ;
 ; --- Write three lines to the file ---
 ; NEWVERSION creates the file, truncating any existing content
 open file:(newversion)
 use file
 write "First line",!
 write "Second line",!
 write "Third line",!
 use $principal
 close file
 write "Wrote 3 lines to ",file,!
 ;
 ; --- Read the file back one line at a time ---
 open file:(readonly)
 set count=0
 for  use file read line quit:$zeof  set count=count+1 use $principal write "Line ",count,": ",line,!
 use $principal
 close file
 write "Read ",count," lines total.",!
 quit

How the file example works:

  • OPEN file:(newversion) creates notes.txt for writing. Without NEWVERSION, an existing file would be appended to instead of replaced.
  • USE file makes the file the current device, so the following WRITEs go to disk rather than the screen.
  • USE $principal switches output back to the terminal.
  • The argumentless FOR loops indefinitely; QUIT:$ZEOF exits the moment a READ reaches end-of-file. The read that hits EOF returns an empty string and sets $ZEOF, so QUIT:$ZEOF skips it before the counter is incremented.
  • CLOSE file flushes and releases the file. The file notes.txt is left in your working directory after the run.

Running with Docker

Use the same YottaDB image from the language overview. The volume mount exposes your .m routines and lets file output land back in your working directory.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# Pull the official image
docker pull yottadb/yottadb-base:latest-master

# Run the formatted-output example
docker run --rm -v $(pwd):/app -e ydb_routines=/app yottadb/yottadb-base:latest-master yottadb -run ioformat

# Run the file I/O example
docker run --rm -v $(pwd):/app -e ydb_routines=/app yottadb/yottadb-base:latest-master yottadb -run iofile

# Run the interactive input example (note the -it flags for a live terminal)
docker run --rm -it -v $(pwd):/app -e ydb_routines=/app yottadb/yottadb-base:latest-master yottadb -run ioread

Expected Output

Running ioformat produces:

=== Output Formatting ===
Name           Score
Alice          95
Bob            88
Pi is approximately 3.14159
Justified:       3.14
Formatted: 1,234,567
Sum of 10 and 20 is 30

Running iofile produces (and leaves a notes.txt file behind in your working directory):

Wrote 3 lines to /app/notes.txt
Line 1: First line
Line 2: Second line
Line 3: Third line
Read 3 lines total.

Key Concepts

  • Everything is a device – the terminal, files, and pipes all use the same OPEN/USE/READ/WRITE/CLOSE commands; only the current device changes.
  • $PRINCIPAL is the terminal and is open automatically, which is why Hello World could WRITE with no setup. USE $PRINCIPAL returns output to the screen after writing to a file.
  • WRITE formats inline with ! (new line) and ?n (column tab), while $JUSTIFY and $FNUMBER handle numeric alignment and thousands separators.
  • READ is flexible – read a full line, add a :seconds timeout and check $TEST, or read a single keystroke with *var to get its ASCII code.
  • File access is declared at OPEN via deviceparameters: NEWVERSION to create/truncate for writing, READONLY to read, and (by default) append for existing files.
  • $ZEOF signals end-of-file during reads, and the argumentless FOR ... QUIT:$ZEOF idiom is the standard way to consume a sequential file line by line.
  • Abbreviations are everywhereW, R, U, O, and C stand in for WRITE, READ, USE, OPEN, and CLOSE in real-world MUMPS code.

Running Today

All examples can be run using Docker:

docker pull yottadb/yottadb-base:latest-master
Last updated:

Comments

Loading comments...

Leave a Comment

2000 characters remaining