Skip to main content

Run Command

The utah run command compiles and immediately executes Utah (.shx) scripts or individual commands without creating persistent .sh files. This is ideal for development, testing, one-off script execution, and quick command testing.

You can now run .shx files and commands directly without the run command! See Direct Execution for details.

Basic Usage

# Run a .shx file
utah run <file.shx>

# Run a .shx file with arguments
utah run <file.shx> -- [script-args...]

# Run a single command directly
utah run -c <command>
utah run --command <command>

# Direct execution (new, shorter syntax)
utah <file.shx>
utah <file.shx> -- [script-args...]
utah -c <command>
utah --command <command>

Examples

File Execution

# Traditional syntax
utah run hello.shx

# New direct execution syntax
utah hello.shx

# With arguments
utah run script.shx -- --name "John" --verbose
utah script.shx -- --config production.yml

Direct Command Execution

# Traditional syntax
utah run -c "console.log('Hello, World!')"

# New direct execution syntax
utah -c "console.log('Hello, World!')"

# Check if a package is installed
utah --command "os.isInstalled('git')"

# File operations
utah -c "console.log(fs.exists('/etc/passwd'))"

# JSON operations
utah --command "json.installDependencies()"

# Multiple statements (use quotes to wrap the entire command)
utah -c "let name: string = \"Utah\"; console.log(\"Hello from \${name}!\");"

With Arguments

Utah scripts can access command-line arguments through the args namespace. To pass arguments to your script, use the -- separator to distinguish between Utah CLI options and script arguments:

script.shx:

script.description("Example script with arguments");

args.define("--name", "-n", "Your name", "string", true);
args.define("--count", "-c", "Number of greetings", "number", false, 1);

let name: string = args.get("--name");
let count: number = args.get("--count");

for (let i: number = 0; i < count; i++) {
console.log("Hello, ${name}!");
}

Running with arguments:

# Pass arguments to the script using -- separator
utah run script.shx -- --name "Alice" --count 3

# Direct execution syntax also supports arguments
utah script.shx -- --name "Bob" --count 2

# Show script help
utah run script.shx -- --help

Output:

Hello, Alice!
Hello, Alice!
Hello, Alice!

Important: The -- separator is required when passing arguments to scripts. Everything before -- is treated as Utah CLI options, and everything after -- is passed to your script.

Direct Command Benefits

The -c/--command option is particularly useful for:

Quick Testing:

# Test a function before adding to a script
utah run -c "console.log(os.platform())"

# Verify file operations
utah run --command "console.log(fs.dirname('/path/to/file.txt'))"

One-liners:

# Quick system checks
utah run -c "console.log(os.isInstalled('docker') ? 'Docker available' : 'Docker not found')"

# Inline calculations
utah run --command "console.log(utility.random(1, 100))"

Scripting Integration:

# Use in shell scripts or CI/CD
if utah run -c "os.isInstalled('node')" | grep -q "true"; then
echo "Node.js is installed"
fi

Development Workflow:

# Quick prototyping without creating files
utah run -c "
let data: string[] = ['apple', 'banana', 'cherry'];
for (let item of data) {
console.log(item.toUpperCase());
}
"

How It Works

1. Temporary Compilation

When you run utah run script.shx, Utah:

  1. Compiles the .shx file to bash in memory
  2. Creates a temporary file in the system temp directory
  3. Executes the temporary bash script
  4. Streams output in real-time
  5. Cleans up the temporary file automatically

2. Real-time Output

Unlike utah compile, the run command streams output as it happens:

// slow-script.shx
for (let i: number = 1; i <= 5; i++) {
console.log("Step ${i} of 5");
`$(sleep 1)`; // Wait 1 second
}
console.log("Completed!");
utah run slow-script.shx
Step 1 of 5
Step 2 of 5
Step 3 of 5
Step 4 of 5
Step 5 of 5
Completed!

3. Error Propagation

Exit codes and errors are properly propagated:

// error-script.shx
console.log("Starting process...");

if (!fs.exists("required-file.txt")) {
console.log("Error: required-file.txt not found");
exit(1);
}

console.log("Process completed successfully");
utah run error-script.shx
echo "Exit code: $?"
Starting process...
Error: required-file.txt not found
Exit code: 1

Development Workflow

Quick Testing

Use utah run for rapid iteration during development:

# Edit script.shx
vim script.shx

# Test immediately
utah run script.shx

# Edit and test again
vim script.shx
utah run script.shx

Debug Output

Enable debug mode for detailed execution information:

export UTAH_DEBUG=1
utah run script.shx

This shows:

  • Compilation progress
  • Temporary file location
  • Execution timing
  • Cleanup status

Interactive Development

Combine with file watching for automatic execution:

# Install inotify-tools (Linux)
sudo apt install inotify-tools

# Watch and auto-run
while inotifywait -e modify script.shx; do
clear
echo "File changed, running..."
utah run script.shx
done

Advanced Features

Environment Variables

Scripts can access and modify environment variables:

// env-script.shx
console.log("Current PATH: ${PATH}");
console.log("Home directory: ${HOME}");

// Set environment variable for child processes
ENV["CUSTOM_VAR"] = "Hello from Utah";
`$(echo $CUSTOM_VAR)`;

Standard Input/Output

Scripts can read from stdin and write to stdout/stderr:

// input-script.shx
console.log("Enter your name:");
let name: string = console.prompt("Name: ");
console.log("Hello, ${name}!");
echo "Alice" | utah run input-script.shx

Process Management

Long-running scripts can be managed like normal processes:

# Run in background
utah run long-running-script.shx &
SCRIPT_PID=$!

# Check if still running
kill -0 $SCRIPT_PID 2>/dev/null && echo "Still running"

# Terminate if needed
kill $SCRIPT_PID

Performance Considerations

Compilation Overhead

Each utah run invocation includes compilation time:

File SizeCompilationExecutionTotal
< 100 lines50msVariable50ms + execution
100-500 lines150msVariable150ms + execution
500+ lines300ms+Variable300ms+ + execution

When to Use Run vs Compile

Use utah run for:

  • Development and testing
  • One-off script execution
  • Interactive scripting
  • Scripts that change frequently

Use utah compile for:

  • Production deployment
  • Scripts run multiple times
  • Performance-critical applications
  • When you need to inspect generated bash

Optimization Tips

  1. Minimize imports: Faster compilation
  2. Use caching: For scripts with heavy imports
  3. Profile execution: Identify bottlenecks
# Time the execution
time utah run script.shx

# Profile with detailed timing
UTAH_DEBUG=1 UTAH_PROFILE=1 utah run script.shx

Error Handling

Compilation Errors

If the script fails to compile, utah run stops with an error:

utah run broken.shx
❌ Compilation failed: Syntax error at line 5: Expected ';'

Runtime Errors

Runtime errors are shown with context:

// runtime-error.shx
let result: number = `$(date +%Y)` / 0; // Division by zero
utah run runtime-error.shx
/tmp/utah_temp_12345.sh: line 3: division by 0 (error token is "0")

Signal Handling

Scripts can be interrupted with Ctrl+C:

utah run infinite-loop.shx
# Press Ctrl+C to stop
^C

The temporary file is still cleaned up even after interruption.

Integration Examples

Testing Framework

#!/bin/bash
# test-runner.sh

echo "Running Utah script tests..."

test_files=(
"tests/unit/test-math.shx"
"tests/unit/test-strings.shx"
"tests/integration/test-api.shx"
)

failed=0

for test_file in "${test_files[@]}"; do
echo "Running $test_file..."

if utah run "$test_file"; then
echo "✅ $test_file passed"
else
echo "❌ $test_file failed"
((failed++))
fi
done

if [ $failed -eq 0 ]; then
echo "All tests passed!"
else
echo "$failed test(s) failed"
exit 1
fi

Build Automation

#!/bin/bash
# dev-workflow.sh

set -e

echo "Development workflow starting..."

# Run linting
echo "Checking code formatting..."
utah format src/*.shx --check

# Run tests
echo "Running tests..."
for test in tests/*.shx; do
utah run "$test"
done

# Run main script
echo "Running main application..."
utah run src/main.shx --env development

echo "Development workflow completed!"

Continuous Integration

# .github/workflows/utah-ci.yml
name: Utah CI

on: [push, pull_request]

jobs:
test:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3

- name: Install Utah
run: |
curl -sL https://utahshx.com/install.sh | sudo bash

- name: Run unit tests
run: |
for test in tests/unit/*.shx; do
echo "Running $test"
utah run "$test"
done

- name: Run integration tests
run: |
for test in tests/integration/*.shx; do
echo "Running $test"
utah run "$test"
done

- name: Run smoke tests
run: |
utah run scripts/server-health-check.shx

Troubleshooting

Common Issues

Issue: Script runs but produces no output

utah run silent-script.shx
# No output

Diagnosis: Check if script has console.log statements Solution: Add debug output to verify execution

Issue: Permission denied errors

utah run script.shx
# Permission denied accessing files

Diagnosis: Script may need elevated privileges Solution: Check file permissions or run with sudo if needed

Issue: Script hangs indefinitely

utah run hanging-script.shx
# Never terminates

Diagnosis: Infinite loop or waiting for input Solution: Use Ctrl+C to interrupt, check script logic

Debug Techniques

  1. Add logging:
console.log("Debug: Reached checkpoint 1");
console.log("Debug: Variable value is ${someVar}");
  1. Use temporary files:
fs.writeFile("/tmp/debug.log", "Current state: ${JSON.stringify(data)}");
  1. Check environment:
console.log("PWD: ${PWD}");
console.log("USER: ${USER}");
console.log("PATH: ${PATH}");

Performance Debugging

# Time compilation vs execution
time utah compile script.shx # Compilation time
time ./script.sh # Execution time
time utah run script.shx # Total time

# Memory usage
/usr/bin/time -v utah run script.shx

Best Practices

1. Use for Development

# Good: Quick testing during development
utah run dev-test.shx

# Good: Interactive debugging
utah run debug-helper.shx --verbose

# Avoid: Production deployment
# utah run production-script.shx # Use compile instead

2. Handle Arguments Properly

// Good: Validate arguments
if (!args.has("--required-param")) {
console.log("Error: --required-param is required");
args.showHelp();
exit(1);
}

// Good: Provide defaults
let timeout: number = args.has("--timeout") ? args.get("--timeout") : 30;

3. Provide Clear Output

// Good: Clear progress indication
console.log("Starting data processing...");
console.log("Processing file 1 of 5...");
console.log("✅ Processing completed successfully");

// Avoid: Silent operation
// processData(); // No feedback

4. Exit Gracefully

// Good: Clean exit with status
if (errorOccurred) {
console.log("❌ Operation failed: " + errorMessage);
exit(1);
}

console.log("✅ Operation completed successfully");
exit(0);

The run command provides an excellent development experience for Utah scripts, enabling rapid iteration and testing without the overhead of managing compiled files.