Skip to main content

Basic Syntax

Utah uses TypeScript-like syntax to write shell scripts. This guide covers the fundamental syntax elements you'll use in every Utah script.

Variables and Constants

Variable Declarations

Utah supports three types of variable declarations:

// Mutable variable with type annotation
let userName: string = "Alice";

// Immutable constant
const appName: string = "MyApp";

// Variable with type inference (less preferred)
let count = 42;

Generated Bash Code

userName="Alice"
readonly appName="MyApp"
count="42"

Supported Types

  • string - Text values
  • number - Numeric values
  • boolean - True/false values
  • string[] - Arrays of strings
  • number[] - Arrays of numbers
  • object - JSON/YAML objects

String Literals and Interpolation

Basic Strings

let message: string = "Hello, World!";

Template Literals

Use template literals for string interpolation:

let name: string = "Utah";
let version: number = 1.0;
let greeting: string = "Welcome to ${name} v${version}!";

Generated bash:

name="Utah"
version="1.0"
greeting="Welcome to ${name} v${version}!"

Comments

Utah supports both single-line and multi-line comments:

// This is a single-line comment

let value: string = "Hello"; // Inline comment

Functions

Function Declaration

function functionName(param1: type, param2: type): returnType {
// Function body
return value; // if returnType is not void
}

Examples

// Function with no return value
function greet(name: string): void {
console.log("Hello, ${name}!");
}

// Function with return value
function add(a: number, b: number): number {
return a + b;
}

// Function with optional parameters (using default values)
function multiply(a: number, b: number = 1): number {
return a * b;
}

Function Generated Bash Code

greet() {
local name="$1"
echo "Hello, ${name}!"
}

add() {
local a="$1"
local b="$2"
echo $((a + b))
}

multiply() {
local a="$1"
local b="${2:-1}"
echo $((a * b))
}

Control Flow

If Statements

if (condition) {
// Code block
} else if (anotherCondition) {
// Another code block
} else {
// Default code block
}

Comparison Operators

let a: number = 5;
let b: number = 10;
let name: string = "Alice";

if (a == b) { /* equal */ }
if (a != b) { /* not equal */ }
if (a < b) { /* less than */ }
if (a > b) { /* greater than */ }
if (a <= b) { /* less than or equal */ }
if (a >= b) { /* greater than or equal */ }

// String comparisons
if (name == "Alice") { /* string equality */ }
if (name != "Bob") { /* string inequality */ }

Logical Operators

let isActive: boolean = true;
let isAdmin: boolean = false;

if (isActive && isAdmin) { /* both true */ }
if (isActive || isAdmin) { /* at least one true */ }
if (!isActive) { /* not active */ }

Switch Statements

let status: string = "active";

switch (status) {
case "active":
console.log("User is active");
break;
case "inactive":
console.log("User is inactive");
break;
default:
console.log("Unknown status");
break;
}

Loops

For Loops

Traditional C-style for loops:

for (let i: number = 0; i < 10; i++) {
console.log("Iteration: ${i}");
}

For-In Loops

Iterate over arrays:

let fruits: string[] = ["apple", "banana", "orange"];

for (let fruit: string in fruits) {
console.log("Fruit: ${fruit}");
}

While Loops

let count: number = 0;

while (count < 5) {
console.log("Count: ${count}");
count++;
}

Break Statements

for (let i: number = 0; i < 10; i++) {
if (i == 5) {
break;
}
console.log(i);
}

Arrays

Array Declaration

// String array
let names: string[] = ["Alice", "Bob", "Charlie"];

// Number array
let numbers: number[] = [1, 2, 3, 4, 5];

// Empty array
let emptyList: string[] = [];

Array Access

let fruits: string[] = ["apple", "banana", "orange"];

let first: string = fruits[0]; // "apple"
let second: string = fruits[1]; // "banana"

Array Properties and Methods

let items: string[] = ["a", "b", "c"];

let length: number = array.length(items);
let isEmpty: boolean = array.isEmpty(items);
let contains: boolean = array.contains(items, "b");

// Reverse array
let reversed: string[] = array.reverse(items);

Error Handling

Try-Catch Blocks

try {
// Code that might fail
let content: string = fs.readFile("file.txt");
console.log(content);
}
catch {
// Handle error
console.log("Failed to read file");
}

Built-in Functions

Utah provides many built-in functions organized into namespaces:

Console Functions

console.log("Message");              // Print message
console.clear(); // Clear screen
let isSudo: boolean = console.isSudo(); // Check privileges
let answer: boolean = console.promptYesNo("Continue?");

File System Functions

let exists: boolean = fs.exists("file.txt");
let content: string = fs.readFile("file.txt");
fs.writeFile("output.txt", "content");

OS Functions

let isInstalled: boolean = os.isInstalled("git");
let osType: string = os.getOS();
let version: string = os.getLinuxVersion();

Utility Functions

let randomNum: number = utility.random(1, 100);

JSON Functions

json.installDependencies();  // Ensure jq is installed
let valid: boolean = json.isValid('{"key": "value"}');
let obj: object = json.parse('{"name": "Utah"}');
let value: string = json.get(obj, ".name");

YAML Functions

yaml.installDependencies();  // Ensure yq and jq are installed
let valid: boolean = yaml.isValid("name: Utah");
let obj: object = yaml.parse("name: Utah\nversion: 1.0");
let value: string = yaml.get(obj, ".name");

Script Metadata

Script Description

Add a description to your script:

script.description("This script automates deployment tasks");

Command Line Arguments

Define and use command-line arguments:

// Define arguments
args.define("--input", "-i", "Input file path", "string", true);
args.define("--output", "-o", "Output file path", "string", false, "output.txt");
args.define("--verbose", "-v", "Enable verbose output");

// Check for help
if (args.has("--help")) {
args.showHelp();
exit(0);
}

// Get argument values
let inputFile: string = args.get("--input");
let outputFile: string = args.get("--output");
let verbose: boolean = args.has("--verbose");

Import System

Basic Import

import "utils.shx";
import "config.shx";

Import Resolution

Utah resolves imports in this order:

  1. Relative to current file
  2. Relative to script directory
  3. Standard library (if implemented)

Ternary Operator

let status: string = isActive ? "active" : "inactive";
let message: string = count > 0 ? "Found ${count} items" : "No items found";

Generated bash:

if [ "${isActive}" = "true" ]; then
status="active"
else
status="inactive"
fi

Exit Statements

// Exit with success
exit(0);

// Exit with error
exit(1);

// Conditional exit
if (criticalError) {
console.log("Critical error occurred");
exit(1);
}

Parallel Execution

Execute functions in parallel:

function slowTask(name: string): void {
console.log("Starting ${name}...");
// Some work here
console.log("Finished ${name}");
}

// Run tasks in parallel
parallel slowTask("Task A");
parallel slowTask("Task B");
parallel slowTask("Task C");

Best Practices

1. Always Use Type Annotations

// Good
let userName: string = "Alice";
let count: number = 42;

// Avoid
let userName = "Alice"; // Type unclear

2. Use Descriptive Names

// Good
let databaseConnectionString: string = "localhost:5432";
let maxRetryAttempts: number = 3;

// Avoid
let db: string = "localhost:5432";
let max: number = 3;

3. Handle Errors Appropriately

// Good
try {
let result: string = riskyOperation();
console.log(result);
}
catch {
console.log("Operation failed gracefully");
}

// Avoid unhandled failures
let result: string = riskyOperation(); // Might fail silently

4. Validate Dependencies

// Check before using external tools
if (!os.isInstalled("git")) {
console.log("Git is required for this script");
exit(1);
}

5. Use Constants for Fixed Values

// Good
const MAX_RETRIES: number = 3;
const CONFIG_FILE: string = "app.json";

// Less clear
let maxRetries: number = 3; // Might be changed accidentally

Syntax Highlighting

For better development experience, configure your editor for TypeScript syntax highlighting when working with .shx files. Most editors will treat .shx files as TypeScript files if you associate the extension.

Next Steps

Now that you understand Utah's basic syntax:

  1. Explore language features: Variables and Types
  2. Learn about functions: Function Guide
  3. Study control flow: Control Flow
  4. Discover built-ins: Built-in Functions

Common Syntax Errors

Improper Array Declaration

// Error: Incorrect array syntax
let items: Array<string> = ["a", "b"];

// Correct
let items: string[] = ["a", "b"];

Missing Semicolons

While not always required, semicolons improve clarity:

// Preferred
let name: string = "Utah";
console.log(name);

// Works but less clear
let name: string = "Utah"
console.log(name)