Rc shell quick reference
Cheat sheet for the rc(1) shell from plan9/unix.
Paper: http://doc.cat-v.org/plan_9/4th_edition/papers/rc
Manual: http://man.cat-v.org/9front/1/rc
Ports
Unix port with readline/editline, etc: https://github.com/rakitzis/rc
9base standalone port: https://github.com/kfarwell/9base-rc
Rfork
A large number of rc scripts on plan9 will prefix their logic with calls to rfork(1).
In particular, you'll usually see
rfork n
which forks the process namespace, so cd(1), etc. don't affect the calling shell process.
From rc(1):
rfork [nNeEsfFm]
Become a new process group using rfork(flags) where
flags is composed of the bitwise OR of the rfork flags
specified by the option letters (see fork(2)). If no
flags are given, they default to ens. The flags and
their meanings are: n is RFNAMEG; N is RFCNAMEG; e is
RFENVG; E is RFCENVG; s is RFNOTEG; f is RFFDG; F is
RFCFDG; and m is RFNOMNT.
Import
Import state (functions, variables, etc.) from another file:
. /path/to/file
Here documents
This streams over an fd (default: 0) a block, substituting variables deliniated by the token EOF
:
ed $3 <<EOF
g/$1/s//$2/g
w
EOF
Escaping is performed as per the paper:
To include a literal $ in a here document, type $$.
If the name of a variable is followed immediately by ^, the caret is deleted.
Variable substitution can be entirely suppressed by enclosing the EOF marker following << in quotation marks, as in <<’EOF’.
Fd redirection
Redirections are executed from left to right. (from paper)
Here documents
# Stream to cmd's fd 4
cmd <<[4]End
...
End
Pipe one fd
# Pipe only fd 2 to and from fd 2
vc junk.c |[2] grep -v ’^$’
Connecting fd on a pipe
# Creates a pipeline with cmd1’s file descriptor 5 connected through a pipe to cmd2’s file descriptor 19 (from paper)
cmd1 |[5=19] cmd2
Redirect one fd
# Redirect only fd 2
vc junk.c >[2] junk.diag
Close an fd
# Close only fd 2
vc junk.c >[2=]
Conjoin fd and redirect
# Redirect both fd 1 and 2 into ;junk.out'
vc junk.c >junk.out >[2=1]
Usage
Showing the name called in execution argv[0]
-style:
argv0 = $0
fn usage {
echo >[1=2] 'usage:' $argv0 'foo ...'
exit 'usage'
}
Commandline flags
Process all commandline flags (-n 10
) and consume arguments:
somenumber = () # Empty initialization
while(~ $1 -*)
switch($1){
case -n
# Shift from '-n' token to the argument token
shift
somenumber = $1
# Shift from value to the next $* token
shift
case -*
# Unknown flags trigger usage text
usage
}
# $* now contains all non-flag-related arguments
# So in 'foo -n 5 bar' $* would now contain 'bar'
Switching on number of arguments
Switch on the number of commandline arguments, can be useful after processing flags:
switch($#*){
case 0
arg=$DEFAULT
if(~ $#weather 1)
arg=$weather
case 1
arg=$1
case *
usage
}
If you don't want to switch, a nice pattern may be:
# If there's not only 1 argument in $*
if(! ~ $#* 1){
usage
}
Snowcone operator
sort <{for(f in $files){
g '/re/' $f
}} | uniq
Divergence in rakitzis vs plan9
The way if statements work diverges from the plan9 style in rakitzis's rc.
Rakitzis
Note how the else is on the same line as the curly brace.
if(test -e $new){
if(! ~ $new $f){
echo >[2=1] fail: $new exists, not renaming $f
}
}else{
mv $f $new
echo $f → $new
}
Plan9
Note how the if and if-not-if exist on newlines.
for(i){
if(test -f $i) go $i
if not if(test -f /bin/$i) go /bin/$i
if not if(test -f /bin/*/$i) go /bin/*/$i
if not if(test -f /bin/*/*/$i) go /bin/*/*/$i
if not echo 'src: can''t find '$i
}
Simplified:
if(~ $#remotesys 1)
suf = @$remotesys
if not
remotesys=''