Shell Variables¶
AmigaDOS has two kinds of shell variables. Local variables belong to one shell process and live in memory on its pr_LocalVars list, together with the shell's aliases. Global variables are plain files in the ENV: assign, visible to every process. The Shell expands both on the command line, and programs read and write them through GetVar(), SetVar(), FindVar() and DeleteVar() (V36). This article covers the expansion syntax, the variables the Shell itself sets or reads, the four functions with their flags, and the bugs and limits of each release.
Where variables live¶
| Kind | Storage | Scope | Persistence |
|---|---|---|---|
| Local variable | struct LocalVar on pr_LocalVars (type LV_VAR) |
The shell and the commands it runs | Copied into shells it creates (NP_CopyVars, default TRUE) |
| Alias | struct LocalVar on the same list (type LV_ALIAS) |
Always local | In memory only |
| Global variable | File ENV:name |
System-wide | Lost on reboot unless also written to ENVARC: |
ENV: is normally an external link in the RAM-Handler that copies entries from ENVARC: on demand; ENVARC: is a late-binding assign to SYS:Prefs/Env-Archive. Saving a variable permanently means writing both.
Names are case-insensitive and may contain / to build a hierarchy. For global variables each level is a directory in ENV:; the hierarchy also works for local variables, whose ln_Name then holds the full path.
Local beats global: when a name exists in both, the local one wins, both in Shell expansion and in GetVar() without flags.
Expansion in the Shell¶
| Syntax | Result |
|---|---|
$name |
Value. The name runs over 0-9, A-Z, a-z; the first other character ends it. |
${name} |
Value; any characters except } allowed, so ${dir/sub} works. |
$?name |
1 if the variable exists (local or global), else 0 |
$??name |
1 if a global variable exists, else 0 |
$!name |
Binary expansion of a global variable, control characters included. Always put it in double quotes. |
*$ |
A literal $ |
Rules:
- An undefined variable is not expanded:
$foostays$foo. - The Shell adds no quotes. A value containing spaces, tabs or
=becomes several arguments unless the reference is inside double quotes. - Inside double quotes the Shell escapes
*and"in the value with*, so thatReadArgs()gets back the original text. Outside quotes nothing is escaped. $!inserts control characters such as line feeds into the tokenizer; the surrounding quotes make the Shell escape them so the command receives them intact.- The prompt is expanded too, so a long prompt can be kept in a local variable.
Set target "RAM:My Dir"
MakeDir "$target" ; one argument: RAM:My Dir
If $?target EQ 1
Echo "target is ${target}"
EndIf
Variables the Shell sets¶
| Variable | Contents | Also in |
|---|---|---|
process |
CLI number of this shell (unique; same as pr_TaskNum and the Status number) |
%N in the prompt |
RC |
Return code of the last command; compared with FailAt to abort scripts |
cli_ReturnCode |
Result2 |
Error code of the last failed command (printed by Why) |
cli_Result2 |
Variables the Shell reads¶
Boolean variables count as on for on, 1, yes or TRUE (any case); every other value means off.
| Variable | Type | Effect |
|---|---|---|
VIEWER |
path | Program used to show a non-executable file that datatypes.library recognises (V45). No default: unset means such a file gives an error. |
echo |
Boolean | Echo each command before running it |
debug |
Boolean | Send every command to the serial port (9600 baud, 8N1); captured in RAM:syslog when boot-menu logging is on |
oldredirect |
Boolean | Redirection only right after the command name (V33 BCPL CLI syntax); List C: >Out then lists a file called >Out |
interactive |
Boolean | Trace scripts: Return/y runs a command, Del/n skips it, Esc/q stops tracing and deletes the variable. Set by the boot menu's trace option. |
simpleshell |
Boolean | Read lines in cooked mode: no TAB expansion and, since V47 (history moved from the console into the Shell), no history |
histsize |
number | History length in lines (ignored with simpleshell) |
histskipdups |
Boolean | Do not store a command again if it repeats (ignored with simpleshell) |
Note: the NDK 3.2 shell release notes (47.37) state that skipping duplicates became the default and that the variable
NOLISTSKIPDUPSturns it off.
GetVar¶
LONG GetVar(CONST_STRPTR name, STRPTR buffer, LONG size, LONG flags); /* V36 */
Looks for a local variable first, then a global one, unless flags restrict it. size includes the terminating NUL.
- A normal (text) read stops at the first line feed or NUL and always NUL-terminates.
- With
GVF_BINARY_VARthe whole value is copied; it is still NUL-terminated unlessGVF_DONT_NULL_TERMis also set. - Truncation is not an error.
- Success: returns the number of characters stored (without the NUL) and sets
IoErr()to the full size of the variable, including anything after a line feed or NUL. - Failure: returns −1.
IoErr()=ERROR_BAD_NUMBERifsizeis 0,ERROR_OBJECT_NOT_FOUNDif there is no such variable, or any error from readingENV:.
UBYTE buf[256];
LONG len = GetVar("Language", buf, sizeof(buf), GVF_GLOBAL_ONLY);
if (len >= 0) {
if (IoErr() > len)
PutStr("value was truncated\n");
Printf("Language = %s\n", buf);
} else if (IoErr() == ERROR_OBJECT_NOT_FOUND) {
PutStr("not set\n");
}
To size a buffer exactly, read once into a small buffer and use IoErr() as the full length (add 1 for the NUL).
SetVar¶
BOOL SetVar(CONST_STRPTR name, CONST_STRPTR buffer, LONG size, LONG flags); /* V36 */
size= −1 meansbufferis a NUL-terminated string (the NUL is not stored).buffer= NULL deletes the variable (same asDeleteVar()).- Without
GVF_GLOBAL_ONLYand with a value,SetVar()always creates or updates a local variable, even if a global one of that name exists. To set a global variable you must passGVF_GLOBAL_ONLY. - Without
GVF_GLOBAL_ONLYand withbufferNULL, it deletes a local variable, or if there is none, a global one. GVF_LOCAL_ONLYonly matters for deletion.GVF_SAVE_VAR(global only) also writes or deletes the copy inENVARC:. It works, although the 1991 AmigaDOS Manual does not document it.- Missing hierarchy levels are created.
- Returns non-zero on success, with
IoErr()undefined. On failure returnsDOSFALSE; deleting a variable that does not exist givesERROR_OBJECT_NOT_FOUND.
/* Make a preference permanent, like "SetEnv SAVE". */
if (!SetVar("MyApp/Mode", "fast", -1, GVF_GLOBAL_ONLY | GVF_SAVE_VAR))
PrintFault(IoErr(), "SetVar");
Requirements and pitfalls¶
- The file system behind
ENV:must supportMODE_READWRITEopens andSetFileSize(). The FFS and the RAM-Handler do; some third-party file systems do not, and then setting a global variable fails silently. - Up to and including V47 there is a race:
SetVar()writes the data and then truncates the file in a second step, so a concurrent writer or reader of the same global variable can see a mixed or truncated value. - Before V47 the files created in
ENV:/ENVARC:kept thee(executable) bit, and a name containing//crashed the system. GVF_GLOBAL_ONLYmust not be combined withLV_ALIAS: aliases are always local.
FindVar¶
struct LocalVar *FindVar(CONST_STRPTR name, ULONG type); /* V36 */
Finds a local variable (LV_VAR) or alias (LV_ALIAS) of the calling process; globals are never found. Only the low bits of type count: GVF_* flags are ignored. Returns NULL and ERROR_OBJECT_NOT_FOUND if not found; on success IoErr() is untouched.
struct LocalVar {
struct Node lv_Node; /* ln_Name = full name; ln_Type = LV_VAR or LV_ALIAS */
UWORD lv_Flags;
STRPTR lv_Value; /* NOT NUL-terminated */
ULONG lv_Len; /* length of lv_Value in bytes */
};
- Never allocate or change this structure yourself; it may grow. Use
SetVar(). lv_Valueis a byte array oflv_Lenbytes, not a C string.lv_FlagsgetsGVF_BINARY_VARwhen a non-printable value is stored, but AmigaDOS never reads it: only theflagsofGetVar()decide between text and binary.
DeleteVar¶
LONG DeleteVar(CONST_STRPTR name, ULONG flags); /* V36 */
Equivalent to SetVar(name, NULL, 0, flags). Without flags it tries a local variable first, then a global one. GVF_GLOBAL_ONLY, GVF_LOCAL_ONLY and GVF_SAVE_VAR work as for SetVar(). It does not delete directories (hierarchy levels). Non-zero on success with IoErr() undefined; ERROR_OBJECT_NOT_FOUND if nothing matched.
Flag values¶
From dos/var.h:
| Constant | Value | Use |
|---|---|---|
LV_VAR 2.0+ |
0 | Variable (type byte) |
LV_ALIAS 2.0+ |
1 | Alias (type byte) |
LVF_IGNORE 2.0+ |
0x80 | Entry ignored by GetVar() and friends |
GVF_GLOBAL_ONLY 2.0+ |
0x100 | Global (ENV:) only |
GVF_LOCAL_ONLY 2.0+ |
0x200 | Local only |
GVF_BINARY_VAR 2.0+ |
0x400 | Do not stop at line feed/NUL |
GVF_DONT_NULL_TERM 2.0+ |
0x800 | With GVF_BINARY_VAR: no NUL terminator |
GVF_SAVE_VAR 3.1+ |
0x1000 | With GVF_GLOBAL_ONLY: also update ENVARC: |
Version notes¶
| Version | Change |
|---|---|
| V36 | GetVar() returned the full variable size instead of the number of characters copied, and did not set IoErr(). Fixed in V37. |
| V36–V37 | GVF_DONT_NULL_TERM worked only for local variables. Fixed in V39. |
| V45 | VIEWER shows data files through datatypes. |
| before V47 | SetVar() left the e bit on ENV: files; // in a name crashed. |
| V47 | History moved from the console into the Shell; simpleshell disables it. |
| up to V47 | SetVar() race on concurrent access to the same global variable. |
See Also¶
- The AmigaDOS Shell
- Shell Internals
- ReadArgs, ReadItem and FindArg
- AmigaDOS Assigns
- OS 3.2 Shell Commands
Sources: Thomas Richter, Amiga ROM Kernel Reference Manual: AmigaDOS (2024, v1.27), §15.1.5–15.1.6 (pp. 317–318), §15.4 (pp. 337–341); NDK 3.2 R4 Include_H/dos/var.h; SFD/dos_lib.sfd; ReleaseNotes/shell-RelNotes Updated: 2026-11-13