FOSS Unleashed

Software I Use: xs

A couple of years ago, I stumbled upon a shell called xs, this shell is a descendant of a shell called es, which is itself a descendant of rc (plan 9’s shell). Unfortunately, the original author no longer wishes to maintain the project, so I maintain my own fork of it.

While I was quite experienced with writing bash scripts, I absolutely hated it, the syntax was excessively unique, and I found myself having to dive into the manual about 50% of the time I would write a script. bash‘s string and array handling are horrendiously clunky, and I was very much wishing for something that wasn’t so horrid. While xs isn’t perfect, it’s is massively simpler to write for than bash.

But that isn’t to say that xs is free of idiosyncracies. It certainly has a few. But let’s go through a quick tour of how to do basic scripting with xs.

a = 1 2 3 4 5

# push a value onto the end of the list `a`
a = $a 6

# unshift a value in front of the list `a`
a = 0 $a

# shift a value out of the list `a` into the list `v`
(v a) = $a

First thing to be aware of is that xs has lists. All variables are lists. Because of this, array-like access is actually really nice, and you can simply rely on syntax to handle three of the four common array mutations (append, prepend, pop, and shift). When assigning a list to multiple names one value will be assigned to each name, with the exception of the last name where the remainder will be assigned. This happens with function arguments as well.

fn demo {|one two rest|
    echo Here is a single value: $one
    echo Here is another single value: $two
    echo Here is the rest of the values $rest
}

demo 1 2 3 4 5

That produces the following output:

Here is a single value: 1
Here is another single value: 2
Here is the rest of the values 3 4 5

This is quite convenient, vastly improving upon writing functions for bash, where one has to write boilerplate simply to handle the function’s arguments if the function is at all remotely complex. But it’s also the first place I found one of xs‘s faults.

Contrary to what one would expect from most other languages, xs will flatten all lists. xs has zero capability to handle multidimentional lists, and if you were to call a function with multiple lists, xs will create a list of the arguments, flatten that list, then process the arguments. Meaning that unless you are passing lists by name, instead of by value, you cannot pass multiple lists to a function.