hshell¶
A bash competitor that lets you write shell-like scripts in Kotlin, with many other useful features that bash doesn't provide.
Warning
This site provides docs for an unreleased product! PROCEED WITH CAUTION AND LOW EXPECTATIONS.
Additionally, these docs are incomplete and do not reflect the full API available. They're intended as a brief guide to be read in combination with in-house API docs and IDE auto completion.
Let's get started¶
demo.shell.kts | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
Run hshell demo.shell.kts
or chmod +x demo.shell.kts; ./demo.shell.kts
.
List directories and print tables¶
Print the current directory contents as colored ls
style output, and a table:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Find the path to the script, and its containing directory:
1 2 |
|
Shell operations¶
For mv
, cp
, find
etc see the Shell API. For wget
see downloading things.
Other string utilities¶
1 |
|
File ops¶
See shell API.
Dependencies¶
Brace expansion in coordinates works:
1 2 3 4 5 6 |
|
XML and JSON¶
Parsing¶
The xml
function takes a string and tries to parse it as a URL, or a file path, or as literal XML (in that order) and returns a DOM
Document
. The json
function does the same but returns a JsonElement.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
They can also be given a Path
or URI
object.
Navigating¶
There is a path
extension function on JsonElement
and JsonObject
that support a simple navigation language:
1 2 3 4 |
|
Keys are separated by dots, and array indexing is supported. If the path isn't found then null is returned. The result can be cast
using jsonObject
, jsonArray
or jsonPrimitive
as per usual when working with Kotlin serialization.
Document
has an extension function xpath
that evaluates the given XPath expression and returns the result assuming it's a string.
Cleaning up¶
Instead of writing foo.use { foo -> foo.whatever() }
to clean things up, you can write foo.closeAtEnd().whatever()
:
1 |
|
The closeAtEnd()
function uses defer
to register a code block to run when the script finishes:
1 2 |
|
The last block deferred is run first.
Use exit(exitCode)
to terminate early. Don't call exitProcess
unless you're sure you want immediate termination without any finally
or deferred blocks running.
Disk caching¶
A LocalDiskCache
will give you a directory associated with an arbitrary string key, passing it to a handler to fill with content if
the key misses in the cache:
1 2 3 4 5 |
|
The cache will automatically keep its size in check, is thread safe and can be configured in various ways. You can also force cache misses and edit content in place.
Subshells and concurrency¶
Shell commands can be modified by global state, to avoid long repetitive argument lists (copyOptions
, diffOptions
etc). subshell
yields a new shell with a copy of all the current shell's state, which can then be used safely from another thread.
1 2 3 4 5 6 7 8 |
|
A better way is like this:
1 2 3 |
|
If the path is a file this function just invokes operation on it and returns. Otherwise, it walks over the given directory and executes operation on each entry in parallel on a thread pool, blocking whilst the entire operation completes.
The operation is invoked for all entries of a directory before the directory itself, meaning you can delete directories, sum their sizes etc. Or in other words the operation is invoked deepest first and path comes last.
The symLinkFollowing
mode is respected. If it's set to SymlinkFollowing.FOLLOW_EXTERNAL
or SymlinkFollowing.ALWAYS_FOLLOW
that means
you may be passed paths that are not children of path, or be passed the same paths more than once, so be ready for that.
WARNING: Circular symlinks aren't currently detected.
Sockets, mmapping files etc¶
hshell doesn't provide anything specific for this, but the whole Java API is available so you can simply use that instead.
Logging things¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
On the console:
When running hshell --show-log
:
Logging how long a code block took is easy, and you can give a time threshold below which no log line is made:
1 2 3 4 |
|
Credential storage¶
The passwords
object supports get/set
operators accessible using square brackets notation (passwords["foo"] = "bar"
). This provides
simple access to the system keychain. It's not really secure because the credentials will be stored under the identity of hshell itself
not the script, but in future this might be improved and it's a convenient place to store passwords. On macOS the user will be prompted to
allow access and the credential will be encrypted using the hardware security chip.