Skip to content

ChristofBecu/posix-script-skeleton

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

POSIX-compliant script skeleton

POSIX Script Skeleton Logo

A POSIX-compliant command-line skeleton with file locking and state persistence. Fully renameable - the tool adapts to whatever you name the binary.

Features

  • Strict POSIX compliance (uses expr instead of $((...)), no bashisms)
  • Configurable lock scope (per-user or system-wide)
  • File-based locking with timeout support and PID ownership verification
  • Persistent state management
  • Modular architecture with library separation
  • Automatic directory creation for locks and state files
  • Support for both system-wide and per-user installation
  • Self-installing - no build tools required
  • Fully dynamic naming - rename bin/posix-script-skeleton to anything by cloning the tool, all paths adapt automatically

Requirements

  • POSIX /bin/sh
  • POSIX utilities only: expr, basename, dirname, mkdir, rm, sleep, printf, cat, id

Non-POSIX dependencies: none

Quick Start

Creating a New Tool from the Skeleton

Important: The original posix-script-skeleton is a template. Use --clone to create your own tool:

./bin/posix-script-skeleton --clone

This interactive command will:

  • Ask for your tool name
  • Ask for target directory
  • Copy the skeleton with the new name
  • Remove the skeleton marker (making it a standalone tool)
  • Guide you through next steps

After Cloning: Configure and Install

  1. Navigate to your new tool:

    cd /path/to/your-new-tool
  2. Edit the config to set your preferences:

    vi config

    Set LOCK_SCOPE and STATE_SCOPE to either "system" or "user"

  3. Implement your tool's logic:

    • Your own logic and arg parsing for the new tool goes into lib/core.sh or other custom files you create
    • usage function in lib/io.sh can be modified for help messages
    • Other files like lock, state, install, clone and bootstrap don't need modification unless you want to change core behavior
  4. Test your tool:

    ./bin/your-tool-name --help
    ./bin/your-tool-name
  5. Install based on config:

    # For user installation (LOCK_SCOPE="user")
    ./bin/your-tool-name --install
    
    # For system-wide installation (LOCK_SCOPE="system")
    sudo ./bin/your-tool-name --install

Installation

Note: The --install command is only available in cloned tools. The original skeleton must be cloned first using --clone.

Installing Your Cloned Tool

  1. Edit the config file to set your preferences:

    vi config

    Set LOCK_SCOPE and STATE_SCOPE to either "system" or "user"

  2. Install based on config:

    # For user installation (LOCK_SCOPE="user")
    ./bin/<your-tool-name> --install
    
    # For system-wide installation (LOCK_SCOPE="system")
    sudo ./bin/<your-tool-name> --install

System-wide installation

If your config has LOCK_SCOPE="system" or STATE_SCOPE="system":

sudo ./bin/<toolname> --install

This will install:

  • Binary: /bin/<toolname>
  • Config: /etc/<toolname>/config
  • Libraries: /etc/<toolname>/lib/
  • State directory: /var/tmp/<toolname>/
  • Lock file: /tmp/<toolname>.lock

Per-user installation

If your config has LOCK_SCOPE="user" and STATE_SCOPE="user":

./bin/<toolname> --install

This will install:

  • Binary: ~/.local/bin/<toolname>
  • Config: ~/.<toolname>rc
  • Libraries: ~/.local/lib/<toolname>/
  • State directory: ~/.local/state/<toolname>/
  • Lock file: /tmp/<toolname>.<uid>.lock

Note: Make sure ~/.local/bin is in your PATH.

Uninstallation

Note: The --uninstall command is only available from installed versions, not from source.

# From user installation
your-tool-name --uninstall

# From system-wide installation
sudo your-tool-name --uninstall

Note: State directories are preserved during uninstallation. Remove them manually if needed.

Development Mode

For development, you can run directly from the source directory without installation:

./bin/<toolname>

In development mode, the tool uses the local config file and stores state in data/.

Structure

The tool name adapts throughout the structure. Examples below use <toolname> as a placeholder for your actual tool name.

Installed (system-wide)

/bin/<toolname>                    Main binary
/etc/<toolname>/
├── config                         Configuration file
└── lib/                           Library modules
    ├── bootstrap.sh
    ├── core.sh
    ├── install.sh
    ├── io.sh
    ├── lock.sh
    └── state.sh
/var/tmp/<toolname>/               State files
└── <toolname>.state
/tmp/<toolname>.lock               Lock file

Installed (per-user)

~/.local/bin/<toolname>            Main binary
~/.<toolname>rc                    Configuration file
~/.local/lib/<toolname>/           Library modules
~/.local/state/<toolname>/         State files
└── <toolname>.state
/tmp/<toolname>.<uid>.lock         Lock file

Source (development)

<toolname>/
├── .skeleton-origin               Marker (original skeleton only)
├── bin/
│   └── <toolname>                 Main entry point
├── config                         Configuration file
├── lib/
│   ├── bootstrap.sh               Environment detection
│   ├── core.sh                    Core application logic
│   ├── clone.sh                   Clone functionality
│   ├── install.sh                 Installation logic
│   ├── io.sh                      Input/output utilities
│   ├── lock.sh                    File locking
│   └── state.sh                   State persistence
└── data/                          Runtime data (auto-created)
    └── <toolname>.<uid>.state     Per-user state files

Note: The .skeleton-origin marker file identifies the original skeleton and is removed during the clone operation.

Configuration

All configuration is done via the config file only. Environment variables do not override config settings.

The configuration file location depends on the mode:

  • System-wide install: Config at /etc/<toolname>/config
  • Per-user install: Config at ~/.<toolname>rc
  • Development mode: Config at ./config (in source directory)

Configuration Variables

Variable Values Default Description
LOCK_SCOPE user / system user Lock scope: per-user or system-wide
STATE_SCOPE user / system user State file scope
LOCK_TIMEOUT seconds 0 Lock timeout (0 = wait indefinitely)
LOCK_NONBLOCK 0 / 1 0 Exit immediately if locked (1) or wait (0)

Important: Edit the config file directly. Environment variables cannot override these settings.

Config File Detection

The tool automatically detects which config to use based on its installation:

  1. Development mode (running from source): Uses ./config
  2. User installation: Uses ~/.<toolname>rc
  3. System installation: Uses /etc/<toolname>/config

Usage

Available Commands

Commands available depend on the tool type and mode:

Original Skeleton (posix-script-skeleton)

./bin/posix-script-skeleton --clone    # Create a new tool
./bin/posix-script-skeleton --help     # Show help

Cloned Tool (Development Mode)

./bin/your-tool-name --install         # Install the tool
./bin/your-tool-name --help            # Show help
./bin/your-tool-name                   # Run the tool

Installed Tool

your-tool-name --uninstall             # Uninstall
your-tool-name --help                  # Show help
your-tool-name                         # Run the tool

Common Options

Option Description Available In
-h, --help Show help message All modes
--clone Clone skeleton with new name Original skeleton only
--install Install the tool Cloned dev mode only
--uninstall Uninstall the tool Installed mode only

Examples

# Clone the skeleton to create your tool
./bin/posix-script-skeleton --clone

# Test your cloned tool before installation
cd /path/to/your-tool
./bin/your-tool --help
./bin/your-tool

# Install as user (per-user locking)
./bin/your-tool --install

# After user installation
your-tool              # Per-user lock: /tmp/your-tool.1000.lock

# Run as different users simultaneously (user mode)
your-tool &            # User lock: /tmp/your-tool.1000.lock
sudo your-tool         # Root lock: /tmp/your-tool.0.lock

# Uninstall when done
your-tool --uninstall

Lock Behavior

Per-user locking (user installation default)

  • Each user gets isolated lock: /tmp/<toolname>.<uid>.lock
  • Multiple users can run simultaneously without blocking
  • State stored per-user: ~/.local/state/<toolname>/<toolname>.state
  • Lockfile contains process PID for ownership verification

System-wide locking (system installation default)

  • Single lock for all users: /tmp/<toolname>.lock
  • Only one instance runs across entire system
  • Root and users share the same lock
  • State shared system-wide: /var/tmp/<toolname>/<toolname>.state

Common features

  • Automatically creates lock directory if missing
  • Uses atomic file operations (set -C) for race-free locking
  • PID-based ownership verification prevents lock theft
  • Cleanup on EXIT, INT, TERM signals

State Management

  • Maintains run counter across executions
  • State scope configured via config file only
  • System install: /var/tmp/<toolname>/<toolname>.state
  • User install: ~/.local/state/<toolname>/<toolname>.state
  • Development mode: ./data/<toolname>.<uid>.state
  • Automatically initialized on first run
  • Automatically creates state directory if missing
  • Configure via STATE_SCOPE in config file

POSIX Compliance

This skeleton is strictly POSIX-compliant:

  • ✅ All arithmetic uses expr (not $((...)))
  • ✅ Command substitution uses $(...) consistently
  • ✅ No arrays, no [[ ]], no (( ))
  • ✅ No local keyword (uses underscore-prefix convention)
  • ✅ Uses printf instead of echo for portability
  • ✅ Exit codes: 0 (success), 1 (error)
  • ✅ Compatible with: dash, ash, ksh, and any POSIX shell

License

MIT License This is a skeleton/template project. Customize as needed for your use case.

About

A POSIX-compliant command-line skeleton with file locking and state persistence.

Resources

Stars

Watchers

Forks

Contributors

Languages