RC(1)
๐ NAME โ RC(1) command language ๐
Bestie, welcome to RC(1) from the General Commands Manual โ yes, that RC(1). The official name list is: rc, cd, eval, exec, exit, flag, rfork, shift, wait, whatis, ., ~ โ and the whole thing is a command language. Periodt. ๐
๐ SYNOPSIS ๐
rc [ -srdiIlxepvV ] [ -c command ] [ file [ arg ... ]]
โจ DESCRIPTION โจ
Rc is the Plan 9 shell. It executes command lines read from a terminal or a file โ or, with the -c flag, straight from rc's argument list. Slay. โจ
๐ธ Command Lines ๐ธ
A command line is a sequence of commands, separated by ampersands or semicolons (& or ;), and terminated by a newline. They run in sequence from left to right. Rc does not wait for a command followed by & to finish before starting the next one โ main character energy, honestly.
Whenever a command followed by & is executed, its process id gets assigned to the rc variable $apid. Whenever a command not followed by & exits or is terminated, the rc variable $status gets the process's wait message (see wait(3)); it will be the null string if the command was successful. ๐ซถ
A long command line may be continued on following lines by typing a backslash (\) followed by a newline. That sequence is treated like it were a blank. Backslash is not otherwise a special character. Ngl, very chill of it. ๐ฆ
A number-sign (#) and any characters after it up to โ but not including โ the next newline are ignored, except inside quotation marks. Comments are iconic, we love to see it. ๐
๐ฆ Simple Commands ๐ฆ
A simple command is a sequence of arguments interspersed with I/O redirections. If the first argument is the name of an rc function or one of rc's built-in commands, rc executes it. Otherwise, if the name starts with a slash (/), it must be the path name of the program to be executed.
Names with no initial slash are searched for in a list of directory names stored in $path. The first executable file of the given name found in a directory in $path is the program to be executed. To be executable, the user must have execute permission (see stat(3)), and the file must be either an executable binary for the current machine's CPU type, or a shell script.
Shell scripts begin with a line containing the full path name of a shell (usually /bin/rc), prefixed by
(and that's literally where this bit ends in the source, bestie โ keeping it exact, no invented characters from me ๐)
The first word of a simple command cannot be a keyword unless it is quoted or otherwise disguised. The keywords are:
for in while if not switch fn ~ ! @
๐ Arguments and Variables ๐
A bunch of constructions may be used where rc's syntax requires an argument to appear. In many cases a construction's value will be a list of arguments rather than a single string. Lists are lowkey the whole vibe here. ๐
The simplest kind of argument is the unquoted word: a sequence of one or more characters, none of which is a blank, tab, newline, or any of the following:
# ; & | ^ $ ` ' { } ( ) < >
An unquoted word that contains any of the characters *, ?, [ is a pattern for matching against file names. The character * matches any sequence of characters, ? matches any single character, and [class] matches any character in the class. If the first character of class is ~, the class is complemented. The class may also contain pairs of characters separated by -, standing for all characters lexically between the two.
The character / must appear explicitly in a pattern, as must the first character of the path name components . and ... A pattern is replaced by a list of arguments, one for each path name matched โ except that a pattern matching no names is not replaced by the empty list, but rather stands for itself. Pattern matching is done after all other operations.
Thus:
x=/tmp echo $x^/*.c
matches /tmp/*.c, rather than matching /*.c and then prefixing /tmp. See? Order matters, bestie. โจ
A quoted word is a sequence of characters surrounded by single quotes ('). A single quote is represented in a quoted word by a pair of quotes ('').
Each of the following is an argument.
(arguments)โ The value of a sequence of arguments enclosed in parentheses is a list comprising the members of each element of the sequence. Argument lists have no recursive structure, although their syntax may suggest it. The following are entirely equivalent:
echo hi there everybody
((echo) (hi there) everybody)
$argument,$argument(subscript)โ The argument after the$is the name of a variable whose value is substituted. Multiple levels of indirection are possible, but of questionable utility. Variable values are lists of strings. Ifargumentis a number n, the value is the nth element of$*, unless$*doesn't have n elements, in which case the value is empty. Ifargumentis followed by a parenthesized list of subscripts, the value substituted is a list composed of the requested elements (origin 1). The parenthesis must follow the variable name with no spaces. Subscripts can also take the form m-n or m- to indicate a sequence of elements. Assignments to variables are described below.$#argumentโ The value is the number of elements in the named variable. A variable never assigned a value has zero elements.$"argumentโ The value is a single string containing the components of the named variable separated by spaces. A variable with zero elements yields the empty string.- Backtick command substitution โ The forms are:
`{command}
`split {command}
rc executes the command and reads its standard output, splitting it into a list of arguments, using characters in $ifs as separators. If $ifs is not otherwise set, its value is ' \t\n'. In the second form of the command, split is used instead of $ifs.
<{command},>{command}โ The command is executed asynchronously with its standard output or standard input connected to a pipe. The value of the argument is the name of a file referring to the other end of the pipe. This allows the construction of non-linear pipelines. For example, the following runs two commandsoldandnewand usescmpto compare their outputs:
cmp <{old} <{new}
<>{command}โ The command is executed asynchronously with its standard input and output each connected to a pipe. The value of the argument is a pair of file names referring to the two other ends of the pipes, in the order corresponding to the symbols<and>(first the pipe connected to the command's standard output, then the pipe connected to its standard input).argument^argumentโ The^operator concatenates its two operands. If the two operands have the same number of components, they are concatenated pairwise. If not, then one operand must have one component, and the other must be non-empty, and concatenation is distributive.
๐ง Free Carets ๐ง
Rc will insert the ^ operator automatically between words that are not separated by white space. Thus:
cc -$flags $stem.c
is equivalent to:
cc -^$flags $stem^.c
Free carets, no cap. ๐ง
๐ I/O Redirections ๐
The sequence >file redirects the standard output file (file descriptor 1, normally the terminal) to the named file; >>file appends standard output to the file. The standard input file (file descriptor 0, also normally the terminal) may be redirected from a file by the sequence <file, or from an inline โhere documentโ by the sequence <<eof-marker.
The contents of a here document are lines of text taken from the command input stream up to a line containing nothing but the eof-marker, which may be either a quoted or unquoted word. If eof-marker is unquoted, variable names of the form $word have their values substituted from rc's environment. If $word is followed by a caret (^), the caret is deleted. If eof-marker is quoted, no substitution occurs. Very demure, very mindful. ๐
Redirections may be applied to a file-descriptor other than standard input or output by qualifying the redirection operator with a number in square brackets. For example, the diagnostic output (file descriptor 2) may be redirected by writing cc junk.c >[2]junk.
A file descriptor may be redirected to an already open descriptor by writing >[fd0=fd1] or <[fd0=fd1]. Fd1 is a previously opened file descriptor, and fd0 becomes a new copy (in the sense of dup(3)) of it. A file descriptor may be closed by writing >[fd0=] or <[fd0=].
Redirections are executed from left to right. Therefore, cc junk.c >/dev/null >[2=1] and cc junk.c >[2=1] >/dev/null have different effects: the first puts standard output in /dev/null and then puts diagnostic output in the same place, where the second directs diagnostic output to the terminal and sends standard output to /dev/null. The way order changes everything โ literally. ๐
๐ Compound Commands ๐
A pair of commands separated by a pipe operator (|) is a command. The standard output of the left command is sent through a pipe to the standard input of the right command. The pipe operator may be decorated to use different file descriptors. |[fd] connects the output end of the pipe to file descriptor fd rather than 1. |[fd0=fd1] connects output to fd1 of the left command and input to fd0 of the right command.
A pair of commands separated by && or || is a command. In either case, the left command is executed and its exit status examined. If the operator is &&, the right command is executed if the left command's status is null. || causes the right command to be executed if the left command's status is non-null.
The exit status of a command may be inverted (non-null is changed to null, null is changed to non-null) by preceding it with a !.
The | operator has highest precedence, and is left-associative (i.e. binds tighter to the left than the right). ! has intermediate precedence, and && and || have the lowest precedence.
The unary @ operator, with precedence equal to !, causes its operand to be executed in a subshell. Subshell era. ๐ฆ
Each of the following is a command.
if ( list ) commandโ Alistis a sequence of commands, separated by&,;, or newline. It is executed, and if its exit status is null, the command is executed.if not commandโ The immediately preceding command must have beenif(list) command. If its condition was non-zero, the command is executed.for(name in arguments) command/for(name) commandโ runs command once for every argument, stuffing that argument into name each round ๐ซ. No argument list? Then$*is what it loops over, periodt.while(list) commandโ keeps running list until its exit status is non-null. Every time list comes back null, command fires. An empty list counts as null status, jsyk ๐.switch(argument){list}โ hunts through list for simple commands that start with the wordcase. Heads up though: the search is only at the top level of the list โ cases hiding inside nested constructs will not be found, no cap ๐. argument gets matched against each word followingcase, using the pattern-matching algorithm described above, except that/and the first characters of.and..need not be matched explicitly. Once something matches, the commands in the list run up to the nextcasecommand (again, top level only) or the closing brace. slay ๐{list}โ braces are the grouping besties, they change up the grouping of commands implied by operator priorities. The body is a sequence of commands separated by&,;, or newline ๐.fn name{list}/fn nameโ first form defines a function with that name. After that, whenever a command whose first argument is name shows up, the current value of the rest of the command's argument list gets assigned to$*(after saving its current value, obviously ๐), and rc executes the list. Second form yeets name's function definition entirely.fn note{list}/fn noteโ a function with a special name gets called when rc receives a matching note; seenotify(3). The valid note names (and their notes) aresighup(hangup),sigint(interrupt),sigalrm(alarm), andsigfpe(floating point trap) ๐. By default rc exits on receiving any signal โ except when it's running interactively, where interrupts and quits normally make rc stop whatever it's doing and start reading a new command. The second form makes rc handle a signal the default way again. Rc also recognizes an artificial note,sigexit, which happens when rc is about to finish executing ๐ซถ.name=argument commandโ any command can be preceded by a sequence of assignments interspersed with redirections. Those assignments stay in effect until the end of the commandโฆ unless the command is empty (i.e. the assignments stand alone), in which case they're effective until rescinded by later assignments โญ.
๐ Built-in Commands ๐
These ones run internally inside rc, usually because running them changes rc's internal state or depends on it. main character commands only โจ
. file ...โ execute commands from file.$*is set, for the duration, to the rest of the argument list following file. file is searched for using$path๐.builtin command ...โ execute command as usual, except any function named command is ignored in favor of the built-in meaning. Built-ins stay winning ๐.cd [dir]โ change the current directory to dir. Default argument is$home. dir is searched for in each of the directories mentioned in$cdpath๐ช.eval [arg ...]โ the arguments get concatenated, separated by spaces, into one single string, read as input to rc, and executed ๐.exec [command ...]โ this instance of rc replaces itself with the given (non-built-in) command. gl hf ๐.flag f [+-]โ either set (+), clear (-), or test (neither+nor-) the flag f, where f is a single character, one of the command line flags (see Invocation, below) ๐ฉ.exit [status]โ exit with the given exit status. If none is given, the current value of$statusis used. bye bestie ๐.rfork [nNeEsfFm]โ become a new process group usingrfork(flags)where flags is the bitwise OR of the rfork flags named by the option letters (seefork(2)). If no flags are given, they default toens. The flags and their meanings are:nisRFNAMEG;NisRFCNAMEG;eisRFENVG;EisRFCENVG;sisRFNOTEG;fisRFFDG;FisRFCFDG; andmisRFNOMNT๐ฑ.shift [n]โ delete the first n (default1) elements of$*โ๏ธ.wait [pid]โ wait for the process with the given pid to exit. No pid? Then it waits for all outstanding processes. patience is a vibe ๐ง.whatis name ...โ print the value of each name in a form suitable for input to rc. The output is an assignment to any variable, the definition of any function, a call tobuiltinfor any built-in command, or the completed pathname of any executable file ๐.~ subject pattern ...โ subject is matched against each pattern in sequence. Matches any of them?$statusgets set to zero ๐ธ. Otherwise$statusis set to one. Patterns are the same as for file name matching, except that/and the first character of.and..need not be matched explicitly. These patterns are not subjected to file name matching before the~command executes, so you don't need to wrap them in quotation marks ๐ .
๐ Environment ๐
The environment is a list of strings that the kernel makes available to executing binaries. Rc creates an environment entry for each variable whose value is non-empty, and for each function ๐. The string for a variable entry is the variable's name followed by = and its value โ and if the value has more than one component, those are separated by SOH (001) characters. The string for a function is just the rc input that defines the function. The name of a function in the environment is the function name preceded by
girl the original page literally stops mid-sentence right there, I'm not out here inventing the rest of it ๐
When rc starts executing it reads variable and function definitions from its environment ๐ฆ.
๐ Special Variables ๐
These ones are set or used by rc:
$*โ set to rc's argument list during initialization. Whenever a.command or a function is executed, the current value is saved and$*receives the new argument list. The saved value is restored when the.or function finishes up ๐ซ.$apidโ whenever a process is started asynchronously with&,$apidis set to its process id ๐ฅ.$homeโ the default directory forcd. Defaults to$HOMEor else
(again, the page just ends the sentence there, not me ๐ญ)
$ifsโ the input field separators used in backquote substitutions. If$ifsisn't set in rc's environment, it's initialized to blank, tab and newline ๐.$pathโ the search path used to find commands and input files for the.command. If it's not set in the environment, it gets initialized by parsing the$PATHvariable (as insh(1)) or bypath=(. /bin). The variables$pathand$PATHare maintained together: changes to one will be reflected in the other ๐ฏ.$pidโ set during initialization to rc's process id ๐.$promptโ when rc runs interactively, the first component of$promptis printed before reading each command. The second component is printed whenever a newline is typed and more lines are needed to complete the command. If it's not set in the environment, it's initialized byprompt=('% ' ' ')๐ฌ.$statusโ set to the wait message of the last-executed program (unless it was started with&).!and~also change$status. Its value is used to control execution in&&,||,ifandwhilecommands. When rc exits at end-of-file of its input, or on executing anexitcommand with no argument,$statusis its exit status โญ.
๐ Invocation ๐
If rc is started with no arguments, it reads commands from standard input ๐ซถ. Otherwise its first non-flag argument is the name of a file to read commands from (but see -c below). Subsequent arguments become the initial value of $*. Rc accepts these command-line flags:
-c stringโ commands are read from string ๐.-sโ print out exit status after any command where the status is non-null ๐.-eโ exit if$statusis non-null after executing a simple command ๐ช.-iโ if-iis present, or rc is given no arguments and its standard input is a terminal, it runs interactively. Commands are prompted for using$prompt๐ฌ.-Iโ makes sure rc is not run interactively ๐ .-lโ if-lis given, or the first character of argument zero is-, rc reads commands from$home/lib/profile, if it exists, before reading its normal input ๐.-pโ a no-op ๐ซ .-dโ a no-op ๐ซ .-vโ echo input on file descriptor2as it's read ๐.-xโ print each simple command before executing it ๐.-rโ print debugging information (the internal form of commands as they're executed) ๐.
โจ SOURCE โจ
/src/cmd/rc
๐ฆ SEE ALSO ๐ฆ
Tom Duff, ``Rc - The Plan 9 Shell''.
๐ BUGS ๐
- There should be a way to match patterns against whole lists rather than just single strings ๐ค.
- Using
~to check the value of$statuschanges$status. rude. - Functions that use here documents don't work ๐.
- The
<{command}syntax depends on the underlying operating system providing a file descriptor device tree at/dev/fd. - Some FreeBSD installations does not provide file descriptors greater than
2in/dev/fd. To fix this, add
/fdescfs /dev/fd fdescfs rw 0 0
to /etc/fstab, and then mount /dev/fd. (Adding the line to fstab ensures causes FreeBSD to mount the file system automatically at boot time.) ๐ ๏ธ
- Some systems require
/bin/rcto be listed in/etc/shellsbefore it can be used as a login shell ๐.
okay that's the whole page bestie, footer says RC(1) and it comes from the source above โ that's the version tag the page is repping, and honestly the whole thing ate ๐
โจ๐ซถ
๐ฆ where this page comes from
- alpine 3.24: plan9port-doc 0_git20260208-r0 ยท LPL-1.02
- arch rolling: plan9port 20261021-2 ยท custom
- nixos 26.05: plan9port 0-unstable-2025-11-10
- nixos 26.05: plan9port-wayland 0-unstable-2025-11-26
- nixpkgs unstable: plan9port 0-unstable-2025-11-10
- nixpkgs unstable: plan9port-wayland 0-unstable-2025-11-26
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms