Record 💭 comments and ❯ commands from from shell scripts in addition to their output.
This is done by building a version of the original script that surfaces all the comments and commands by also echoing them to the screen.
Then passing that augmented script to asciinema's rec --command command.
The asciinema tool is an awesome terminal session recorder.
And it has a rec [filename] command with a -c --command=<command>
option.
This specifies a command
to record (other than the default of $SHELL
)
So given an (executable) script screencasts/demo-date_maths:
#!/usr/bin/env bash
## Date maths
# The `date` command can be used to retrieve the:
# * *day of the week* using the `%l` option
day_of_the_week=$(date +%l)
# * *hour of the day* using the `%u` option
hour=$(date +%u)
# Now, can you guess what we're going to do with those two numbers?
# 🤔...
sleep 3
# We're going to add them together!
echo $((day_of_the_week + hour))
We can use asciinema's rec
to make a recording:
asciinema rec --command screencasts/demo-date_maths
However that recording is going to lose a bunch of context from the script and end up looking something like this:
If, however we instead used this command:
asciinema-rec_script screencasts/demo-date_maths
Without any effort we can end up with a recording like this:
Place asciinema-rec_script
somewhere in your $PATH.
- asciinema (installation)
- bash
- (optional) bat (installation)
- A
cat
clone to provide syntax highlighting
- A
asciinema-rec_script ./screencasts/demo-date_maths
- When called with no extra arguments, the tool will pass
asciinema rec
a filename of./screencasts/demo-date_maths.cast
to place the recording in - (Nb. the filename is derived from the source script by attaching a
.cast
extension.)
- When called with no extra arguments, the tool will pass
./screencasts/demo-date_maths.asc
- Will take advantage of the shebang line #!/usr/bin/env asciinema-rec_script in the
.asc
script - (ie. allowing the input script to be run as its own command, without having to pass it as an argument to
asciinema-rec_script
)
- Will take advantage of the shebang line #!/usr/bin/env asciinema-rec_script in the
./screencasts/demo-date_maths.asc --
- When called with
--
the tool will allowasciinema rec
to receive no additional arguments - (ie. allowing it to maintain its default behaviour of uploading screencasts to https://asciinema.org)
- When called with
./screencasts/demo-date_maths.asc --help
- Will also pass any additional arguments it gets to
asciinema rec
- (so eg.
--help
will show all the asciinema rec [options])
- Will also pass any additional arguments it gets to
SLEEP=0 ./screencasts/demo-bash_functions.asc
- (env vars can be passed into the script in the regular way)
- (to eg set
PROMPT="$ "
,PROMPT_PAUSE=5
)
bash ./screencasts/demo-date_maths.asc
- Nb. It should also be possibe to execute the
.asc
script in your $SHELL as a regular bash script - (Maintaining this compatability means that the
.asc
file won't require any special commands that a regular shell script wouldn't already have in it. Which hopefully results in regular shell scripts resulting in half-decent looking recordings.)
- Nb. It should also be possibe to execute the
(Nb. the .asc
extension ("ASCiinema") is not strictly necessary, but gives some uniformity.)
asciinema-rec_script
(written in bash) reads from the file passed to it one line at a time and will detect:
- ❯ lines of code (syntax highlighting them as bash code)
- 💭 comments (syntax highlighting them as markdown)
- blank lines (preserving whitespace)
- 💬 and some other special lines
It uses meta-programming to build an augmented version of itself from each of these lines storing them in a temporary augmented_script
file.
And finally it passes that file (and any other arguments specified on the command line) to asciinema run --command <augmented_script>
for it to make a recording.
Nb. although the .asc
scripts are used to produced asciinema recordings its expected that these files can also be run in bash/zsh
eg.
❯ source screencasts/demo-bash_functions.asc
a='a 0', b='b 0', c=''
-> f1(a='a 0', b='b 0', c='')
-> f1(a='a 0', b='b 0', c='')
BEFORE: a='a 0', b='b 0', c=''
-> f1(a='a 1', b='b 1', c='c 1')
AFTER : a='a 1', b='b 1', c='c 1'
BEFORE: a='a 0', b='b 0', c=''
-> f1(a='a 1', b='b 1', c='c 1')
AFTER : a='a 0', b='b 0', c=''
-> f1(a='a 1', b='b 2', c='c 2')
-> f1(a='a 1', b='b 0', c='c 2')
Sun Oct 17 20:56:56 AEDT 2021
-> f1(a='a 1', b='b 0', c='Sun Oct 17 20:56:56 AEDT 2021')
BEFORE: a='a 0', b='b 0', c=''
-> f1(a='a 1', b='b 3', c='c 3')
AFTER : a='a 0', b='b 0', c=''
-> f2(a='a 4', b='b 4', c='Sun Oct 17 20:56:56 AEDT 2021')
-> f2(a='a 4', b='b 4', c='Sun Oct 17 20:56:56 AEDT 2021')
-> f2(a='a 4', b='b 4', c='c 4')
As the .asc
is read in one line at a time, making it difficult (if not impossible) to execute multi-line commands in these shell scripts.
The two workarounds I could think of were:
- making every multi-line command fit on one line
- So this:
f1() { echo "-> f1(a='$a', b='$b', c='$c')" }
- would have to be manually edited in the
.asc
file to this:f1() { echo "-> f1(a='$a', b='$b', c='$c')"; }
- inline the multi-line code from a
source
command:
- So something like this:
source "${script%.asc}/f1.1"
- could be used (eg. here) to source this file but be seemlessly displayed in the recording as:
f1() { echo "-> f1(a='$a', b='$b', c='$c')" }
This tool provides a command line playack menu of screencasts which it pulls from github repos.
It works with both public & private github repos.
(Nb. The recordings that appear in the menu don't necessarily have to be made using asciinema-rec_script
,
but the menu will filter out all the .asc
files from that directory.)
asciinema-gh zechris/asciinema-rec_script
- search for .cast files in https://github.com/zechris/asciinema-rec_script/tree/master/screencasts
REF=v0.9.0 asciinema-gh zechris/asciinema-rec_script
- (which can be used with a github
REF
s like release tags eg. v0.9.0)
- (which can be used with a github
REF=first_pr asciinema-gh zechris/asciinema-rec_script
- (or branch names eg. first_pr)
asciinema-gh spectreconsole/spectre.console docs/input/assets/casts
- (NB. a path can also be specified if the screencasts aren't found in the default
./screencasts
)
- (NB. a path can also be specified if the screencasts aren't found in the default
echo 26 | screencast_dir=docs/input/assets/casts asciinema-gh spectreconsole/spectre.console
- (to pre-select number
26
from the menu)
- (to pre-select number