- C 80.6%
- Roff 10.7%
- HTML 6.7%
- Yacc 1.1%
- Makefile 0.7%
- Other 0.2%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
It was never fully working anyway. Note that this is not the same editline library that is shipped on BSD systems. |
||
| .woodpecker | ||
| bench | ||
| ext | ||
| maint | ||
| museum | ||
| test-history | ||
| .gitignore | ||
| .vimrc | ||
| addon.c | ||
| addon.h | ||
| AUTHORS | ||
| builtins.c | ||
| check_readline.sh | ||
| config.def.h | ||
| COPYING | ||
| develop.c | ||
| dot.rcrc | ||
| edit-bestline.c | ||
| edit-null.c | ||
| edit-readline.c | ||
| edit.h | ||
| except.c | ||
| exec.c | ||
| fn.c | ||
| footobar.c | ||
| getopt.c | ||
| glob.c | ||
| glom.c | ||
| hash.c | ||
| heredoc.c | ||
| history.1 | ||
| history.c | ||
| input.c | ||
| input.h | ||
| lex.c | ||
| list.c | ||
| main.c | ||
| Makefile | ||
| match.c | ||
| mksignal.c | ||
| mkstatval.c | ||
| nalloc.c | ||
| open.c | ||
| p9p-test.rc | ||
| parse.c | ||
| parse.h | ||
| parse.y | ||
| print.c | ||
| proto.h | ||
| rc.h | ||
| rc23.1 | ||
| README.md | ||
| redir.c | ||
| rlimit.h | ||
| signal.c | ||
| status.c | ||
| system.c | ||
| tree.c | ||
| trip.rc | ||
| tripping.c | ||
| utils.c | ||
| var.c | ||
| VERSION | ||
| wait.c | ||
| walk.c | ||
| which.c | ||
; rc23
rc23 is a shell for UNIX and Linux. It is a reimplementation of the Plan 9 rc shell with a few extensions and adaptations, but it tries to be backward compatible. It offers a C-like syntax massively simplified compared to the Bourne shells, which makes it easier to learn, read, and safer to use when writing scripts.
Examples
# One of the main advantage of rc shells compared to bourne shells
# (like dash, bash, ksh, zsh...) is that variables are not strings
# but arrays of strings.
cities = ('San Francisco' 'New York' Berlin 東京都)
# You do not need to quote your variables anymore!
for (city in $cities)
echo $city
> San Francisco
> New York
> Berlin
> 東京都
# In fact, double-quotes are just a normal characters in rc23. Let's join
# our cities with another array, and print the results with double-quotes:
zones = (America America Europe Asia)
echo "$zones/$cities"
> "America/San Francisco" "America/New York" "Europe/Berlin" "Asia/東京都"
# ~ is the match operator. We use it to compare variables with patterns.
if (~ `pwd $home) {
echo We are at home!
} else if (!~ $cwd /*) {
echo warn: '$cwd' is not an absolute path: "$cwd" >[1=2]
~ $fatal true && exit 1
}
Differences with Byron's rc
- functions can have named arguments
fn warn(file line) { echo $file:$line: $* >[1=2] }
- range support in variable subscripts
echo $var(1 3-6 7 8 9-)
- list append operations are optimized to avoid unneeded memory allocations, which brings an almost 20x speedup with large arrays
list = ($list x y z)
-
trueandfalseare builtin -
returnandexitaccept arbitrary strings as return value, like on Plan 9. But since we are on UNIX, non-empty strings evaluate to1. -
path lookups are cached
-
builtin
waitaccepts multiple PIDs and will set$statusaccordingly [edit: this has been merged in rc]
true&false&true& wait $apids; whatis status
status=(0 1 0)
$ofscan be used to control list flattening
x=(a b c)
ofs=', ' echo $"x
> a, b, c
- the double-backquote process substitution without
$ifsallows to get the whole output in one string, while still trimming the last newline. This is important for cases where the output of a command might contain newlines (and directories on Unix sure can), likeme = ``{basename $0}.
x = ``{ls /dir}
whatis x
> x='file1
file2
file3'
-
a simple
readbuiltin is provided (optional) -
the
flagbuiltin accepts multiple flags at once -
flag -ucan detect undeclared variables and function calls with less arguments than their signature -
forloops can set multiple variables at a time
for (number file in `{ls | nl})
echo $number: $file
-
error messages contain the file name where the error occurred
-
the
.(dot) builtin looks for files in$path, like in p9rc -
you can do multiple-assignments at once
(kernel version arch) = `{uname -srm}
# It can also be used to swap variables
(x y) = ($y $x)
- more changes are planned in the future
Installation
Requirements
- a C99 compiler
make(GNU or BSD)readline, orediton BSD (optional)
Steps
makemake checkto run tests (optional)make install
To specify the line editing library, add EDIT={readline,edit,bestline,null} to each step. readline is used by default (it has the best completion).
FreeBSD and DragonFly users will probably need to add LDFLAGS=-L/usr/local/lib for the compiler to find the library.
To compile the addon builtin functions (see addon.c), you must pass RC_ADDON=1 to make.
The history program must be built explicitly: make history. It will get installed as -, -p, -- and --p. See history.1.
When installing, you can customize PREFIX and MANPATH. rc23 is installed into /usr/local by default.
If you want to make rc23 your default shell, you need to add it to the list of allowed shells on your system, usually in /etc/shells. Then run chsh(1) to make it your default shell. This is not recommended unless you really know what you are doing.
Run make uninstall to uninstall rc23.
Usage
Read the manpage! man rc23
The easiest way to use rc23 is to configure your terminal emulator program to start rc23 instead of the default shell. In that case, remember to run it as a login shell (-l), or else it will not read $home/.rcrc when starting up.
Take a look at dot.rcrc for an example.
FAQ
Is rc23 compatible with POSIX sh(1)?
It is not. That's the point!
Why should I use rc23 then?
Because, like most things coming from Plan 9, rc is just better designed: the syntax has been simplified while making it more powerful, and much less error-prone. How many times have I seen buggy shell scripts because the author forgot to put double-quotes around its variables, or because of the subtly broken error handling? Clearly, most people do not want to spend a lot of time learning the details of how their shell works. So we might as well use something simpler. Not to mention that many advanced features of the Bourne shells are rarely used (job control, complex parameter expansion, regex support, etc.). By taking away what is not essential, we end up with a more elegant design.
And talking about elegance, rc23's syntax is very close to C, even more so than csh(1). Hasn't it ever bothered you that UNIX, an operating system born with C, has a shell with a pascal-like syntax?
How do I configure rc23?
rc23 reads only one file on startup: ~/.rcrc. You can put your environment variable and functions in there (rc23 does not have aliases, just use functions). You can also customize the prompt by setting the $prompt variable. Unlike in other shells, there is no special syntax for $prompt. To make it dynamic, define a prompt function. It will be executed each time rc23 displays $prompt.
An example is available in dot.rcrc.
Does it have auto-completion?
Yes, all the supported editing libraries offer at least path and command completion, as well as history search (ctrl+r). Only libedit(3) has troubles with file names containing spaces and other special characters. Patches welcome!
Why is it not written in Rust?
Because I have a real job and I do not have the time/energy for a full rewrite. Besides, rc23 aims to be highly portable, including on old platforms where running rust would not be practical.
How do I declare local variables with rc23?
By default, all variables declared in rc23 are exported to the environment. To avoid that, you have 3 solutions:
- declare a variable attached to a block:
x=hello { echo $x } - use a function with named arguments:
fn greet(x) { echo $x } - add your variable to
$noexport. This is highly discouraged:$noexportis reserved for rc23's special variables.
Of course, you can also simply undefine variables when you don't need them anymore: x=().
How can I quickly access the last argument from the last command (!$ in bash)?
If you have compiled rc23 with readline(3), simply type alt+_ (underscore).
Is there an equivalent of "sudo !!" from bash?
Once again, we can use readline(3) to do something similar. Add the following lines to your $home/.inputrc:
$if rc23
Control-t: "\C-p\C-asudo \C-e"
$endif
After restarting rc23, when you type Ctrl+T, the last command will be brought back prefixed with "sudo".
Why can't I use ~ (tilde) as a shortcut for $HOME anymore?
In rc shells, tilde is the match operator, it does not mean "home". That said, if compiled with readline(3), there is an option to use ~ when autocompleting paths. Add these lines to your $home/.inputrc:
$if rc23
set expand-tilde on
$endif
Now, when typing a file path starting with a tilde, press Tab and it will be replaced by your $home.
What is the quickest way to check if a variable is defined?
The correct way is:
if (!~ $x ()) ...
or
if (!~ $#x 0) ...
but since () essentially means nothing, you can omit it:
if (!~ $x) ...
Thus, to set a default value to a variable:
~ $x && x = 'abc'
Is rc23 compatible with Plan 9 rc?
Mostly. The syntax should be fully compatible. We are using the official test.rc from Plan 9 to verify the parsing. Please refer to the Incompatibilities section of the manual page for a list of known differences. I hope to remove a bunch of those in the future.
Did you use AI?
None of the rc23 source code, nor its documentation, has been generated by AI and I intend to keep it that way. AI-generated merge requests will be rejected.
What's your Discord server?
I hope you are joking. We do have an IRC channel: irc.oftc.net/#rc23.