This repository has been archived on 2026-08-16. You can view files and clone it, but cannot push or open issues or pull requests.
ollie-9p/skills/rc-shell/SKILL.md

126 lines
3.1 KiB
Markdown

---
name: rc-shell
intent: scripting, shell
description: Plan 9 rc shell scripting syntax and conventions. Use when writing .rc shell scripts or working with Plan 9 rc syntax.
user-invocable: false
---
# Plan 9 rc Shell Syntax
Shebang: `#!/usr/bin/env rc` Extension: `.rc`
## Variables
```
name=value # NO spaces around =
files=(a b c) # lists in parens
$name $files(1) $files(2-) # substitute, 1st element, from 2nd onward
$files(2-4) # range
$#files # count
$"files # join with spaces
$^files # concat (no spaces)
```
## Command Substitution
```
result=`{command} # backticks, NOT $()
result=`split{command} # split using 'split' instead of $ifs
```
## Control Flow
```
if(test -f file) {
echo exists
}
for(i in 1 2 3) { echo $i }
for(i) { echo $i } # iterates over $* if no list
while(test $n -lt 10) { n=`{echo $n + 1 | bc} }
switch($file) {
case *.txt
echo text
case *
echo other
}
```
**`if not` is unreliable** — though documented in the rc manual, `if not` causes syntax errors in practice. Use a flag variable instead:
```
ok=0
if(test -f file) { echo exists; ok=1 }
if(~ $ok 0) echo missing
```
Chained `if` statements serve as logical AND:
```
if(test -f file) if(~ $mode rw) echo writable
```
## Functions & Pattern Matching
```
fn name { # always use multi-line bodies
echo $* $1 # $* all args, $1 first arg
}
fn name # remove definition
~ $file *.txt # pattern match (sets $status)
if(~ $#list 0) { echo empty }
```
**Single-line function bodies with `;` cause syntax errors.** Always use multi-line:
```
# WRONG: fn die { echo $* >[1=2]; exit 1 }
fn die {
echo $* >[1=2]
exit 1
}
```
## Redirection & Pipes
```
>out >>out <in <<EOF # stdout, append, stdin, here doc
>[2]err >[2=] # fd 2 to file, close fd 2
>[1=2] # stdout to stderr (write to stderr)
>[2=1] # stderr to stdout
<[0=3] # fd 0 from fd 3
<{cmd} >{cmd} <>{cmd} # process substitution
cmd1 | cmd2 # pipe
|[2] |[1=2] # pipe fd 2, pipe fd 2 to fd 1
{ cmd1; cmd2 } > file # redirect block stdout to file
```
## Operators
`; & && || ! @` - sequence, async, and, or, invert, subshell
`^` - concatenate (auto-inserted without whitespace)
## Special Variables
`$*` `$status` `$apid` `$pid` `$ifs` `$path` `$home`
## Built-ins
`. file` `cd` `eval` `exec` `exit` `shift` `wait` `whatis`
## Key Differences from Bash
- Backticks `{cmd}`, not `$(cmd)`
- Lists `(a b c)`, 1-indexed
- No reliable `if/else`: use flag variable + chained `if` instead of `if not`
- `test` or `~`, not `[[ ]]`
- `$#var` not `${#var}`, `$"var` not `"$*"`
- `>[1=2]` to write to stderr (stdout→stderr), `>[2=1]` for stderr→stdout
- `>[2]file` redirects fd 2 to a file; bare `>[2]` (no filename) is a syntax error
- No `${var:-default}`
- `fn name` not `function name`
- Single quotes only (double quotes not special)
- Free carets: `$x.c` → `$x^.c`