A simple, user-friendly command-line tool for managing symbolic links in ~/.local/bin.
✨ Simple & Intuitive - Easy-to-use commands with helpful prompts 🎨 Beautiful Output - Color-coded display with smart formatting 🔍 Link Management - List, create, inspect, and remove symbolic links 🛡️ Safety First - Validates inputs, confirms destructive operations 🔧 Maintenance Tools - Verify and fix broken links automatically 📊 Multiple Formats - Export to text, JSON, or CSV 🚀 Cross-Platform - Works on both macOS and Linux ⚡ Fast & Reliable - Built with bash best practices
- Installation
- Quick Start
- Commands
- Options
- Output Formats
- Configuration
- Examples
- Tips & Best Practices
- Troubleshooting
- Contributing
- License
The easiest way to install sym on macOS is using Homebrew:
brew install 11ways/sym/symThis automatically installs:
- The
symcommand in your PATH - The man page (accessible via
man sym) - All dependencies
Verify installation:
sym --version
man symFor a quick installation on any Unix-like system:
curl -fsSL https://raw.githubusercontent.com/11ways/sym/main/install.sh | bashOr safer (inspect the script first):
curl -fsSL https://raw.githubusercontent.com/11ways/sym/main/install.sh -o install.sh
less install.sh # Review the script
bash install.shThis installs to ~/.local/bin and builds the man page if pandoc is available.
For Linux users:
Homebrew also works on Linux! Install Homebrew first, then:
brew install 11ways/sym/symThe Makefile automates installation of both the script and man page:
# Clone the repository
git clone https://github.com/11ways/sym.git
cd sym
# Install to user directory (no sudo required)
make install-local
# Or install system-wide (requires sudo)
sudo make installWhat gets installed:
- Script:
~/.local/bin/sym(or/usr/local/bin/symfor system-wide) - Man page:
~/.local/share/man/man1/sym.1.gz(or/usr/local/share/man/man1/sym.1.gz)
View the man page:
# Add to your shell config (~/.bashrc or ~/.zshrc)
export MANPATH="$MANPATH:$HOME/.local/share/man"
# Then view the manual
man symRequirements:
pandoc- for building the man page (install instructions)gzip- usually pre-installed
- Download
sym.shto a directory of your choice - Make it executable:
chmod +x sym.sh - Add it to your PATH or create a symlink in
~/.local/bin
sym --version
# Output: sym version 1.0.1Homebrew:
brew uninstall symMakefile:
make uninstall-local # or make uninstall for system-wide# Create a symlink
sym mycommand /path/to/executable
# List all symlinks
sym ls
# Show information about a link
sym info mycommand
# Remove a link
sym rm mycommandCreate a symbolic link with an explicit name:
sym <link_name> <source_path>Or let sym prompt you for a name:
sym <source_path>
# You'll be prompted to enter a link nameExamples:
# Create a link to Sublime Text
sym sublime /Applications/Sublime\ Text.app/Contents/SharedSupport/bin/subl
# Create a link to a Python script (will prompt for name)
sym ~/scripts/my-tool.py
# Create multiple links
sym docker-compose /usr/local/bin/docker-compose
sym dc /usr/local/bin/docker-compose # Short aliasAlternative Syntax:
sym create <source_path> # Will prompt for name
sym create <link_name> <source_path>List all symbolic links:
sym lsShow only broken links:
sym ls --brokenOutput in different formats:
sym ls --format=json
sym ls --format=csvExample Output:
Existing symbolic links in '~/.local/bin':
notch → ~/Repos/wp-notch/notch.sh
sym → ~/Repos/sym/sym.sh
swatch → ~/swatch/swatch.sh
speak → ~/Repos/simple-speaker/speak.sh
km → /Applications/Keyboard Maestro.app/Contents/MacOS/keyboardmaestro
yap → ~/repos/slack-extract/yap (Does not exist!)
Show detailed information about a specific link:
sym info <link_name>Or use the shorthand:
sym <link_name>Example Output:
→ From: ~/.local/bin/sublime
→ To: /Applications/Sublime Text.app/Contents/SharedSupport/bin/subl
→ Created: 2025-10-31 14:23:45
→ Size: 2.4MB
→ Type: File
To delete:
sym rm sublime
To change the destination:
sym sublime <new_destination>
Remove a symbolic link (with confirmation):
sym rm <link_name>Aliases:
sym remove <link_name>
sym delete <link_name>Check all symbolic links and report their status:
sym verifyExample Output:
Verifying symbolic links...
✓ notch → ~/Repos/wp-notch/notch.sh
✓ sym → ~/Repos/sym/sym.sh
✗ yap → ~/repos/slack-extract/yap (broken)
⚠ Warning: Found 1 broken link(s) out of 3 total.
To remove broken links, run: sym fix
Aliases:
sym checkAutomatically remove all broken symbolic links:
sym fixThis will:
- Find all broken links
- Show you what will be removed
- Ask for confirmation
- Remove the broken links
Aliases:
sym cleanThese options work with all commands:
| Option | Short | Description |
|---|---|---|
--help |
-h |
Show help message |
--version |
-v |
Show version information |
--force |
-f |
Skip all confirmation prompts |
--dry-run |
-n |
Preview changes without making them |
For sym ls:
| Option | Description |
|---|---|
--broken |
Show only broken links |
--format=FORMAT |
Output format: text, json, or csv |
# Create without confirmation
sym -f mylink /path/to/file
# Preview what would happen
sym -n mylink /path/to/file
# Force remove without prompt
sym -f rm oldlink
# List broken links in JSON format
sym ls --broken --format=jsonHuman-readable, color-coded output perfect for terminal use.
sym lsMachine-readable format for scripting and integrations:
sym ls --format=json[
{
"name": "sublime",
"target": "/Applications/Sublime Text.app/Contents/SharedSupport/bin/subl",
"status": "ok",
"size": "2.4MB",
"created": "2025-10-31 14:23:45"
},
{
"name": "oldtool",
"target": "~/bin/removed-tool",
"status": "broken",
"size": "",
"created": "2025-09-15 10:30:22"
}
]Spreadsheet-friendly format:
sym ls --format=csvname,target,status,size,created
sublime,/Applications/Sublime Text.app/Contents/SharedSupport/bin/subl,ok,2.4MB,2025-10-31 14:23:45
oldtool,~/bin/removed-tool,broken,,2025-09-15 10:30:22
| Variable | Default | Description |
|---|---|---|
SYM_DIR |
~/.local/bin |
Directory where symlinks are created |
NO_COLOR |
(unset) | Disable colored output |
Set a different directory for your symlinks:
export SYM_DIR="$HOME/bin"
sym mylink /path/to/file# Temporarily
NO_COLOR=1 sym ls
# Permanently (add to ~/.bashrc or ~/.zshrc)
export NO_COLOR=1Make sure your symlink directory is in your PATH:
# Add to ~/.bashrc or ~/.zshrc
export PATH="$PATH:$HOME/.local/bin"Link to GUI Applications (macOS):
# Sublime Text
sym sublime /Applications/Sublime\ Text.app/Contents/SharedSupport/bin/subl
# Visual Studio Code
sym code /Applications/Visual\ Studio\ Code.app/Contents/Resources/app/bin/code
# Keyboard Maestro
sym km /Applications/Keyboard\ Maestro.app/Contents/MacOS/keyboardmaestroLink to Scripts:
# Python script
sym mytool ~/scripts/mytool.py
# Shell script
sym backup ~/scripts/backup.sh
# Node.js script
sym deploy ~/projects/deployer/cli.jsCreate Short Aliases:
# Long command with short alias
sym dc /usr/local/bin/docker-compose
sym k /usr/local/bin/kubectl
sym tf /usr/local/bin/terraformBatch Cleanup:
# Find and fix all broken links
sym verify
sym fix
# Or just fix directly
sym fix -f # Skip confirmationExport Link Inventory:
# Create a backup of all links
sym ls --format=json > ~/symlinks-backup.json
# Create a CSV for documentation
sym ls --format=csv > ~/symlinks-inventory.csvCheck if a link exists:
if sym info mylink &>/dev/null; then
echo "Link exists"
else
echo "Link doesn't exist"
fiProcess all broken links:
# Get broken links as JSON
broken_links=$(sym ls --broken --format=json)
# Process with jq
echo "$broken_links" | jq -r '.[].name' | while read link; do
echo "Broken: $link"
doneAutomated cleanup:
# Remove all broken links without prompts
sym fix -f✅ Good:
- Use lowercase names:
mycommand - Use hyphens for multi-word names:
my-command - Keep names short and memorable:
dcinstead ofdocker-compose-wrapper
❌ Avoid:
- Spaces in names:
my command - Special characters:
my@command,my$cmd - Starting with a dot:
.mycommand(hidden files)
- Keep it simple: Don't create too many symlinks
- Use descriptive names: Others should understand what each link does
- Regular maintenance: Run
sym verifyperiodically - Document custom links: Keep a README in
~/.local/bin
- Always review: Check
sym infobefore removing links - Use dry-run: Test with
-nflag before destructive operations - Backup important links: Export with
sym ls --format=json - Verify after changes: Run
sym verifyafter bulk operations
- Use absolute paths: Relative paths work but absolute are clearer
- Check your PATH: Ensure
~/.local/binis in PATH - Avoid deep nesting: Keep source files in accessible locations
Problem: Created a link but can't use it.
Solution:
# Check if directory is in PATH
echo $PATH | grep -q "$HOME/.local/bin" || echo "Not in PATH!"
# Add to PATH (in ~/.bashrc or ~/.zshrc)
export PATH="$PATH:$HOME/.local/bin"
# Reload shell
source ~/.bashrc # or source ~/.zshrcProblem: Can't create or remove links.
Solution:
# Check directory permissions
ls -ld ~/.local/bin
# Fix permissions
chmod 755 ~/.local/bin
# If directory doesn't exist
mkdir -p ~/.local/binProblem: Output is not colored.
Solution:
# Check if NO_COLOR is set
echo $NO_COLOR
# Unset it
unset NO_COLOR
# Check terminal type
echo $TERM
# If TERM is "dumb", use a better terminalProblem: Links break when source files move.
Solution:
# Find all broken links
sym ls --broken
# Fix by recreating
sym rm oldlink
sym oldlink /new/path/to/file
# Or remove all broken links
sym fixProblem: sym: command not found
Solution:
# Make script executable
chmod +x sym.sh
# Or use bash explicitly
bash sym.sh --helpsym uses meaningful exit codes for scripting:
| Code | Meaning |
|---|---|
0 |
Success |
1 |
General error |
2 |
Not found (file or link doesn't exist) |
3 |
Permission denied |
4 |
Invalid argument |
Example Usage:
if sym info mylink; then
echo "Link exists"
else
case $? in
2) echo "Link not found" ;;
3) echo "Permission denied" ;;
*) echo "Unknown error" ;;
esac
fi# Set custom directory
export SYM_DIR="$HOME/bin"
# All commands now use ~/bin
sym mylink /path/to/file# Count total links
sym ls --format=csv | tail -n +2 | wc -l
# Find links to a specific path
sym ls | grep "/Applications"
# Export only broken links
sym ls --broken --format=json > broken-links.jsonWeekly maintenance script:
#!/bin/bash
# ~/.local/scripts/sym-maintenance.sh
echo "Checking symbolic links..."
sym verify
echo "Cleaning up broken links..."
sym fix -f
echo "Exporting inventory..."
sym ls --format=json > ~/Dropbox/symlinks-backup.json
echo "Done!"Add to cron:
# Run every Sunday at 9 AM
0 9 * * 0 /bin/bash ~/.local/scripts/sym-maintenance.sh✅ Fully supported
- Uses BSD stat commands
- Handles .app bundles correctly
- Supports long paths with spaces
✅ Fully supported
- Uses GNU stat commands
- Works with all distributions
- Supports systemd paths
✅ Supported via WSL (Windows Subsystem for Linux)
- Use Linux commands in WSL
- Can link to Windows executables
- Requires proper PATH configuration
- Fast operations: All commands run in milliseconds
- No dependencies: Pure bash, no external tools required
- Efficient sorting: Uses native bash sorting
- Optional features:
numfmtused for better file sizes if available
Contributions are welcome! Here's how you can help:
- Report bugs: Open an issue on GitHub
- Suggest features: Share your ideas in discussions
- Submit pull requests: Fix bugs or add features
- Improve documentation: Help make the docs better
- Share feedback: Let us know how you use sym
# Clone the repo
git clone https://github.com/11ways/sym.git
cd sym
# Make changes
vim sym.sh
# Test your changes
./sym.sh --version
./sym.sh ls
# Run with dry-run to test safely
./sym.sh -n mytest /path/to/file- Use
bash(notsh) - Follow existing formatting
- Add comments for complex logic
- Use meaningful variable names
- Handle errors gracefully
MIT License - see LICENSE file for details.
Copyright (c) 2025 Roel van Gils
- Inspired by the need for simple symlink management
- Built with love for the command-line
- Thanks to all contributors and users
- Issues: https://github.com/11ways/sym/issues
- Discussions: https://github.com/11ways/sym/discussions
- Email: your-email@example.com
Made with ❤️ by Roel Van Gils
If you find this tool useful, consider giving it a ⭐ on GitHub!