Like npx and uvx, but for shell scripts.
shurl is a minimal tool that fetches and executes shell scripts from URLs. Think of it as curl <url> | bash but with caching, GitHub shorthand, proper argument passing, and safety features.
If you're familiar with:
npx- runs npm packages without installationuvx- runs Python tools without installationdeno run- runs TypeScript/JavaScript from URLs
Then shurl is the shell script equivalent.
# Install shurl (one-liner)
curl -sSL https://raw.githubusercontent.com/day50-dev/shurl/main/install.sh | bash
# Run a script
shurl gh:day50-dev/shurl/examples/hello.sh
# Install a tool globally (like pipx/uvx install)
shurl --install gh:user/repo/tool.sh
# Preview before running (safety first!)
shurl --dry-run gh:day50-dev/shurl/examples/hello.sh
# Force fresh download
shurl --update gh:day50-dev/shurl/examples/hello.sh# One-line install (auto-detects macOS/Linux)
curl -sSL https://raw.githubusercontent.com/day50-dev/shurl/main/install.sh | bashDirect download:
# To ~/.local/bin (Unix standard)
curl -sSL https://raw.githubusercontent.com/day50-dev/shurl/main/shurl -o ~/.local/bin/shurl
chmod +x ~/.local/bin/shurl
# Ensure ~/.local/bin is in your PATH
export PATH="$HOME/.local/bin:$PATH"With custom location:
# Install to specific directory
INSTALL_DIR=/opt/bin curl -sSL https://raw.githubusercontent.com/day50-dev/shurl/main/install.sh | bash
# macOS Homebrew users
INSTALL_DIR=/opt/homebrew/bin curl -sSL https://raw.githubusercontent.com/day50-dev/shurl/main/install.sh | bashshurl --version
shurl --help# Run any shell script from a URL
shurl https://example.com/script.sh
# Pass arguments to the script
shurl https://example.com/tool.sh install --verbose arg1 arg2Like pipx install or uvx install, install scripts as standalone commands:
# Install a tool to ~/.local/bin
shurl --install gh:user/repo/tool.sh
# Force fresh download and install
shurl --update --install gh:user/repo/tool.sh
# Preview what would be installed
shurl --dry-run --install gh:user/repo/tool.shAfter installation, you can run the tool directly:
# Install it once
shurl --install gh:user/repo/weather-cli.sh
# Use it like any other command
weather-cli new-yorkThe gh: prefix expands GitHub URLs automatically:
# These are equivalent:
shurl gh:user/repo/script.sh
shurl https://raw.githubusercontent.com/user/repo/main/script.sh
# Specify a branch with @ syntax
shurl gh:user/repo@develop/setup.sh
shurl gh:user/repo@v1.2.3/install.sh
# Nested paths work too
shurl gh:docker/compose/contrib/completion/bash/docker-compose# Preview what would happen without executing
shurl --dry-run gh:user/repo/script.sh
shurl -n https://example.com/install.sh # -n is short for --dry-run
# Preview with arguments
shurl --dry-run gh:user/repo/setup.sh --verbose --forceDry-run shows:
- What URL would be downloaded
- Where it would be cached
- What arguments would be passed
- First 10 lines of the script (if cached)
- Cache directory information
# Force fresh download (ignore cache)
shurl --update gh:user/repo/script.sh
shurl -u https://example.com/install.sh # -u is short for --update
# Update and run with arguments
shurl --update gh:company/tools/deploy.sh --env production
# Preview what would be updated
shurl --dry-run --update gh:user/repo/setup.sh# Clear the cache
shurl --clear-cache
# View cache location
echo $SHURL_CACHE
# Custom cache directory
SHURL_CACHE=/tmp/my-cache shurl gh:user/repo/script.sh# Basic hello world with the day50 logo
shurl gh:day50-dev/shurl/examples/hello.sh
# Colorful demonstration
shurl gh:day50-dev/shurl/examples/colors.sh
# Force update example
shurl --update gh:day50-dev/shurl/examples/hello.sh# Common installers (hypothetical)
shurl https://get.docker.com
shurl https://sh.rustup.rs
# Development workflows
shurl gh:myteam/scripts/dev-setup.sh
shurl --update gh:org/tools@develop/deploy.sh --env production # Get latest
# Install and use tools permanently
shurl --install gh:cli/tools/weather.sh
weather --location "New York"
# CI/CD pipelines
shurl -n gh:external/vendor/install.sh # Preview before running!# GitHub Actions example
- name: Setup environment
run: |
# Always get latest version
shurl --update gh:myorg/ci-scripts/ubuntu-setup.sh
shurl gh:myorg/ci-scripts/install-deps.sh
- name: Deploy with safety checks
run: |
# Safety check first
shurl --dry-run gh:myorg/deploy-scripts/deploy.sh ${{ github.ref_name }}
# Then run for real
shurl --update gh:myorg/deploy-scripts/deploy.sh ${{ github.ref_name }}- Parse input: Expands
gh:shorthand to full GitHub URLs - Check cache: Looks in platform-appropriate cache directory
- Check --update: If specified, deletes cached version
- Download if needed: Uses
curlorwgetto fetch script - Cache: Saves with SHA256 hash as filename
- Execute: Makes executable and runs with arguments
- Install: When using
--install, copies to~/.local/binfor permanent use
- Installs to:
~/.local/bin(preferred) or/usr/local/bin - Cache directory:
~/Library/Caches/shurl - Install location:
~/.local/binfor--install - Add to PATH: Add to
~/.zshrcor~/.bash_profile
- Installs to:
~/.local/bin(XDG standard) or/usr/local/bin - Cache directory:
~/.cache/shurl(XDG_CACHE_HOME) - Install location:
~/.local/binfor--install - Add to PATH: Add to
~/.bashrcor~/.zshrc
- Falls back to
~/.local/binand~/.cache/shurl
- No pipe execution: Unlike
curl | bash, scripts are saved to disk first - Cache inspection: You can review cached scripts at any time
- Dry-run mode: Preview before execution
- Update control: Choose when to get fresh versions
- Explicit permissions: Scripts are made executable only when run
- Install verification:
--dry-runshows what would be installed
# Always preview unknown scripts
shurl --dry-run https://unknown.com/script.sh
# Preview before installing
shurl --dry-run --install gh:new/tool/installer.sh
# Force updates for critical scripts
shurl --update gh:security/patches/apply.sh
# Review cached scripts
ls -la ~/.cache/shurl/ # Linux
ls -la ~/Library/Caches/shurl/ # macOS
# Review installed tools
ls -la ~/.local/bin/ | grep ".sh"
# Clear cache if unsure
shurl --clear-cache- Use
shurl <url>: For one-time scripts, installers, or ephemeral tasks - Use
shurl --install <url>: For tools you'll use regularly, similar topipx install - Use
--dry-runfirst: Always preview before installing
| Variable | Default | Purpose |
|---|---|---|
SHURL_CACHE |
Platform-specific | Custom cache directory |
| Script env vars | Passed through to executed scripts |
# Remove the binary
rm ~/.local/bin/shurl # or wherever installed
# Clear cache (optional)
rm -rf ~/.cache/shurl # Linux/BSD
rm -rf ~/Library/Caches/shurl # macOS
# Remove installed tools (optional)
# Check what was installed via shurl
ls -la ~/.local/bin/ | grep ".sh"Safer than curl | bash but you should still:
- Use
--dry-runto preview scripts - Use
--dry-run --installto preview installations - Review scripts from untrusted sources
- Clear cache if something seems suspicious
--update: Updates one specific script--clear-cache: Removes ALL cached scripts
For private GitHub repos, you'll need to add authentication:
# With GitHub token
GITHUB_TOKEN=your_token shurl https://raw.githubusercontent.com/private/repo/main/script.shBetter: Create a wrapper script that adds auth headers.
Ensure ~/.local/bin is in your PATH:
# Temporary fix
export PATH="$HOME/.local/bin:$PATH"
# Permanent fix (add to your shell config)
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc # or ~/.zshrcWhen you use --install, shurl:
- Downloads and caches the script (like normal)
- Extracts a clean name from the URL (removes
.shextension) - Copies it to
~/.local/bin/ - Makes it executable
- Shows you:
"tool-name is now available in /home/user/.local/bin"
Found a bug? Want a feature? Contributions welcome!
- Fork the repo: https://github.com/day50-dev/shurl
- Create a feature branch
- Submit a pull request
MIT License - see LICENSE
- npx - npm package runner
- uvx - Python tool runner from Astral
- deno run - Run code from URLs
- basher - Package manager for shell scripts
Made with ❤️ by DA`/50
shurl --update --dry-run gh:day50-dev/shurl/examples/hello.sh
shurl --install gh:day50-dev/shurl/examples/hello.sh