Do you find yourself using tools like make to manage non-build-related scripts?
Build tools are great, but they are not optimized for general script management.
Run aims to be better at managing small scripts and wrappers, while incorporating a familiar make-like syntax.
Where make has the ubiquitous Makefile, run has the cleverly-named "Runfile"
By default, run will look for a file named "Runfile" in the current directory, exiting with error if not found.
Read below for details on specifying alternative runfiles, as well as other special modes you might find useful.
In place of make's targets, runfiles contain 'commands'.
Similar to make, a command's label is used to invoke it from the command-line.
Instead of recipes, each runfile command contains a 'script' which is executed when the command is invoked.
You might be used to make's (default) behavior of executing each line of a recipe in a separate sub-shell.
In run, the entire script is executed within a single sub-shell.
-h & --help For Free.SHELL.RUN.RUNFILE.RUNFILE.DIR.SELF.SELF.DIR#! Support
Runfile
hello:
echo "Hello, world"
We'll see that hello shows as an invokable command, but has no other help text.
list commands
$ run list
Commands:
list (builtin) List available commands
help (builtin) Show help for a command
version (builtin) Show run version
hello
show help for hello command
$ run help hello
hello: no help available.
invoke hello command
$ run hello
Hello, world
Run accepts the following pattern for command names:
alpha ::= 'a' .. 'z' | 'A' .. 'Z'
digit ::= '0' .. '9'
CMD_NAME ::= [ alpha | '_' ] ( [ alpha | digit | '_' | '-' ] )*
Some examples:
* hello
* hello_world
* hello-world
* HelloWorld
When registering commands, run treats the command name as case-insensitive and subject to command override rules.
case-insensitive override example
For example, run will generate an error if a command name is defined multiple times in the same runfile, even if the names use different cases:
Runfile
hello-world:
echo "Hello, world"
HELLO-WORLD:
echo "HELLO, WORLD"
list commands
$ run list
run: Runfile: command hello-world defined multiple times in the same file: lines 1 and 4
When invoking commands, run treats the command name as case-insensitive:
Runfile
Hello-World:
echo "Hello, world"
output
$ run Hello-World
$ run Hello-world
$ run hello-world
Hello, world
When displaying help text, run displays command names as they are originally defined:
list commands
$ run list
Commands:
...
Hello-World
...
show help for Hello-World command
$ run help hello-world
Hello-World: no help available.
We can add a simple title to our command, providing some help content.
Runfile
## Hello world example.
hello:
echo "Hello, world"
output
$ run list
Commands:
list (builtin) List available commands
help (builtin) Show help for a command
version (builtin) Show run version
hello Hello world example.
...
$ run help hello
hello:
Hello world example.
We can further flesh out the help content by adding a description.
Runfile
##
# Hello world example.
# Prints "Hello, world".
hello:
echo "Hello, world"
output
$ run list
Commands:
list (builtin) List available commands
help (builtin) Show help for a command
version (builtin) Show run version
hello Hello world example.
...
$ run help hello
hello:
Hello world example.
Prints "Hello, world".
Positional arguments are passed through to your command script.
Runfile
##
# Hello world example.
hello:
echo "Hello, ${1}"
output
$ run hello Newman
Hello, Newman
You can configure command-line options and access their values with environment variables.
Runfile
##
# Hello world example.
# Prints "Hello, <name>".
# OPTION NAME -n,--name <name> Name to say hello to
hello:
echo "Hello, ${NAME}"
output
$ run help hello
hello:
Hello world example.
Prints "Hello, <name>".
Options:
-h, --help
Show full help screen
-n, --name <name>
Name to say hello to
$ run hello --name=Newman
$ run hello -n Newman
Hello, Newman
You can use ! to indicate that an option is required:
# OPTION NAME! -n,--name <name> Name to say hello to
Required options will be indicated in help text:
-n, --name <name> (required)
Name to say hello to
An error will be generated if a required option is not provided:
hello: ERROR: Missing required option:
-n, --name <name>
Name to say hello to
Although options are already optional by default, you can use ? to explicitly indicate that an option is optional:
# OPTION NAME? -n,--name <name> Name to say hello to
NOTE: This exists mostly for parity with ! and behaves the same as when it is not used
You can use ?= to specify a default value for an option, which will be used if the option is not provided:
# OPTION NAME ?= Newman -n,--name <name> Name to say hello to
output
$ run hello
Hello, Newman
Note: Any standard variable assignment value can be used (quoted strings, variable references, etc)
Default values will be indicated in help text:
-n, --name <name> (default: Newman)
Declare flag options by omitting the '<...>' segment.
Runfile
##
# Hello world example.
# OPTION NEWMAN --newman Say hello to Newman
hello:
NAME="World"
[[ -n "${NEWMAN}" ]] && NAME="Newman"
echo "Hello, ${NAME}"
output
$ run help hello
hello:
Hello world example.
...
--newman
Say hello to Newman
You can specify a default value for boolean options, but they behave slightly different from standard options:
# OPTION NEWMAN ?= enabled --newman Say hello to Newman
The content of the default value text is not used to determine the option's default true/false value.
Why?
Since boolean values are already always false by default, providing a "default value" can only have the effect of defaulting the value to true.
output
$ run hello
Hello, Newman
Even though a boolean option with provided default is always assumed to default to true, the default value text is still useful in that it will be displayed in the help text:
--newman (default: enabled)
This allows you to give better messaging than just "true" or "1" (i.e "enabled" in this example)
$ run help --newman=true # true | True | TRUE
$ run help --newman=1 # 1 | t | T
$ run help --newman # Empty value = true
$ run help # Default value = true if option has ?=
Hello, Newman
$ run help --newman=false # false | False | FALSE
$ run help --newman=0 # 0 | f | F
$ run help # Default value = false if option does not have ?=
Hello, World
-h & --help For FreeIf your command defines one or more options, but does not explicitly configure options -h or --help, then they are automatically registered to display the command's help text.
Runfile
##
# Hello world example.
# Prints "Hello, world".
hello:
echo "Hello, world"
output
$ run hello -h
$ run hello --help
hello:
Hello world example.
Prints "Hello, world".
If your command does not define any options within the Runfile, then run will pass all command line arguments directly through to the command script.
Runfile
##
# Echo example
# Prints the arguments passed into the script
#
echo:
echo script arguments = "${@}"
output
$ run echo -h --help Hello Newman
script arguments = -h --help Hello Newman
NOTE: As you likely surmised, help options (-h & --help) are not automatically registered when the command does not define any other options.
If your command script does define one or more options within the Runfile, you can still pass options directly through to the command script, but the syntax is a bit different:
Runfile
##
# Echo example
# Prints the arguments passed into the script
# Use -- to separate run options from script options
# OPTION ARG -a <arg> Contrived argument
#
echo:
echo ARG = "${ARG}"
echo script arguments = "${@}"
output
$ run echo -a my-arg -- -h --help Hello Newman
ARG = my-arg
script arguments = -h --help Hello Newman
Notice the '--' in the argument list - Run will stop parsing options when it encounters the '--' and pass the rest of the arguments through to the