diff --git a/content/blog/MOP2/tb-shell-scripting.adoc b/content/blog/MOP2/tb-shell-scripting.adoc new file mode 100644 index 0000000..e6149bb --- /dev/null +++ b/content/blog/MOP2/tb-shell-scripting.adoc @@ -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 MP:/path/`. + +== 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 ` 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 ;).