← Back to Home

Table of Contents

Getting Started

Script Mode

Run gosh scripts by passing the script file as an argument:

./gosh script.sh

Or directly with gosh:

gosh script.sh arg1 arg2

Cross-Platform Script Execution

Gosh provides seamless script execution across all platforms with platform-specific shebang handling:

Shebang Line

Start your scripts with a shebang line to specify the interpreter. Gosh handles shebangs differently on each platform:

Unix-like Platforms (Linux, macOS)

Shebangs are respected and scripts run with the specified interpreter:

#!/bin/bash            # Runs with bash
#!/usr/bin/env zsh     # Runs with zsh
#!/usr/bin/env python3 # Runs with python3
#!/usr/bin/env gosh    # Runs with gosh

Windows Platform

Shebangs are recognized but all scripts run through gosh for compatibility:

#!/usr/bin/env gosh    # Recommended for cross-platform scripts
#!/bin/gosh            # Direct path (recognized but runs through gosh)
#!/bin/sh              # Generic shell (recognized but runs through gosh)
#!/bin/bash            # Bash-compatible (recognized but runs through gosh)

Making Scripts Executable

On Unix/macOS:

chmod +x script.sh
./script.sh

On Windows:

No need to make scripts executable - gosh automatically detects and runs them! Gosh, that's convenient!

# All of these work on Windows:
./script.sh
gosh script.sh
script.sh  # If gosh is in PATH

Windows-Specific Features

Gosh provides excellent Windows support with automatic shell script execution - gosh, it's about time!

Automatic Script Detection

On Windows, gosh automatically detects and executes shell scripts that would normally fail with "not a valid Win32 application" errors:

Cross-Platform Script Examples

#!/usr/bin/env gosh
# This script works identically on Windows, macOS, and Linux
# Gosh, cross-platform scripting has never been easier!

echo "Running on Windows with gosh!"
echo "Script arguments: $@"

# Use cross-platform commands
ls -la          # Works on all platforms
echo "Files listed above"

# Windows-compatible commands also work
dir -l          # Windows-style listing
type README.md  # Windows-style file display

# Environment variables work the same way
export MY_VAR="cross-platform value"
echo "MY_VAR: $MY_VAR"

echo "Gosh darn it, that's some fine cross-platform scripting!"

Windows Path Handling

Gosh handles both forward slashes and backslashes on Windows:

# Both path styles work on Windows
cd ~/Documents     # Unix-style (works everywhere)
cd ~\Documents     # Windows-style (works on Windows)

# Script paths work with both separators
./scripts/build.sh      # Forward slashes
.\scripts\build.sh      # Backslashes

# Gosh automatically handles the differences!

No More "Not a Valid Win32 Application" Errors

Before gosh's Windows shell script support:

# This would fail on Windows:
C:\> script.sh
'script.sh' is not recognized as an internal or external command,
operable program or batch file.

# Or worse:
C:\> .\script.sh
Access is denied.

With gosh's automatic detection - gosh, it just works!

# All of these work seamlessly on Windows:
gosh> ./script.sh arg1 arg2
gosh> script.sh arg1 arg2
gosh> gosh script.sh arg1 arg2

# Output:
Hello from gosh script!
Arguments: arg1 arg2
Gosh, that was easy!

Basic Script Structure

Example Script

#!/usr/bin/env gosh
# Example gosh script - gosh, this is neat!

echo "Hello from gosh!"
pwd
export MY_VAR="test"  # Environment variable
local TEMP_COUNT=42   # Local variable (function scope)
echo "MY_VAR is: $MY_VAR"

# Pipe examples - gosh, pipes are powerful!
echo "testing pipe functionality" | wc -w
ls -la | head -5
cat /etc/passwd | grep root | wc -l

# Gosh darn it, that's some fine scripting!

Variable Expansion

Gosh supports several forms of variable expansion:

Variable Declaration Requirements

In gosh, all variables must be explicitly declared with either local or export:

# Local variables (current shell only)
local NAME="World"
local COUNT=42

# Environment variables (exported to child processes)
export MY_VAR="Hello"
export PATH="$PATH:/new/path"

Basic Variable Expansion

local NAME="World"  # Local variable
echo "Hello, $NAME!"
echo "Path: ${HOME}/Documents"

Environment Variables

# Set environment variables
export MY_VAR="Hello"
export PATH="$PATH:/new/path"

# Use environment variables
echo $MY_VAR
echo "Current PATH: $PATH"

Variable Usage Examples

# Project configuration
export PROJECT_NAME="my-app"  # Environment variable
export PROJECT_DIR="$HOME/projects/$PROJECT_NAME"  # Environment variable
local BUILD_DIR="$PROJECT_DIR/build"  # Local variable

# Create directory structure
mkdir -p $BUILD_DIR
cd $PROJECT_DIR
echo "Working on $PROJECT_NAME in $PROJECT_DIR"

Command Substitution

Execute commands and use their output in your scripts:

Using $(command) Syntax

# Store command output in variables
local CURRENT_DIR="$(pwd)"
export BUILD_DATE="$(date +%Y-%m-%d)"
echo "Current directory: $CURRENT_DIR"
echo "Today is $BUILD_DATE"
echo "Found $(ls *.txt | wc -l) text files in $(pwd)"

Using Backticks

# Store command output in variables
local FILE_COUNT="`ls | wc -l`"
export CURRENT_USER="`whoami`"
echo "Files: $FILE_COUNT"
echo "Current user: $CURRENT_USER"

Practical Examples

# Backup with timestamp
local DATE="$(date +%Y%m%d_%H%M%S)"  # Local variable
cp important.txt "backup_$DATE.txt"

# Dynamic file processing
for file in $(ls *.log); do
    echo "Processing $file..."
    local BASE_NAME="$(basename $file .log)"  # Local variable
    grep "ERROR" $file > "errors_$BASE_NAME.txt"
done

# System information
if command -v uname >/dev/null 2>&1; then
    local SYSTEM="$(uname -s)"  # Local variable
else
    local SYSTEM="Windows"  # Local variable
fi
export CURRENT_USER="$(whoami)"  # Environment variable
echo "System: $SYSTEM"
echo "User: $CURRENT_USER"
echo "Home: $HOME"

Tilde Expansion

Gosh expands tilde (~) to represent home directories with cross-platform path separator support:

# Expand to current user's home
ls ~/Documents
cp file.txt ~/backup/
cd ~

# Expand to specific user's home
cd ~user/projects
ls ~admin/logs

Windows Backslash Support

On Windows, gosh supports both forward slashes and backslashes for tilde expansion:

# Windows-style paths with backslashes
cd ~\Documents
ls ~\backup\
cp file.txt ~\Projects\myapp\

# Mixed separators work too
cd ~user\projects
ls ~admin\logs

Cross-Platform Compatibility

Your scripts can use either path separator style and work across platforms:

# Both styles work on Windows
cd ~/Documents    # Unix-style (works everywhere)
cd ~\Documents    # Windows-style (works on Windows)

# The path separator style is preserved in expansions
echo ~/path       # Expands to /home/user/path on Unix
echo ~\path       # Expands to C:\Users\user\path on Windows

Practical Examples

# Backup to home directory
tar -czf ~/backups/project_$(date +%Y%m%d).tar.gz .

# Configuration files (cross-platform)
cp config.ini ~/.config/myapp/
echo "export PATH=\$PATH:~/bin" >> ~/.bashrc

# Windows-specific examples
copy config.ini ~\AppData\Local\myapp\
xcopy /s source\* ~\backup\

Glob Patterns

Use wildcards for filename matching:

# Match all .go files
ls *.go

# Match single character
rm temp_file_?.txt

# Character classes
echo [Hh]ello*
ls [abc]*

# More complex patterns
find . -name "*.{js,ts,jsx,tsx}"
ls test_[0-9][0-9].log

Practical Examples

# Process all source files
for file in *.{c,cpp,h}; do
    echo "Compiling $file..."
    gcc -c $file
done

# Clean temporary files
rm -f *.tmp *.bak *~
rm -f test_*.log debug_[0-9]*.txt

Input/Output Redirection

Input Redirection

# Read from file
cat < input.txt
sort < unsorted.txt
wc -l < data.csv

Output Redirection

# Overwrite file
echo "Hello" > output.txt
ls -la > file_list.txt
date > timestamp.txt

# Append to file
echo "World" >> output.txt
date >> log.txt
echo "Process completed" >> status.log

Combining Redirection

# Sort a file and save result
sort < input.txt > sorted.txt

# Process and save
grep "ERROR" < application.log > errors.txt
cut -d',' -f1,3 < data.csv > names_scores.csv

Practical Examples

# Log script execution
echo "Script started at $(date)" > script.log
echo "Processing files..." >> script.log

# Generate reports
echo "Daily Report - $(date)" > daily_report.txt
echo "===================" >> daily_report.txt
ps aux | grep myapp >> daily_report.txt
df -h >> daily_report.txt

Pipes and Command Chaining

Using Pipes

# Chain commands with pipes
ls | grep ".go" | wc -l
cat file.txt | sort | uniq
ps aux | grep gosh | head -5

Command Chaining with Semicolons

# Execute commands sequentially
echo "First command"; echo "Second command"; pwd
ls; echo "Files listed"; pwd

# Combine with pipes and redirection
ls *.go > go_files.txt; cat go_files.txt | wc -l

Complex Pipeline Examples

# Log analysis
cat access.log | grep "$(date +%Y-%m-%d)" | cut -d' ' -f1 | sort | uniq -c | sort -nr

# File processing
find . -name "*.txt" | xargs grep "TODO" | cut -d: -f1 | sort | uniq

# System monitoring
ps aux | awk '{print $3, $11}' | sort -nr | head -10

Background Execution

Run commands in the background:

# Run command in background
sleep 10 &
long_running_process &

# Continue with other commands
echo "Background job started"
ps aux | grep sleep

Practical Examples

# Start multiple background tasks
echo "Starting background tasks..."
./data_processor input1.csv &
./data_processor input2.csv &
./data_processor input3.csv &

# Wait for completion
echo "Waiting for tasks to complete..."
wait
echo "All tasks completed"

Comments

Use comments to document your scripts:

#!/usr/bin/env gosh

# This is a comment
echo "This is not a comment"  # Inline comment

# Multi-line comments
# can span multiple lines
# like this

# Script configuration
export DEBUG="true"  # Environment variable
export LOG_LEVEL="info"  # Environment variable

# Main processing logic
if [ "$DEBUG" = "true" ]; then
    echo "Debug mode enabled"
fi

Functions

Gosh supports user-defined functions with full positional parameter support, enabling you to create reusable code blocks within your scripts.

Function Definition

Define functions using the standard shell syntax:

# Basic function definition
function_name() {
    # Function body
    echo "Hello from function!"
}

# Alternative syntax
function function_name {
    echo "Hello from function!"
}

Calling Functions

Call functions by name, optionally passing arguments:

# Call function without arguments
function_name

# Call function with arguments
greet "World" "from" "gosh"

Positional Parameters

Functions have access to special variables for handling parameters:

greet() {
    echo "Function name: $0"
    echo "First parameter: $1"
    echo "Second parameter: $2"
    echo "All parameters: $@"
    echo "Parameter count: $#"
}

# Call the function
greet "Alice" "Bob" "Charlie"
# Output:
# Function name: greet
# First parameter: Alice
# Second parameter: Bob
# All parameters: Alice Bob Charlie
# Parameter count: 3

Parameter Variables

Practical Examples

File Processing Function

process_file() {
    if [ $# -eq 0 ]; then
        echo "Usage: $0 "
        return 1
    fi
    
    local file="$1"  # Local variable (function scope)
    echo "Processing file: $file"
    
    if [ -f "$file" ]; then
        local line_count="$(wc -l "$file" | cut -d' ' -f1)"  # Local variable
        local todo_count="$(grep -c "TODO" "$file" || echo "0")"  # Local variable
        echo "Lines: $line_count"
        echo "TODOs: $todo_count"
    else
        echo "Error: File '$file' not found"
        return 1
    fi
}

# Usage
process_file "script.sh"
process_file "README.md"

Backup Function

backup_file() {
    local source="$1"  # Local variable (function scope)
    local backup_dir="${2:-./backups}"  # Local variable with default value
    
    if [ $# -eq 0 ]; then
        echo "Usage: $0  [backup_directory]"
        return 1
    fi
    
    mkdir -p "$backup_dir"
    local timestamp="$(date +%Y%m%d_%H%M%S)"  # Local variable
    local basename="$(basename $source)"  # Local variable
    cp "$source" "$backup_dir/$basename.$timestamp"
    echo "Backed up $source to $backup_dir"
}

# Usage
backup_file "important.txt"
backup_file "config.ini" "/home/user/backups"

System Information Function

system_info() {
    echo "=== System Information ==="
    local current_date="$(date)"  # Local variable
    local current_user="$(whoami)"  # Local variable
    local current_dir="$(pwd)"  # Local variable
    local disk_usage="$(df -h . | tail -1 | awk '{print $5}')"  # Local variable
    
    echo "Date: $current_date"
    echo "User: $current_user"
    echo "Directory: $current_dir"
    echo "Disk usage: $disk_usage"
    
    if [ $# -gt 0 ]; then
        echo "Additional info requested: $@"
    fi
}

# Usage
system_info
system_info "memory" "processes"

Nested Function Calls

Functions can call other functions, with each maintaining its own parameter context:

log_message() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
}

process_with_logging() {
    log_message "Starting process: $1"
    
    # Simulate processing
    echo "Processing $1..."
    sleep 1
    
    log_message "Completed process: $1"
}

# Usage
process_with_logging "data_file.csv"
process_with_logging "config_update"

Function Management

List Defined Functions

# List all defined functions
functions

# Show function definitions
type function_name

Complete Function Example

#!/usr/bin/env gosh
# Project management script with functions

# Configuration
export PROJECT_ROOT="$(pwd)"  # Environment variable
export LOG_FILE="project.log"  # Environment variable

# Logging function
log() {
    echo "[$(date '+%H:%M:%S')] $@" | tee -a "$LOG_FILE"
}

# Error handling function
error_exit() {
    log "ERROR: $1"
    exit 1
}

# Build function
build_project() {
    local target="${1:-all}"
    log "Building target: $target"
    
    case "$target" in
        "clean")
            rm -rf build/
            log "Clean completed"
            ;;
        "compile")
            mkdir -p build
            log "Compilation started"
            # Add compilation commands here
            log "Compilation completed"
            ;;
        "all")
            build_project clean
            build_project compile
            ;;
        *)
            error_exit "Unknown build target: $target"
            ;;
    esac
}

# Test function
run_tests() {
    log "Running tests with $# test files"
    
    for test_file in "$@"; do
        if [ -f "$test_file" ]; then
            log "Running test: $test_file"
            # Execute test file
            ."/$test_file" || error_exit "Test failed: $test_file"
        else
            log "Warning: Test file not found: $test_file"
        fi
    done
    
    log "All tests completed successfully"
}

# Main execution
log "Project script started"
build_project "$1"

if [ "$2" = "test" ]; then
    run_tests test_*.sh
fi

log "Project script completed"

Aliases in Scripts

Create and use aliases within scripts:

# Create aliases
alias ll="ls -la"
alias grep="grep --color=auto"
alias ..="cd .."

# Use aliases
ll
grep "pattern" file.txt
..

Best Practices

Script Structure

Error Handling

# Check if file exists before processing
if [ -f "input.txt" ]; then
    cat input.txt | process_data
else
    echo "Error: input.txt not found"
    exit 1
fi

Variable Usage

# Use quotes to handle spaces in filenames
local FILE_NAME="my document.txt"  # Local variable
cp "$FILE_NAME" "backup_$FILE_NAME"

# Use braces for clarity
local CURRENT_TIME="$(date)"  # Local variable
echo "Processing ${FILE_NAME} at $CURRENT_TIME"

Complete Example Script

#!/usr/bin/env gosh
# Project build script
# Usage: ./build.sh [clean|build|test]

# Configuration
export PROJECT_NAME="my-project"  # Environment variable
local BUILD_DIR="./build"  # Local variable
local SRC_DIR="./src"  # Local variable

# Functions
clean_build() {
    echo "Cleaning build directory..."
    rm -rf "$BUILD_DIR"
    mkdir -p "$BUILD_DIR"
}

build_project() {
    echo "Building $PROJECT_NAME..."
    # Copy source files
    cp -r "$SRC_DIR"/* "$BUILD_DIR"/
    
    # Process files
    find "$BUILD_DIR" -name "*.template" | while read file; do
        echo "Processing template: $file"
        sed "s/{{PROJECT_NAME}}/$PROJECT_NAME/g" "$file" > "${file%.template}"
        rm "$file"
    done
    
    echo "Build completed at $(date)"
}

run_tests() {
    echo "Running tests..."
    find "$BUILD_DIR" -name "*_test.sh" -exec {} \;
}

# Main script logic
case "$1" in
    clean)
        clean_build
        ;;
    build)
        clean_build
        build_project
        ;;
    test)
        run_tests
        ;;
    *)
        echo "Usage: $0 {clean|build|test}"
        exit 1
        ;;
esac

echo "Script completed successfully!"