git clone --depth 1 https://github.com/aiya000/bash-toys.git /path/to/bash-toysIf using bash:
echo 'source /path/to/bash-toys/source-all.sh' >> ~/.bashrcIf using zsh:
echo 'source /path/to/bash-toys/source-all.sh' >> ~/.zshrcFor detailed documentation with examples and options, see:
- doc/main.md - Overview and main entry point
- doc/bin.md - Executable commands in
./bin/ - doc/sources.md - Source functions in
./sources/
- doc/bin.md - Executable commands in
See also 'Scripts' section for what is bin and sources.
For a complete list of scripts, visit ./bin and ./sources.
πΉ Executables (./bin)
bash-toys scripts have minimal dependencies. These dependencies are documented at the beginning of the script.
| Script | Description | Quick Example | Test |
|---|---|---|---|
bak |
Toggle backup (.bak) extension for files | $ bak file.txt # mv to file.bak.txt |
|
bookmark-open |
(WIP) Opens a selected bookmark in the default browser | $ bookmark-open # select & open |
|
calc-japanese-remaining-working-hours |
Calculate required daily working hours for remaining business days | $ calc-japanese-remaining-working-hours 80:00 # 80h worked in this month |
|
cat-which |
A shorthand for cat $(which cmd). Uses bat if available |
$ cat-which rm-dust # show source |
|
clamdscan-full |
Performs a full virus scan using ClamAV | $ clamdscan-full / # scan root |
|
ctags-auto |
Automatically determine git project and generate ctags | $ ctags-auto # generate tags |
|
date-diff-seconds |
Calculate time difference in minutes between two times | $ date-diff-seconds 21:47 22:33 # => 46 mins |
|
date-diff-seconds-now |
Calculate time difference between given time and now | $ date-diff-seconds-now 22:30 # mins until |
|
docker-attach-menu |
Attach to a Docker container selected from interactive menu | $ docker-attach-menu # select & attach |
|
docker-kill-menu |
Kill a Docker container selected from interactive menu | $ docker-kill-menu # select & kill |
|
expects |
A smaller test API like jest for bash script | $ expects "$x" to_be 10 # assert x=10 |
|
fast-sync |
Efficiently sync files from source to target by comparing file lists | $ fast-sync /src /dst # sync new only |
|
gh-issue-view-select |
Show GitHub issues in interactive filter and open selected issue | $ gh-issue-view-select # select & view |
|
gh-run-view-latest |
Show the latest GitHub Actions run preview and log | $ gh-run-view-latest # view latest run |
|
git-root |
Shows the git root directory of the current directory | $ git-root # => /path/to/repo |
|
is-in-wsl |
Check if current shell is running in WSL | $ is-in-wsl && echo WSL # detect WSL |
|
kill-latest-started |
Kill the latest started background process | $ kill-latest-started # kill last bg |
|
kill-list |
Display and kill processes selected from interactive menu | $ kill-list # select & kill |
|
list-dpkg-executables |
List executable files provided by a dpkg package | $ list-dpkg-executables git # list bins |
|
notify |
Send desktop notification with title and message | $ notify "Title" "Msg" # show popup |
|
notify-at |
Send notification at specified time with flexible date formats | $ notify-at 12:00 "T" "M" # at noon |
|
notify-at-at |
Send notification at specified time using at command (Linux/WSL) | $ notify-at-at 12:00 "T" "M" # Linux |
|
notify-at-launchd |
Send notification at specified time using launchd (macOS) | $ notify-at-launchd 12:00 "T" "M" # macOS |
|
notify-cascade |
Send cascade of notifications at specified intervals before target time | $ notify-cascade 15:00 "M" "M" 30m 5m # 30m,5m before |
|
notify-ntfy |
Send notification to mobile via ntfy.sh | $ notify-ntfy "T" "M" # to mobile |
|
pathshorten |
Abbreviate file path with shortened parent directories | $ pathshorten ~/Documents/Proj # => ~/Docu/Proj |
|
peco-reverse |
Reverse order interactive filter using peco | $ ls | peco-reverse # reversed filter |
|
photoframe |
Display photos in fullscreen slideshow mode using feh | $ photoframe ~/Pictures # slideshow |
|
pomodoro-timer |
A simplest Pomodoro Timer implementation in shell script | $ pomodoro-timer 25 # start 25min |
|
rm-dust |
An alternative to rm, moving files to a dustbox instead. Use --restore to recover files. alias rm=rm-dust is recommended! |
$ rm-dust file.txt # mv to dustbox |
|
run-wait-output |
Run two commands sequentially, with second triggered after first becomes silent | $ run-wait-output 1000 "npm watch" "echo Done" # after silent |
|
skip |
Skip n-lines from the beginning of output | $ seq 10 | skip 3 # => 4,5,...,10 |
|
slice |
Slice fields from input lines | $ echo "a,b,c" | slice , 2 3 # => b,c |
|
start |
Starts a process in the background without output | $ start firefox # run silently |
|
take-until-empty |
Takes input lines until a blank line appears | $ cat file | take-until-empty # stop at blank |
|
vim-configure |
Executes ./configure for Vim source with modern flags |
$ vim-configure # run ./configure |
|
vim-configure-debug |
Execute Vim source configure script with debug flags | $ vim-configure-debug # with debug |
|
vim-configure-macos |
Execute Vim source configure script with modern macOS flags | $ vim-configure-macos # for macOS |
πΉ Sources (./sources)
'Sources' are utility scripts that affect the parent shell (like the cd command).
| Script | Description | Quick Example | Test |
|---|---|---|---|
alias-of |
Creates an alias only if the command exists | $ alias-of rg 'rg --color always' # if rg exists |
|
cd-finddir |
Shows directories and cd to a selected one via interactive filter |
$ cd-finddir # fuzzy cd |
|
cd-to-git-root |
Change directory to the git root, with WSL path recovery support | $ cd-to-git-root # cd to repo root |
|
cd-to-node-root |
Change directory to the nearest parent directory containing package.json |
$ cd-to-node-root # cd to pkg dir |
|
contains-value |
Checks if an array contains a value | $ contains-value "${arr[@]}" "val" # check in arr |
|
define-alt |
Defines a shell variable named 'foo' if not defined | $ define-alt EDITOR vim # set if unset |
|
define-alt-export |
Similar to define-alt, but for environment variables |
$ define-alt-export EDITOR vim # export if unset |
|
force-unexport |
Unexports an environment variable | $ force-unexport MY_VAR # remove env |
|
get-var |
Read and output the value of a variable by name | $ get-var HOME # => /home/user |
|
i-have |
Check if a specified command exists in the system | $ i-have bat && echo yes # check cmd |
|
is-array |
Detect if a variable is an array (supports Bash and Zsh) | $ is-array arr && echo yes # check array |
|
load-my-env |
Load environment-specific settings and aliases for various tools and runtimes | $ load-my-env # load settings |
|
nvim-parent-edit |
Open files in parent Neovim instance via RPC from child terminal | $ nvim-parent-edit file.txt # edit in parent |
|
source-if-exists |
Conditionally source a file if it exists | $ source-if-exists ~/.local.sh # source if exists |
Here are some scripts that can boost your daily workflow:
File Operations
rm-dust - Safe alternative to rm. Never lose files by accident again!
$ rm-dust important.txt # Moves to dustbox instead of deleting
$ rm-dust *.log # Clean up logs safely
$ alias rm=rm-dust # Recommended: replace rm globally
$ rm-dust --restore # Interactively restore files from dustboxbak - Quick backup toggle. One command to backup, one to restore.
$ bak config.yaml # Creates config.bak.yaml
$ vim config.yaml # Edit the original
$ bak config.yaml # Restore from backup if needednvim-parent-edit - Edit files in parent Neovim from nested terminal. (Show also an example: nvim.lua, keymaps.lua)
# Inside Neovim's :terminal
$ nvim-parent-edit file.txt # Opens in parent Neovim, not a nested instance (not Neovim in Neovim)
$ nvim-parent-edit *.js # Open multiple filesfast-sync - Efficient file sync by comparing file lists first.
$ fast-sync --init ~/photos # Initialize sync state
$ fast-sync ~/photos /backup # Only syncs new/changed files
# Much faster than full rsync for large directoriesTesting
expects - Jest-like assertions for shell scripts. Write readable tests!
Many tests in this project (./test) are written with expects.
$ x=10 ; expects "$x" to_be 42
FAIL: expected {actual} to_be '42', but {actual} is '10'
$ x=42
$ expects "$x" to_be 42 # No output on success (exit 0)
$ expects "$x" to_be 42 && echo "PASS" # Equality check
PASS
$ expects "$x" not to_be 0 && echo "PASS" # Negation
PASS
$ expects "hello world" to_contain "world" # String containmentAnd more assertions are available.
See expects.
Notifications & Timers
notify - Simple desktop notification. Works on macOS, Linux, and WSL.
$ notify "Build Done" "Your project compiled successfully"
$ make && notify "Success" "Build complete" || notify "Failed" "Build error"notify-ntfy - Send notifications to your phone via ntfy.sh.
$ notify-ntfy "Backup Done" "Server backup completed" # Sends to mobile
$ long-running-task && notify-ntfy "Done" "Task finished"
# Requires: export BASH_TOYS_NTFY_TOPIC="your-topic-name"
# See https://ntfy.sh for setup
# See ./bin/notify-ntfy for usagenotify-at - Schedule notifications with human-friendly time formats. (A wrapper for notify, and at command (Linux) or launchd (macOS).)
$ notify-at 15:00 "Meeting" "Team standup starting" # Show notification to desktop at 3 PM
$ notify-at 15:00 "Meeting" "Team standup starting" --local # --local (Show notification to desktop) by default (Same as above)
$ notify-at "01-15 09:00" "Reminder" "Project deadline" # Show notification to desktop on Jan 15 at 9 AM
$ notify-at 12:00 "Lunch" "Take a break" --mobile # Send notification to mobile via ntfy.sh (if want to send both mobile and desktop, See below)
$ notify-at 18:00 "Dinner" "Cook" 1h --mobile --local # Show/Send notification to both mobile and desktopnotify-cascade - Get reminded at multiple intervals before an event. (A wrapper for notify-at.)
$ notify-cascade 15:00 "Meeting" "Standup" 30m 10m 5m # Show notification at 14:30, 14:50, and 14:55
$ notify-cascade 18:00 "Dinner" "Cook" 1h 30m --mobile # Send notification to mobile (requires notify-ntfy setup)
$ notify-cascade 18:00 "Dinner" "Cook" 1h 30m --local # Desktop only (default)
$ notify-cascade 18:00 "Dinner" "Cook" 1h --mobile --local # Both mobile and desktoppomodoro-timer - Simple Pomodoro technique timer with notifications.
$ pomodoro-timer # Default 30 minutes
$ pomodoro-timer 25 # Classic 25-minute pomodoro
$ pomodoro-timer --rest 5 # 5-minute break timerText Processing
skip & slice - Simple but powerful text manipulation.
$ cat data.csv | skip 1 # Skip header row
$ echo "a,b,c,d" | slice , 2 3 # Extract fields 2-3: "b,c"
$ ps aux | skip 1 | slice ' ' 1 2 # Get PID and user columnstake-until-empty - Read until blank line. Perfect for parsing sections.
$ cat changelog.md | take-until-empty # Get first section only
$ git log --format="%B" -1 | take-until-empty # Get commit title onlypathshorten - Shorten paths like Vim's pathshorten(). Great for prompts!
$ pathshorten ~/Documents/Projects/myapp/src # => ~/Docu/Proj/myap/src
$ PS1="\$(pathshorten \$PWD) $ " # Use in bash promptNavigation & Development
cd-finddir - Fuzzy directory navigation. Never type long paths again!
$ cd-finddir # Shows directory picker
...
luarrow.lua/src/
luarrow.lua/spec/
luarrow.lua/scripts/
luarrow.lua/luarrow.bak.lua/
luarrow.lua/doc/
luarrow.lua/
chotto.lua/src/
chotto.lua/spec/
chotto.lua/scripts/
chotto.lua/readme/
chotto.lua/doc/
chotto.lua/
...
> (Type partial name to filter, select to cd)cd-to-git-root & git-root - Quick access to repository root.
$ cd-to-git-root # Jump to repo root from anywhere
$ cat $(git-root)/README.md # Reference files from repo rootcd-to-node-root - Jump to nearest package.json directory.
$ pwd
/project/src/components/ui
$ cd-to-node-root
$ pwd
/project # Jumped to package.json directory!
$ npm test # Now you can run npm commandscat-which - Instantly view any script's source code.
$ cat-which rm-dust # See how rm-dust works
$ cat-which my-script # Debug your own scriptsBackground Processes
start - Launch GUI apps without terminal noise.
$ start firefox # Opens Firefox, returns prompt immediately
$ start code . # Open VS Code without blocking
$ start vlc music.mp3 # Play music in backgroundkill-list - Interactive process killer. No more memorizing PIDs!
$ kill-list
PID START COMMAND
...
12345 Thu Jan 30 10:15:00 2025 node server.js
12346 Thu Jan 30 10:15:01 2025 npm run watch
12347 Thu Jan 30 10:20:30 2025 python script.py
...
> Select process to kill (fuzzy search, multi-select with Tab)Security
clamdscan-full - Full system virus scan with ClamAV.
$ clamdscan-full / # Scan entire system
$ clamdscan-full ~/Downloads # Scan specific directory
# Requires ClamAV daemon (clamd) runningMost commands support --help option:
$ rm-dust --help
rm-dust - Alternative to rm that moves files to dustbox instead of deletion
Usage:
rm-dust FILE...
rm-dust --help
...If a command doesn't have --help, use bash-toys-help to extract help from script comments:
$ bash-toys-help rm-dust
# For 'source' commands (don't forget .sh extension)
$ bash-toys-help cd-to-git-root.sh
# Disable markdown rendering
$ bash-toys-help --disable-glow rm-dustIn this section, we assumed you are using bash and ~/.bashrc.
If you are using zsh, replace ~/.bashrc with ~/.zshrc.
- Clone the repository
$ git clone --depth 1 https://github.com/aiya000/bash-toys.git /path/to/bash-toys- Source the
source-all.shscript in your.bashrcor.zshrc
$ echo 'source /path/to/bash-toys/source-all.sh' >> ~/.bashrc- (Optional) Configure options if necessary
$ vim /path/to/bash-toys/define-options.shOr set in your .bashrc:
export BASH_TOYS_DUSTBOX_DIR="$HOME/dustbox"
export BASH_TOYS_MUSIC_PLAYER='afplay /System/Library/Sounds/Funk.aiff'
export BASH_TOYS_MUSIC_PLAYER_OPTIONS=''Example configuration
export BASH_TOYS_INTERACTIVE_FILTER=fzf
export BASH_TOYS_DUSTBOX_DIR="$HOME/dustbox"
export BASH_TOYS_BATCAT_OPTIONS=''Please see ./define-options.sh and configure your options as needed.
vlc: Forpomodoro-start(if$BASH_TOYS_MUSIC_PLAYERis set to the default value)
Here is how to install individual tools.
- Create base directory
$ mkdir -p ~/lib/bash-toys || true
$ echo 'export PATH=$PATH:~/lib/bash-toys' >> ~/.bashrc- (Optional) Install dependencies if needed
$ curl https://raw.githubusercontent.com/aiya000/bash-toys/refs/heads/main/lib/fun.sh -o ~/lib/bash-toys/fun.sh
$ echo 'source ~/lib/bash-toys/fun.sh' >> ~/.bashrc- (Optional) Configure environment variables if necessary
Some scripts require environment variables to be configured. You can either:
Download and source define-options.sh:
$ curl https://raw.githubusercontent.com/aiya000/bash-toys/refs/heads/main/define-options.sh -o ~/lib/bash-toys/define-options.sh
$ echo 'source ~/lib/bash-toys/define-options.sh' >> ~/.bashrcOr set the variables directly in your .bashrc (or .zshrc for zsh):
export BASH_TOYS_INTERACTIVE_FILTER=fzf
export BASH_TOYS_DUSTBOX_DIR="$HOME/dustbox"
export BASH_TOYS_BATCAT_OPTIONS=''- Install a tool you want
$ curl https://raw.githubusercontent.com/aiya000/bash-toys/refs/heads/main/bin/bak -o ~/bin/bakFor ./sources/*, don't forget to source:
$ curl https://raw.githubusercontent.com/aiya000/bash-toys/refs/heads/main/sources/cd-to-git-root.sh -o /path/to/sources/cd-to-git-root.sh
$ echo 'source /path/to/sources/cd-to-git-root.sh' >> ~/.bashrcWe welcome contributions! Please follow these steps.
- Create an issue for the feature you want to add
- Wait for maintainers to approve the feature
- Open a pull request!
This project is licensed under the MIT License - see the LICENSE file for details.
Happy scripting! π