Scripts
In rsh, you can write and run scripts in the rsh language. To
run a script, you can pass it as an argument to the
rsh
commandline application:
> rsh myscript.rsh
This will run the script to completion in a new instance of Rsh.
You can also run scripts inside the current instance of
Rsh using
source
:
> source myscript.rsh
Let's look at an example script file:
# myscript.rsh
def greet [name] {
["hello" $name]
}
greet "world"
A script file defines the definitions for custom commands as well as the main script itself, which will run after the custom commands are defined.
In the above, first greet
is defined by the rsh
interpreter. This allows us to later call this definition. We
could have written the above as:
greet "world"
def greet [name] {
["hello" $name]
}
There is no requirement that definitions have to come before the parts of the script that call the definitions, allowing you to put them where you feel comfortable.
How scripts are processed
In a script, definitions run first. This allows us to call the definitions using the calls in the script.
After the definitions run, we start at the top of the script file and run each group of commands one after another.
Script lines
To better understand how rsh sees lines of code, let's take a look at an example script:
a
b; c | d
When this script is run, rsh will first run the
a
command to completion and view its results. Next,
rsh will run b; c | d
following the rules in the
"Semicolons" section.
Parameterizing Scripts
Script files can optionally contain a special "main"
command. main
will be run after any other Rsh code,
and is primarily used to add parameters to scripts. You can pass
arguments to scripts after the script name (rsh <script name> <script args>
).
For example:
# myscript.rsh
def main [x: int] {
$x + 10
}
> rsh myscript.rsh 100
110
Argument Type Interpretation
By default, arguments provided to a script are interpreted with
the type Type::Any
, implying that they are not
constrained to a specific data type and can be dynamically
interpreted as fitting any of the available data types during
script execution.
In the previous example, main [x: int]
denotes that
the argument x should possess an integer data type. However, if
arguments are not explicitly typed, they will be parsed
according to their apparent data type.
For example:
# implicit_type.rsh
def main [x] {
$"Hello ($x | describe) ($x)"
}
# explicit_type.rsh
def main [x: string] {
$"Hello ($x | describe) ($x)"
}
> rsh implicit_type.rsh +1
Hello int 1
> rsh explicit_type.rsh +1
Hello string +1
Subcommands
A script can have multiple sub-commands like run
,
build
, etc. which allows to execute a specific main
sub-function. The important part is to expose them correctly
with def main [] {}
. See more details in the
Custom Command
section.
For example:
# myscript.rsh
def "main run" [] {
print "running"
}
def "main build" [] {
print "building"
}
# important for the command to be exposed to the outside
def main [] {}
> rsh myscript.rsh build
building
> rsh myscript.rsh run
running
#!
)
Shebangs (
On Linux and macOS you can optionally use a
shebang
to tell the OS that a file should be interpreted by Rsh. For
example, with the following in a file named
myscript
:
#!/usr/bin/env rsh
"Hello World!"
> ./myscript
Hello World!
For script to have access to standard input,
rsh
should be invoked with
--stdin
flag:
#!/usr/bin/env -S rsh --stdin
echo $"stdin: ($in)"
> echo "Hello World!" | ./myscript
stdin: Hello World!