Est. 1976 Intermediate

AOS/VS (32bit CLI)

CLI32, the 32-bit rewrite of Data General's Command Line Interpreter for AOS/VS: a bracket-and-percent-sign macro language descended from the 1976 AOS CLI that ran the batch jobs and logon macros of Eclipse MV minicomputers.

Created by Data General Corporation

Paradigm Procedural, Scripting (command language with macro expansion and conditional pseudo-macros)
Typing Untyped (everything is text; unsigned-integer pseudo-macros treat arguments as numbers)
First Appeared 1976 (AOS CLI, User's Manual original release April 1976); the 32-bit CLI32 followed with AOS/VS II, which is generally dated to 1988
Latest Version AOS/VS II CLI32 Release 03.21.00.00 (seen in use in February 2001); no further releases are known after EMC acquired Data General in 1999

AOS/VS (32bit CLI) is CLI32, the 32-bit Command Line Interpreter for Data General’s AOS/VS and AOS/VS II operating systems. These ran on the Eclipse MV family of 32-bit minicomputers. CLI32 was a rewrite of the 16-bit CLI that Data General had shipped with AOS since 1976, and it kept that CLI’s odd and fairly capable macro language. Scripts are plain .CLI text files. Arguments are written as %1%. Anything in square brackets is expanded before the command runs, whether it names a file, a macro or a built-in “pseudo-macro” such as [!DATE]. Conditionals are written [!EQUAL,a,b][!ELSE][!END]. On an Eclipse MV system, logon macros, batch jobs, system-management scripts and some of Data General’s own utilities were all written in this language.

History & Origins

The AOS CLI (1976)

Data General already had a “Command Line Interpreter” for its RDOS operating system on the Nova and Eclipse by early 1975. When the company released the Advanced Operating System (AOS) for its 16-bit Eclipse minicomputers, the CLI came with it. The Command Line Interpreter (CLI) User’s Manual (document 093-000122) gives its original release as April 1976. The same document was revised in 1977, 1978, 1979, November 1980, May 1982, February 1984 and October 1984. That makes it an unusually complete record of how the language changed.

AOS/VS and the 32-bit Eclipse MV (1980)

In 1980 Data General introduced the Eclipse MV/8000, its first 32-bit minicomputer. It was code-named Eagle and is the machine whose development Tracy Kidder described in The Soul of a New Machine. The new operating system was AOS/VS (Advanced Operating System/Virtual Storage). It used the MV’s eight protection rings and ran 16-bit AOS programs unchanged. The CLI came across as well. The October 1984 manual covers both “AOS Rev. 6.00” and “AOS/VS Rev. 5.00” and says:

The CLI utilities for AOS and AOS/VS are almost identical. In the few instances where a CLI command or command switch is valid for one operating system but not the other, we highlight the difference.

At this stage the CLI running on 32-bit AOS/VS was still a 16-bit program. A batch listing from Atari in January 1984 identifies it as AOS/VS CLI REV 03.03.00.00.

CLI32 and AOS/VS II

AOS/VS II came out in 1988. It started as AOS/VS revision 8.00, but it introduced a file system that was incompatible with the old one, so Data General gave it its own revision numbers starting at 1.00. The original system carried on as maintained “classic” AOS/VS. Secondary accounts, including Wikipedia’s AOS article and an Easter-egg write-up on eeggs.com, say AOS/VS II brought a new interpreter, CLI32.PR, which was a complete 32-bit rewrite of the old CLI. The original CLI was then usually called CLI16. No dated Data General announcement of CLI32 has turned up, so its exact release date is uncertain. By August 1992 it was clearly shipping on both systems. The AOS/VS Revision 7.70 Release Notice lists about 150 numbered CLI32 fixes, refers to “the initial release of CLI32”, and warns about behaviour that differs “from CLI16 and CLI32 from earlier revisions”.

The two interpreters were installed side by side. The [!CLI] pseudo-macro returns CLI16 or CLI32, so a macro can check which one it is running under. The 1993 Phrack guide does this in its example code, and prints a message if the reader is “using CLI16 which won’t let READ take arguments.”

Design Philosophy

The CLI works by textual expansion. It does not parse the input into an abstract syntax tree. Before a command runs, the CLI rewrites the line. Dummy arguments are replaced with actual arguments, bracketed file names with the contents of those files, and bracketed pseudo-macros with their values. Only then does it run what is left. The idea is similar to what Unix shells do with $1 and `command` substitution, but the CLI takes it further:

  • Macros are only files. Typing TOP makes the CLI look for a command called TOP, then for TOP.CLI in the working directory and search list. Writing [TOP] explicitly substitutes the file’s contents, so a macro can have the same name as a built-in command.
  • Conditionals are expansions too. [!EQUAL,x,y] can appear inside an argument to another pseudo-macro. The manual shows a single statement that tests whether three arguments are all equal by nesting [!EQUAL]…YES[!ELSE]NO[!END] inside the arguments of an outer [!EQUAL].
  • Switches are part of the language. DG command syntax uses /SWITCH and /SWITCH=value everywhere, and the macro language can pick switches apart. %1/S% includes the first argument’s /S switch, %2\L% includes every switch except /L, and %0/T=% gives the value of a /T= switch on the macro’s own name.
  • The environment is stacked. PUSH opens a new CLI level with its own search list, working directory, list file, string, prompt and error-handling settings, and POP restores the previous level. Well-behaved macros push on entry and pop on exit.

Key Features

Dummy arguments

FormExpands to
%0%The macro name, with all its switches
%n%The nth argument, with its switches
%m-n,i%Every ith argument from m to n (defaults: m=1, n=32767, i=1)
%-%Every argument
%n/%Only the nth argument’s switches
%n\%The nth argument without its switches
%n/S=%The value of the nth argument’s /S= switch

This example from the 1984 manual swaps the names of two files:

1
2
3
RENAME %1% TEMP
RENAME %2% %1%
RENAME TEMP %2%

Parentheses make a command repeat. If ASM.CLI contains XEQ MASM/L=%1%.LS %1%, then calling ASM (FILEA,FILEB,FILEC) runs the assembly three times, once for each file.

Conditional pseudo-macros

[!EQUAL] and [!NEQUAL] compare any two strings without regard to case. [!UEQ], [!UNE], [!ULT], [!ULE], [!UGT] and [!UGE] compare unsigned decimal integers. Each block ends with [!END], with an optional [!ELSE] before it:

1
2
3
4
5
[!EQUAL,[!OPERATOR],ON]
    QBATCH %-%
[!ELSE]
    WRITE THE OPERATOR IS OFF DUTY.
[!END]

If a macro is missing an [!END], the CLI prompts with !) when it is inside a true branch and \) when it is inside a false one. It waits until you type the missing [!END].

Arithmetic and system pseudo-macros

[!UADD], [!USUB], [!UMULTIPLY], [!UDIVIDE] and [!UMODULO] work on unsigned integers up to 4,294,967,295. Results wrap modulo 4,294,967,296. [!OCTAL] and [!DECIMAL] convert between number bases. Other pseudo-macros query the system, including [!DATE], [!PID], [!USERNAME], [!SEARCHLIST], [!FILENAMES template], [!SIZE file] and [!CONSOLE]. [!READ prompt] writes a prompt and expands to whatever the user types in reply. CLI32 adds /LENGTH=, /EOF= and /FILEID= switches to [!READ] so that it can also read from files.

Loops: recursion first, then real loops

For most of its history the CLI language had no loop construct. The 1984 manual shows how to get one: a macro that calls itself.

1
2
3
4
5
6
7
[!ULE %2% %3%]
    WRITE CONTINUE LOOPING - INDEX VARIABLE IS %2%
    %1%
    DO_INCREMENT %1% [!UADD %2% %4%] %3% %4%
[!ELSE]
    WRITE STOP LOOPING - INDEX VARIABLE IS %2%
[!END]

DO_INCREMENT TEST 2 10 3 runs TEST with the index at 2, 5 and 8. AOS/VS Revision 7.70 (August 1992) finally gave CLI32 [!LOOPSTART] and [!LOOPEND], along with [!EXIT/LOOP], [!EXIT/MACRO] and [!EXIT/ALL].

Example: 99 Bottles of Beer in CLI32

Robert de Mander of Robdem Soft AB wrote this CLI32 version, published on his Robdem Soft pages (it was reportedly also submitted to 99-bottles-of-beer.net). It uses a VAR0 variable, the 7.70 loop pseudo-macros, \\ lexical comments and a trick with the parity bit. In the author’s words, [!ascii 254] is “really a comma with the parity bit set, and write/7bit strips it away”. The trick is needed because a literal comma would act as an argument separator.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
\\ Data General AOS/VS 32-bit CLI
\\ 99 Bottles of Beer
\\ by Robert de Mander

push
prompt pop
var0 99
[!loopstart [!var0]]
string [!var0] bottle[!une,[!var0],1]s[!end]
write/7bit [!string] of beer on the wall[!ascii 254], [!string] of beer.
write/nonewline/7bit Take one down and pass it around[!ascii 254],
var0 [!usub,[!var0],1]
string [!ueq,[!var0],0]no more[!else][!var0][!end] bottle[!une,[!var0],1]s[!end]
write [!string] of beer on the wall.
[!loopend]
write/7bit No more bottles of beer on the wall[!ascii 254], no more bottles of beer.
write/7bit Go to the store[!ascii 254], buy some more[!ascii 254], 99 bottles of beer on the wall.

pop

The pluralisation logic is bottle[!une,[!var0],1]s[!end]. It is a conditional placed in the middle of a word, and it shows how this language treats control flow as text substitution.

Error handling

Errors fall into two classes, CLASS1 and CLASS2. Each class can be set to ABORT, ERROR, WARNING or IGNORE for the current environment level, and any single command can override that with /1= or /2=. For example, CREATE/2=IGNORE file keeps going if the file already exists. CLI32 lets every command take the global switches /STR= and /ESTR=. The 7.70 release notice explains that /ESTR=name writes a command’s error message into a named string, which a macro can then test instead of simply stopping.

Evolution

PeriodInterpreterNotable language changes
1976AOS CLIManual 093-000122 first released (April 1976)
1980-198416-bit CLI on AOS and AOS/VSBy Rev. 07 (Oct 1984): dummy-argument ranges and switch extraction, nested conditionals, unsigned 32-bit arithmetic, PUSH/POP environment levels, ; for several commands on one line, & for continuation lines
c. 1988 (by 1992-1993 at the latest)CLI32 with AOS/VS II32-bit rewrite; CLI32-only PRIVILEGE and PASSWORD commands, group ACLs, extended [!READ], global /STR= and /ESTR= switches
Aug 1992CLI32 in AOS/VS 7.70[!LOOPSTART]/[!LOOPEND], [!EXIT], %[!VAR0]% inside dummy arguments, /7BIT, [!SYSTEM/SPECIFIC], rule for reading two-digit years
1990s-2001CLI32 3.2x on AOS/VS IIMaintenance and year-2000 updates; Release 03.21.00.00 seen in 2001

CLI16 was not removed. The 7.70 release notice still lists CLI16 enhancements, such as ?PRIVILEGE support “with prompts SuSpSm compatible with CLI32”. Sites could run either interpreter, and a 2001 session log shows both on the same machine.

Current Relevance

AOS/VS and AOS/VS II are discontinued. Eclipse MV hardware was obsolescent by the late 1990s, and EMC bought Data General in 1999. Sarpy County replaced its MV30000 in 1996 rather than rewrite 2,500 AOS/VS COBOL programs, which illustrates the choice facing the remaining installations. No Docker image exists, and there is no public, redistributable way to run CLI32 on modern hardware. What survives is mainly documentation: Data General manuals and release notices kept in online archives, the 1993 Phrack guide, and pages written by long-time administrators recording tricks and Easter eggs from these systems.

Why It Matters

The AOS/VS CLI was a well-developed command language on a commercial minicomputer at a time when many systems had much more limited job-control languages. Inline expansion of [!pseudo-macros], pattern-based argument ranges, macros that can read their own switches and stacked environment levels made it a real scripting language for the shops that used Eclipse and Eclipse MV systems. It also shows the downside of building everything on text expansion. Loops had to be written as recursion for more than a decade, commas and brackets needed tricks such as the parity bit to print literally, and the 7.70 release notice records how fragile & continuation at end-of-file could be. For anyone studying shell design, CLI32 is a well-documented example of a command language that developed separately from the Unix shell tradition.

Timeline

1975
Data General documents a Command Line Interpreter for its Real-Time Disk Operating System (RDOS CLI manual 093-000109-00, February 1975), so the CLI name was already in use before AOS was released
1976
The Command Line Interpreter (CLI) User's Manual for the Advanced Operating System (AOS), document 093-000122, has its original release in April 1976. AOS runs on Data General's 16-bit Eclipse minicomputers, and its CLI is the language that AOS/VS and CLI32 later inherit
1980
Data General introduces the 32-bit Eclipse MV/8000 (code-named Eagle, the machine in Tracy Kidder's The Soul of a New Machine) and its operating system AOS/VS. The CLI itself remains a 16-bit program, and 16-bit AOS software runs unchanged under AOS/VS
1984
Revision 07 of the CLI User's Manual (October 1984), effective with AOS Rev. 6.00 and AOS/VS Rev. 5.00, says the CLIs for the two systems are 'almost identical'. It documents percent-sign dummy arguments, the [!EQUAL]/[!ELSE]/[!END] conditional pseudo-macros, unsigned arithmetic up to 4,294,967,295, and a recursive macro that stands in for a DO loop
1988
AOS/VS II is released. It was originally planned as AOS/VS revision 8.00, but its new file system was incompatible, so its revision numbers restarted at 1.00 while 'classic' AOS/VS stayed in maintenance. Secondary accounts (Wikipedia and an Easter-egg write-up on eeggs.com) say CLI32.PR, the 32-bit rewrite of the CLI, arrived with AOS/VS II. No dated Data General announcement has been found
1992
The AOS/VS Revision 7.70 Release Notice (August 1992) shows CLI32 shipping on classic AOS/VS too. It adds [!LOOPSTART] and [!LOOPEND] (the first real loops in CLI32 macros), [!EXIT/LOOP|MACRO|ALL], [!SYSTEM/SPECIFIC], the /ESTR= error-string switch and /7BIT on WRITE. It also sets how two-digit years are read: 68-99 as 1968-1999 and 00-67 as 2000-2067
1993
Phrack issue 44 runs a two-part 'Guide to Data General's AOS/VS', with sample sessions captured on an AOS/VS II 2.20 system on 31 May 1993. The guide explains that 'there are two versions of CLI, CLI16 and CLI32, with CLI32 being more advanced', and flags the commands and switches that exist only in CLI32
1999
EMC acquires Data General. The Eclipse MV line and AOS/VS were already obsolescent by then, but classic AOS/VS (rev 7.7x) and AOS/VS II (rev 3.2x) were both updated to survive the year-2000 rollover
2001
A session log from February 2001 shows 'AOS/VS II CLI32 Release 03.21.00.00' still running alongside the 16-bit CLI. Typing xyzzy gets 'Nothing happens.' from CLI16 and 'Twice as much happens.' from CLI32

Notable Uses & Legacy

Atari software development system

A January 1984 batch-output listing from Atari's 'S/W Development HCD1' system, reproduced on Wikipedia, shows the AOS/VS CLI (rev 03.03) running a queued .CLI job. The job sets the search list, directory and default ACL, then runs Atari's CAMAC assembler

Data General system utilities

Data General shipped some utilities and languages as CLI macros rather than programs. The 1984 manual notes that for these you 'do not begin the command line with XEQ', and under CLI32 the LOAD command is a macro that calls the DUMP_II program

Sarpy County, Nebraska

Until 1996 the county's core system was a Data General MV30000 running AOS/VS, with about 500 users (175 to 200 each day) and 2,500 AOS/VS COBOL programs, according to a July 1997 FOCUS user-group case study. On sites like this the CLI was the operating environment for logons, batch queues and job control

Logon macros and batch jobs on Eclipse MV sites

Interactive sessions and batch jobs on AOS/VS normally ran under a CLI process (the 1984 Atari listing shows one starting and terminating around a batch job). The initial logon macro and queued .CLI job files were written in the CLI language, and the Revision 7.70 notice adds a CLI32 /NOCA switch so that console interrupts are ignored while the logon macro runs

Running Today

Run examples using the official Docker image:

docker pull
Last updated: