rsh 0.25
rsh, or Rsh for short, is a new shell that takes a modern, structured approach to your commandline. It works seamlessly with the data from your filesystem, operating system, and a growing number of file formats to make it easy to build powerful commandline pipelines.
Today, we're releasing 0.25 of Rsh. It's one of the largest releases we've ever done. With it rsh grows from being a shell to being a full scripting language as well.
Where to get it
Rsh 0.25 is available as
pre-built binaries
or from
crates.io. If you have Rust installed you can install it using
cargo install rsh.
If you want all the goodies, you can install
cargo install rsh --features=extra.
As part of this release, we also publish a set of plugins you
can install and use with Rsh. To install, use
cargo install rsh_plugin_<plugin name>.
What's New
Lots of new features in this release. If you'd like to watch a demonstration, we can also watch a video showing off the new features.
Custom commands
A long-requested feature for rsh is to have scripting capability. A key piece of this story is the ability to make your own commands in addition to those built into Rsh.
With 0.25, you can now make your own custom commands:
def add [x, y] {
= $x + $y
}
add 1 5
The definitions are created in the scope where you define them, and are visible before any of the script body runs. This allows you to have written the above like so:
add 1 5
def add [x, y] {
= $x + $y
}
There are a few other important features of custom commands. The first is that you can optionally add a type annotation to each parameter you take in. These type annotations tell the parser how to parse arguments given to the function and tell the type checker what is allowed to be passed in.
def add [x:int, y] {
= $x + $y
}
add "bob" 4
Now if you run the example, you'll see a type error like this:
error: Type Error
┌─ shell:5:5
│
5 │ add "bob" 4
│ ^^^^^ Expected int, found "bob"
Here's a list of the types that are allowed:
- int - an integer
- string - a string
- path - a filepath
- table - a table
- unit - a number with a unit (like
10kb) - number - an integer or decimal number
- pattern - a glob pattern (like
foo*) - range - a numeric range (like
1..10) - block - a code block (like
{ ls }) - any - any of the above types (this is assumed if you leave off the type)
Note: rsh is whitespace-significant, so the variable + ':' + type need to be united as one, without spaces.
You can also create flags for your commands:
def create-item(name, --age:int) { ... }
create-item "driver" --age 20
Variables
You can now also define variables using let.
let name = "rsh"
echo $name
These variables are created in the scope they're defined in.
If, for example, we had written this instead:
do {
let $name = "Rsh"
echo $name # prints "Rsh"
}
echo $name # this will fail
Once we leave the block above, the name variable is
no longer visible.
These variables are immutable and need to be initialized as they are defined.
Along with variables, we also support "shadowing", so that you can create a variable inside of a scope, and have it "shadow" the variable of the same name outside of the scope:
let size = 10
do {
let size = 5
echo $size # prints 5
}
echo $size # prints 10
Environment variables
You can also use let-env to create environment
variables. Just like variables, they are created in the scope
they're defined.
let-env TRACE = on
echo $rsh.env.TRACE # prints 'on'
Aliases
With 0.25, we've also changed how aliases work to be more like a text expansion, in the spirit of how aliases work in shells like Bash.
alias ll = ls -l
ll -a
This lets you alias a larger command to a smaller name, and then also pass additional arguments and flags to it.
Source-ing
You can now also source a script, so that the
definitions and code of that script runs in the current scope
and context.
Let's say we had a file called definitions.rsh:
# definitions.rsh
def add [x, y] {
= x + y
}
We can later use the definitions in this file using
source:
source definitions.rsh
add 3 7
Like variables and definitions, the definitions we
source are put into the current scope.
do {
source definitions.rsh
echo $(add 3 7) # prints 10
}
echo $(add 1 11) # errors, `add` isn't in scope here
Breaking changes
-
Please note that the
aliascommand no longer works the same way it did pre-0.25
Pre-0.25, aliases worked similarly to how def works
now. We used multiple arguments, and each was optional:
alias mycmd [a b c d] { myverylongcommand $a $b $c $d }
With 0.25, we no longer pass parameters to alias this way. Instead, think of the aliased name being replaced by the right hand side. To update the previous alias to 0.25, we can write:
alias mycmd = myverylongcommand
Calling mycmd 1 2 now expands to
myverylongcommand 1 2 and then runs the expanded
command.
Improvements
-
the
whichcommand now shows if the name points to an alias or custom command (LhKipp) - you can configure the style that primities are shown (fdncred)
- optionally you can highlight trailing spaces (fdncred)
-
we support comments now, using
#(jonathandturner) -
better information for debugging in
version(gillespiecd) - Thanks to all those who landed general improvements too! (baoyachi, scrabsha, stormasm, max-sixty, ArturKovacs, JosephTLyons)
Looking forward
This update opens a lot of doors to what's possible with rsh. There are a few areas we'd like to explore with it: better autocompletions, describing external commands, and more. We'd also really like to hear your feedback on the release so we can continue to improve the overall experience of using Rsh.