Compare commits
2 Commits
01626432c4
...
34be439843
| Author | SHA1 | Date | |
|---|---|---|---|
| 34be439843 | |||
| 0f7959a642 |
Binary file not shown.
|
Before Width: | Height: | Size: 121 KiB |
131
content/blog/MOP2/tb-shell-scripting.adoc
Normal file
131
content/blog/MOP2/tb-shell-scripting.adoc
Normal file
@ -0,0 +1,131 @@
|
||||
= TB shell scripting for MOP2
|
||||
Kamil Kowalczyk
|
||||
2025-11-08
|
||||
:jbake-type: post
|
||||
:jbake-tags: MOP2 osdev
|
||||
:jbake-status: published
|
||||
|
||||
This post is about TB (ToolBox) - the shell interpreter for MOP2 operating system.
|
||||
|
||||
= Invoking applications
|
||||
|
||||
Applications are invoked by providing an absolute path to the binary executable and the list of
|
||||
arguments. In MOP2 paths must be formatted as `MOUNTPOINT:/path/to/my/file`. All paths are absolute
|
||||
and MOP2 doesn't support relative paths (there's no concept of a CWD or current working directory).
|
||||
|
||||
.Example of listing currently running processes
|
||||
[source]
|
||||
----
|
||||
base:/bin/pctl ls
|
||||
----
|
||||
|
||||
Typing out the entire path might get tiresome. Imagine typing `MOUNTPOINT:/path/to/app arg more args`
|
||||
every time you want to call an app. This is what TB aliases/macros are for. They make the user type
|
||||
less ;).
|
||||
|
||||
.Example of calling an application via an alias
|
||||
[source]
|
||||
----
|
||||
$pctl ls
|
||||
----
|
||||
|
||||
Now that's way better!
|
||||
|
||||
=== Creating new aliases
|
||||
|
||||
To create an alias we can type
|
||||
|
||||
[source]
|
||||
----
|
||||
mkalias pctl base:/bin/pctl
|
||||
----
|
||||
|
||||
And then we can use our `$pctl`!
|
||||
|
||||
But there's another issue - we have to write aliases for every application, which isn't better than
|
||||
us typing out the entire path. Luckliy, there's a solution for this. TB has two useful functions
|
||||
that can help us solve this - `eachfile` and `mkaliasbn`.
|
||||
|
||||
`eachfile` takes a directory, an ignore list and a command, which is run for every entry in the said
|
||||
directory. We can also access the current directory entry via special symbol called `&EF-ELEM`.
|
||||
In `base/scripts/rc.tb` we can see the full example in action.
|
||||
|
||||
[source]
|
||||
----
|
||||
eachfile !.gitkeep base:/bin \
|
||||
mkaliasbn &EF-ELEM
|
||||
----
|
||||
|
||||
This script means: for each file in base:/bin (excluding .gitkeep), call mkaliasbn for the current
|
||||
entry. `mkaliasbn` then takes the base name of a path, which is expanded by `&EF-ELEM` and creates
|
||||
an alias. `mkaliasbn` just simply does `mkalias <app> MP:/path/<app>`.
|
||||
|
||||
== Logging
|
||||
|
||||
In the UNIX shell there's one very useful statement - `set -x`. `set -x` tells the shell to print out
|
||||
executed commands. It's useful for script debugging or in general to know what the script does (or
|
||||
if it's doing anything / not stuck). This is one thing that I hate about Windows - it shows up a
|
||||
stupid dotted spinner and doesn't tell you what it's doing and you start wondering. Is it stuck?
|
||||
Is it waiting for a drive/network/other I/O? Is it bugged? Can I turn of my PC? Will it break if I
|
||||
do? The user SHOULD NOT have these kinds of questions. That's why I believe that `set -x` is very
|
||||
important.
|
||||
|
||||
I wanted to have something similar in TB, so I've added a `setlogcmds` function. It takes `yes` or
|
||||
`no` as an argument to enable/disable logging. It can be invoked like so:
|
||||
|
||||
[source]
|
||||
----
|
||||
setlogcmds yes
|
||||
----
|
||||
|
||||
Now the user will see printed statements, for eg. during the system start up:
|
||||
|
||||
[source]
|
||||
----
|
||||
this is an init script!
|
||||
+$tb -m runfile -f base:/scripts/mount.tb
|
||||
Mounting filesystems...
|
||||
+$fs mount -mp uhome -fs LittleFS -dev atasd-ch0-M-part2 -fmt no
|
||||
OK uhome
|
||||
----
|
||||
|
||||
== String stack and subshells
|
||||
|
||||
In UNIX shell, it's common to get the output of an application, store it into a variable and then
|
||||
pass that variable around to other apps. For eg:
|
||||
|
||||
[source]
|
||||
----
|
||||
# Use of a subshell
|
||||
MYVAR=$(cat file.txt)
|
||||
echo $MYVAR | myapp # or something...
|
||||
----
|
||||
|
||||
In TB, I've decided to go with a stack, since I find it easier to implement than a variable hashmap.
|
||||
A stack can be implemented using any dynamic list BTW.
|
||||
|
||||
The stack in TB is manipulated via `stackpush` and `stackpop` functions. We can `stackpush` a string
|
||||
using `stackpush <my string>` and then `stackpop` it to remove it. We can also access the top of
|
||||
the stack via `$stack`. It's a special magic alias, which expands to the string that is at the top.
|
||||
An example of stack usage would be:
|
||||
|
||||
[source]
|
||||
----
|
||||
stackpush 'hello world string!'
|
||||
print $stack
|
||||
stackpop
|
||||
----
|
||||
|
||||
=== The `do` function
|
||||
|
||||
The `do` function does what a subshell does in UNIX shell. We can `do` a command an then have it's
|
||||
output placed at the top of the stack. An example of this would be:
|
||||
|
||||
[source]
|
||||
----
|
||||
do print 'hello world from subshell'
|
||||
print $stack
|
||||
stackpop
|
||||
----
|
||||
|
||||
It's a simpler, more primitive mechanism than the UNIX subshells, but it gets the job done ;).
|
||||
Reference in New Issue
Block a user