Shell Scripting
You can also use the rex CLI with typical shell scripts
to automate everything in Rex:
#!/bin/sh
rex new work
rex split -s work -- htop
rex run -s work --label logs -- tail -f app.logFor anything beyond a few commands in a row, consider a Lua script instead. Lua can do everything on this page with one connection to the server and without parsing JSON.
Keeping Track of IDs
Commands that create something print its ID with --json. Save the ID and
pass it to later commands, so the script keeps working when a label changes
or focus moves.
$ rex split --json -- htop
{
"anchor_block_id": "block:01a10e00-7268-78c6-8b85-86c56d6a53a9",
"block_ids": [
"block:01a10e00-729d-7c38-90f7-d6568611b1c4"
],
"split_ids": [
"split:01a10e00-72ab-7157-b9e0-0d4c2a8a1a68"
],
"revision": 2
}jq is the easiest way to pull the ID out:
block=$(rex split --json -- htop | jq -r '.block_ids[0]')
rex zoom -b "$block"| Command | Creates | ID is at |
|---|---|---|
rex new --json | A session with one window and one terminal | .session_id |
rex run --json | A terminal in a new window | .block_ids[0], .window_id |
rex split --json | A terminal beside an existing one | .block_ids[0] |
A label is a fine alternative when the script sets it. Give the block a label
with --label and use that as the target:
rex split --label logs -- tail -f app.log
rex capture -b logsBuilding a Layout
rex new creates a session, rex run adds a window to it, and rex split splits an existing block.
#!/bin/sh
# Open a project: an editor with a shell below it, and a second
# window that follows the logs.
rex new api --cwd ~/src/api -- nvim
rex split -s api --split=below --ratio 30 --cwd ~/src/api
rex run -s api --label logs --focus=false -- tail -f ~/src/api/app.logThese options are shared by rex run and rex split:
| Option | Does |
|---|---|
-- COMMAND | Runs a command instead of an interactive shell. |
--cwd DIR | Sets the working directory. |
--label TEXT | Labels the new block. |
--split DIRECTION | Places the block right, left, above, or below its anchor. |
--ratio PCT | Gives the new block that percentage of the split. |
--focus=false | Leaves focus where it was. |
--keep-open | Keeps the terminal open after its command exits. |
A command runs with the environment of your login shell, so your PATH applies but aliases don’t. It isn’t parsed by a shell. To use pipes or &&,
run a shell explicitly:
rex run -- sh -c 'make build && make test'Typing Into a Terminal
rex send types text into a terminal, and rex send-key presses keys:
rex send -b logs 'make test'
rex send-key -b logs enterrex send doesn’t press Enter for you. Follow it with rex send-key enter,
or include the newline in the text. It also reads from stdin:
printf 'make test\n' | rex send -b logsrex send-key uses the same key names as keyboard shortcuts, so rex send-key ctrl+c interrupts whatever is running.
Reading a Terminal
rex capture prints what is on a terminal’s screen:
$ rex capture -b logs --trim
hello from logs| Option | Does |
|---|---|
--trim | Removes trailing blank lines. |
--unwrap | Joins lines the terminal wrapped. |
--format html | Keeps colors and styling, as HTML. |
--format vt | Keeps the terminal’s control sequences. |
Running a Command and Waiting
Add --wait to rex run or rex split to wait for the command to finish.
The rex command then exits with the command’s own status:
if rex run --wait -- make test; then
echo "tests passed"
firex wait does the same for a terminal that is already running, given its
ID or label:
block=$(rex split --json -- make test | jq -r '.block_ids[0]')
# ... do other things ...
rex wait "$block"
echo "make exited with $?"rex wait also accepts a window or a session, and returns when it closes.
Use --for to give up after a while. A wait that times out exits with
status 1:
rex wait --for 10m "$block" || echo "failed or timed out"Performing Actions
rex do performs an action by name, with arguments as key=value pairs. It
runs both the app’s actions and the custom actions in your configuration file:
rex do dev_layout file=app.log
rex do pane.split direction=rightActions such as pane.split are performed by the app, so the app must be
running, with Remote Control turned on in its settings.
Watching Events
rex events prints events as they happen and runs until interrupted. With -s it follows one session:
$ rex events -s work
14:38:07 block_created …65951c session …8f9d5d
14:38:07 session_view_changed …8f9d5d revision 11
14:38:07 block_event …65951c com.superlogical.terminal.bell {}With --json it prints one JSON object per line, ready for jq. This prints
the ID of each terminal that rings its bell:
rex events -s work --json |
jq -r 'select(.payload.event.payload.name == "bell")
| .payload.event.payload.block_id'The events reference describes the JSON and lists every event. To act on events rather than print them, Lua event handlers are much less work than a pipeline.
Example: Run Tests in a Split
This script runs a command in a split beside the current terminal. If the command fails, it zooms the split and exits with a failure.
#!/bin/sh
# tests.sh
cmd="${1:-make test}"
block=$(rex split --keep-open --focus=false --json -- sh -c "$cmd" |
jq -r '.block_ids[0]')
if ! rex wait --for 10m "$block"; then
rex zoom -b "$block" --on
exit 1
fi./tests.sh 'go test ./...'The same script in Lua is a useful comparison.
