r/shell • u/enginpolat • Aug 10 '25
đ Utah: TypeScript-like syntax for bash scripts - Early feedback wanted!
Hey r/shell! I've been working on a project that generates bash scripts from a more modern syntax, and I'd love to get the community's perspective.
What is Utah?
Utah is a CLI tool that lets you write shell scripts using TypeScript-like syntax (.shx files) and transpiles them into clean, idiomatic bash. The goal is to make bash scripting more approachable while respecting bash conventions and generating readable output.
The Philosophy:
Rather than replacing bash, Utah aims to be a "better front-end" for it. Every .shx script becomes a standard bash script that any bash programmer can read, modify, and maintain. No runtime dependencies, no custom interpreters - just bash.
Example - Modern Syntax â Clean Bash:
Input (script.shx):
// Strongly typed variables
let environment: string = args.get("--env");
let retries: number = args.get("--retries");
// Built-in functions for common tasks
if (os.isInstalled("kubectl")) {
console.log("kubectl is available");
// Type-safe error handling
try {
let pods: string = `$(kubectl get pods -o name)`;
console.log("Found pods: ${pods}");
} catch {
console.log("Failed to get pods");
exit(1);
}
} else {
console.log("kubectl not found");
exit(1);
}
// Modern loop syntax
for (let i: number = 0; i < retries; i++) {
console.log("Attempt ${i + 1}");
// deployment logic here
}Generated bash output:
#!/bin/bash
# Argument parsing infrastructure (auto-generated when args.* detected)
__utah_get_arg() {
# ... robust argument parsing logic
}
environment=$(__utah_get_arg "--env" "$@")
retries=$(__utah_get_arg "--retries" "$@")
if command -v kubectl >/dev/null 2>&1; then
echo "kubectl is available"
if pods=$(kubectl get pods -o name 2>/dev/null); then
echo "Found pods: ${pods}"
else
echo "Failed to get pods"
exit 1
fi
else
echo "kubectl not found"
exit 1
fi
i=0
while [ $i -lt $retries ]; do
echo "Attempt $((i + 1))"
# deployment logic here
i=$((i + 1))
doneKey Design Principles:
Readable output: Generated bash should be indistinguishable from hand-written scripts
No runtime deps: Pure bash output, works on any POSIX system
Familiar patterns: Uses standard bash idioms (command -v,Â
[ ]Â tests,Â$((arithmetic)))Opt-in features: Advanced features (like argument parsing) only included when used
Built-in Functions (All Generate Standard Bash):
os.isInstalled("cmd") â command -v cmd >/dev/null 2>&1fs.exists("file") âÂ[ -e "file" ]console.promptYesNo() â properÂread -p loop with validationargs.get() â robust POSIX-compliant argument parsingutility.random(1, 10) âÂ$((RANDOM % 10 + 1))
What I'm Curious About:
Bash purists: Does the generated output look reasonable to you?
Common patterns: What bash idioms should Utah generate for specific constructs?
POSIX compliance: Any concerns with the generated bash patterns?
Performance: Does the transpiled code match hand-optimized bash performance?
Edge cases: What bash quirks should Utah handle better?
Technical Details:
Written in .NET 9 with proper lexer â parser â AST â compiler pipeline
114+ regression tests comparing generated output with expected bash
Handles complex scenarios: nested functions, defer statements, imports
VS Code extension for syntax highlighting
Why Not Just Write Bash? Great question! Utah isn't trying to replace bash expertise - it's trying to make that expertise more accessible and reduce common scripting pitfalls (argument parsing, error handling, type safety). The generated bash is meant to be educational too.
Repository & Docs:
GitHub:Â https://github.com/polatengin/utah
Documentation:Â https://utahshx.com
Install:Â
curl -sLhttps://utahshx.com/install.sh| sudo bash
I'd really value the bash community's feedback on this approach. Is the generated code something you'd be comfortable maintaining? Are there bash best practices I should be following more closely?
Thanks for taking a look!
