{"id":25955282,"url":"https://github.com/aiya000/bash-toys","last_synced_at":"2026-04-11T17:02:58.559Z","repository":{"id":279109905,"uuid":"937744665","full_name":"aiya000/bash-toys","owner":"aiya000","description":"A tiny collection of shell script and aliases for bash/zsh for those who don't want to memorize too many.","archived":false,"fork":false,"pushed_at":"2026-04-10T05:27:21.000Z","size":949,"stargazers_count":7,"open_issues_count":2,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-10T07:37:56.480Z","etag":null,"topics":["bash","bashrc","cline","roo","roocode","zsh","zsh-plugin","zshrc"],"latest_commit_sha":null,"homepage":"https://github.com/aiya000/bash-toys","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/aiya000.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2025-02-23T19:45:50.000Z","updated_at":"2026-04-10T05:27:24.000Z","dependencies_parsed_at":"2025-02-23T20:33:13.712Z","dependency_job_id":"960c6c85-eec4-4c1a-a3e5-5a8ee13c3b11","html_url":"https://github.com/aiya000/bash-toys","commit_stats":null,"previous_names":["aiya000/bash-toys"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/aiya000/bash-toys","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aiya000%2Fbash-toys","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aiya000%2Fbash-toys/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aiya000%2Fbash-toys/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aiya000%2Fbash-toys/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aiya000","download_url":"https://codeload.github.com/aiya000/bash-toys/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aiya000%2Fbash-toys/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31687882,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-11T13:07:20.380Z","status":"ssl_error","status_checked_at":"2026-04-11T13:06:47.903Z","response_time":54,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bash","bashrc","cline","roo","roocode","zsh","zsh-plugin","zshrc"],"created_at":"2025-03-04T16:18:23.030Z","updated_at":"2026-04-11T17:02:58.553Z","avatar_url":"https://github.com/aiya000.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n# :dog2: bash-toys :dog2:\n\n**Tiny Tools that Reach the Finer Details**\n\n![Shell: bash/zsh](https://img.shields.io/badge/Shell-bash%20%7C%20zsh-green)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)\n![Dependencies: minimal](https://img.shields.io/badge/Dependencies-minimal-blue)\n\n![](./readme/cat-which-rm-dust.png)\n\n\u003c/div\u003e\n\n## :bookmark_tabs: Table of Contents\n\n- [Quick Start](#bookmark_tabs-quick-start)\n- [Scripts](#bookmark_tabs-scripts)\n- [Documentation](#bookmark_tabs-documentation)\n- [Installation](#bookmark_tabs-installation)\n- [Help](#bookmark_tabs-show-help-for-commands)\n- [Contributing](#bookmark_tabs-contributing)\n- [License](#bookmark_tabs-license)\n\n## :bookmark_tabs: Quick Start\n\n```bash\ngit clone --depth 1 https://github.com/aiya000/bash-toys.git /path/to/bash-toys\n```\n\nIf using bash:\n```bash\necho 'source /path/to/bash-toys/source-all.sh' \u003e\u003e ~/.bashrc\n```\n\nIf using zsh:\n```bash\necho 'source /path/to/bash-toys/source-all.sh' \u003e\u003e ~/.zshrc\n```\n\n## :bookmark_tabs: Documentation\n\nFor detailed documentation with examples and options, see:\n\n- [doc/main.md](./doc/main.md) - Overview and main entry point\n    - [doc/bin.md](./doc/bin.md) - Executable commands in `./bin/`\n    - [doc/sources.md](./doc/sources.md) - Source functions in `./sources/`\n\nSee also 'Scripts' section for what is `bin` and `sources`.\n\n## :bookmark_tabs: Scripts\n\nFor a complete list of scripts, visit [./bin](https://github.com/aiya000/bash-toys/tree/main/bin) and [./sources](https://github.com/aiya000/bash-toys/tree/main/sources).\n\n### :small_blue_diamond: Executables ([./bin](https://github.com/aiya000/bash-toys/tree/main/bin))\n\nbash-toys scripts have **minimal dependencies**.\nThese dependencies are documented at the beginning of the script.\n\n| Script | Description | Quick Example | Test |\n|--------|-------------|---------------|:----:|\n| [`bak`](./bin/bak) | Toggle backup (.bak) extension for files | `$ bak file.txt # mv to file.bak.txt` | [![Test](https://github.com/aiya000/bash-toys/actions/workflows/test-bak.yml/badge.svg)](https://github.com/aiya000/bash-toys/actions/workflows/test-bak.yml) |\n| [`bookmark-open`](./bin/bookmark-open) | (WIP) Opens a selected bookmark in the default browser | `$ bookmark-open # select \u0026 open` | [![Test](https://github.com/aiya000/bash-toys/actions/workflows/test-bookmark-open.yml/badge.svg)](https://github.com/aiya000/bash-toys/actions/workflows/test-bookmark-open.yml) |\n| [`calc-japanese-remaining-working-hours`](./bin/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` | ![No Test](https://img.shields.io/badge/Test-N%2FA-lightgray) |\n| [`cat-which`](./bin/cat-which) | A shorthand for `cat $(which cmd)`. Uses [bat](https://github.com/sharkdp/bat) if available | `$ cat-which rm-dust # show source` | [![Test](https://github.com/aiya000/bash-toys/actions/workflows/test-cat-which.yml/badge.svg)](https://github.com/aiya000/bash-toys/actions/workflows/test-cat-which.yml) |\n| [`clamdscan-full`](./bin/clamdscan-full) | Performs a full virus scan using ClamAV | `$ clamdscan-full / # scan root` | [![Test](https://github.com/aiya000/bash-toys/actions/workflows/test-clamdscan-full.yml/badge.svg)](https://github.com/aiya000/bash-toys/actions/workflows/test-clamdscan-full.yml) |\n| [`confirm`](./bin/confirm) | Ask a yes/no confirmation question and exit based on the answer | `$ confirm 'Continue?' # asks y/n` | [![Test](https://github.com/aiya000/bash-toys/actions/workflows/test-confirm.yml/badge.svg)](https://github.com/aiya000/bash-toys/actions/workflows/test-confirm.yml) |\n| [`ctags-auto`](./bin/ctags-auto) | Automatically determine git project and generate ctags | `$ ctags-auto # generate tags` | ![No Test](https://img.shields.io/badge/Test-N%2FA-lightgray) |\n| [`date-diff-seconds`](./bin/date-diff-seconds) | Calculate time difference in minutes between two times | `$ date-diff-seconds 21:47 22:33 # =\u003e 46 mins` | [![Test](https://github.com/aiya000/bash-toys/actions/workflows/test-date-diff-seconds.yml/badge.svg)](https://github.com/aiya000/bash-toys/actions/workflows/test-date-diff-seconds.yml) |\n| [`date-diff-seconds-now`](./bin/date-diff-seconds-now) | Calculate time difference between given time and now | `$ date-diff-seconds-now 22:30 # mins until` | [![Test](https://github.com/aiya000/bash-toys/actions/workflows/test-date-diff-seconds-now.yml/badge.svg)](https://github.com/aiya000/bash-toys/actions/workflows/test-date-diff-seconds-now.yml) |\n| [`docker-attach-menu`](./bin/docker-attach-menu) | Attach to a Docker container selected from interactive menu | `$ docker-attach-menu # select \u0026 attach` | ![No Test](https://img.shields.io/badge/Test-N%2FA-lightgray) |\n| [`docker-clean-all`](./bin/docker-clean-all) | Remove all Docker containers and volumes at once | `$ docker-clean-all # clean all` | ![No Test](https://img.shields.io/badge/Test-N%2FA-lightgray) |\n| [`docker-kill-menu`](./bin/docker-kill-menu) | Kill a Docker container selected from interactive menu | `$ docker-kill-menu # select \u0026 kill` | ![No Test](https://img.shields.io/badge/Test-N%2FA-lightgray) |\n| [`expects`](./bin/expects) | A smaller test API like [jest](https://jestjs.io/ja/docs/expect) for bash script | `$ expects \"$x\" to_be 10 # assert x=10` | [![Test](https://github.com/aiya000/bash-toys/actions/workflows/test-expects.yml/badge.svg)](https://github.com/aiya000/bash-toys/actions/workflows/test-expects.yml) |\n| [`fast-sync`](./bin/fast-sync) | Efficiently sync files from source to target by comparing file lists | `$ fast-sync /src /dst # sync new only` | [![Test](https://github.com/aiya000/bash-toys/actions/workflows/test-fast-sync.yml/badge.svg)](https://github.com/aiya000/bash-toys/actions/workflows/test-fast-sync.yml) |\n| [`gh-issue-view-select`](./bin/gh-issue-view-select) | Show GitHub issues in interactive filter and open selected issue | `$ gh-issue-view-select # select \u0026 view` | [![Test](https://github.com/aiya000/bash-toys/actions/workflows/test-gh-issue-view-select.yml/badge.svg)](https://github.com/aiya000/bash-toys/actions/workflows/test-gh-issue-view-select.yml) |\n| [`gh-run-view-latest`](./bin/gh-run-view-latest) | Show the latest GitHub Actions run preview and log | `$ gh-run-view-latest # view latest run` | ![No Test](https://img.shields.io/badge/Test-N%2FA-lightgray) |\n| [`git-credential-gh-switch`](./bin/git-credential-gh-switch) | Git credential helper that switches GitHub account per repository | `$ # See doc/bin.md` | [![Test](https://github.com/aiya000/bash-toys/actions/workflows/test-git-credential-gh-switch.yml/badge.svg)](https://github.com/aiya000/bash-toys/actions/workflows/test-git-credential-gh-switch.yml) |\n| [`git-root`](./bin/git-root) | Shows the git root directory of the current directory | `$ git-root # =\u003e /path/to/repo` | ![No Test](https://img.shields.io/badge/Test-N%2FA-lightgray) |\n| [`is-in-wsl`](./bin/is-in-wsl) | Check if current shell is running in WSL | `$ is-in-wsl \u0026\u0026 echo WSL # detect WSL` | ![No Test](https://img.shields.io/badge/Test-N%2FA-lightgray) |\n| [`kill-latest-started`](./bin/kill-latest-started) | Kill the latest started background process | `$ kill-latest-started # kill last bg` | ![No Test](https://img.shields.io/badge/Test-N%2FA-lightgray) |\n| [`kill-list`](./bin/kill-list) | Display and kill processes selected from interactive menu | `$ kill-list # select \u0026 kill` | ![No Test](https://img.shields.io/badge/Test-N%2FA-lightgray) |\n| [`list-dpkg-executables`](./bin/list-dpkg-executables) | List executable files provided by a dpkg package | `$ list-dpkg-executables git # list bins` | ![No Test](https://img.shields.io/badge/Test-N%2FA-lightgray) |\n| [`notify`](./bin/notify) | Send desktop notification with title and message | `$ notify \"Title\" \"Msg\" # show popup` | ![No Test](https://img.shields.io/badge/Test-N%2FA-lightgray) |\n| [`notify-at`](./bin/notify-at) | Send notification at specified time with flexible date formats | `$ notify-at 12:00 \"T\" \"M\" # at noon` | [![Test](https://github.com/aiya000/bash-toys/actions/workflows/test-notify-at.yml/badge.svg)](https://github.com/aiya000/bash-toys/actions/workflows/test-notify-at.yml) |\n| [`notify-at-at`](./bin/notify-at-at) | Send notification at specified time using at command (Linux/WSL) | `$ notify-at-at 12:00 \"T\" \"M\" # Linux` | ![No Test](https://img.shields.io/badge/Test-N%2FA-lightgray) |\n| [`notify-at-launchd`](./bin/notify-at-launchd) | Send notification at specified time using launchd (macOS) | `$ notify-at-launchd 12:00 \"T\" \"M\" # macOS` | ![No Test](https://img.shields.io/badge/Test-N%2FA-lightgray) |\n| [`notify-cascade`](./bin/notify-cascade) | Send cascade of notifications at specified intervals before target time | `$ notify-cascade 15:00 \"M\" \"M\" 30m 5m # 30m,5m before` | ![No Test](https://img.shields.io/badge/Test-N%2FA-lightgray) |\n| [`notify-ntfy`](./bin/notify-ntfy) | Send notification to mobile via ntfy.sh | `$ notify-ntfy \"T\" \"M\" # to mobile` | ![No Test](https://img.shields.io/badge/Test-N%2FA-lightgray) |\n| [`pathshorten`](./bin/pathshorten) | Abbreviate file path with shortened parent directories | `$ pathshorten ~/Documents/Proj # =\u003e ~/Docu/Proj` | ![No Test](https://img.shields.io/badge/Test-N%2FA-lightgray) |\n| [`peco-reverse`](./bin/peco-reverse) | Reverse order interactive filter using peco | `$ ls \\| peco-reverse # reversed filter` | ![No Test](https://img.shields.io/badge/Test-N%2FA-lightgray) |\n| [`photoframe`](./bin/photoframe) | Display photos in fullscreen slideshow mode using feh | `$ photoframe ~/Pictures # slideshow` | ![No Test](https://img.shields.io/badge/Test-N%2FA-lightgray) |\n| [`pomodoro-cycle`](./bin/pomodoro-cycle) | Run a full pomodoro cycle with multiple work sessions and breaks | `$ pomodoro-cycle 3 25 25 25 # 3x25min cycle` | [![Test](https://github.com/aiya000/bash-toys/actions/workflows/test-pomodoro-cycle.yml/badge.svg)](https://github.com/aiya000/bash-toys/actions/workflows/test-pomodoro-cycle.yml) |\n| [`pomodoro-timer`](./bin/pomodoro-timer) | A simplest Pomodoro Timer implementation in shell script | `$ pomodoro-timer 25 # start 25min` | [![Test](https://github.com/aiya000/bash-toys/actions/workflows/test-pomodoro-timer.yml/badge.svg)](https://github.com/aiya000/bash-toys/actions/workflows/test-pomodoro-timer.yml) |\n| [`prompt`](./bin/prompt) | Display a prompt and wait for the user to press Enter (always exits 0) | `$ prompt 'Press Enter: ' # pause` | [![Test](https://github.com/aiya000/bash-toys/actions/workflows/test-prompt.yml/badge.svg)](https://github.com/aiya000/bash-toys/actions/workflows/test-prompt.yml) |\n| [`rm-dust`](./bin/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` | [![Test](https://github.com/aiya000/bash-toys/actions/workflows/test-dust.yml/badge.svg)](https://github.com/aiya000/bash-toys/actions/workflows/test-dust.yml) |\n| [`run-wait-output`](./bin/run-wait-output) | Run two commands sequentially, with second triggered after first becomes silent | `$ run-wait-output 1000 \"npm watch\" \"echo Done\" # after silent` | [![Test](https://github.com/aiya000/bash-toys/actions/workflows/test-run-wait-output.yml/badge.svg)](https://github.com/aiya000/bash-toys/actions/workflows/test-run-wait-output.yml) |\n| [`update-audio-file-volume`](./bin/update-audio-file-volume) | Adjust audio volume to match a target mean/max level using ffmpeg | `$ update-audio-file-volume --input foo.mp3 --output bar.mp3 --mean-volume -48.7` | ![No Test](https://img.shields.io/badge/Test-N%2FA-lightgray) |\n| [`skip`](./bin/skip) | Skip n-lines from the beginning of output | `$ seq 10 \\| skip 3 # =\u003e 4,5,...,10` | ![No Test](https://img.shields.io/badge/Test-N%2FA-lightgray) |\n| [`slice`](./bin/slice) | Slice fields from input lines | `$ echo \"a,b,c\" \\| slice , 2 3 # =\u003e b,c` | ![No Test](https://img.shields.io/badge/Test-N%2FA-lightgray) |\n| [`start`](./bin/start) | Starts a process in the background without output | `$ start firefox # run silently` | ![No Test](https://img.shields.io/badge/Test-N%2FA-lightgray) |\n| [`take-until-empty`](./bin/take-until-empty) | Takes input lines until a blank line appears | `$ cat file \\| take-until-empty # stop at blank` | [![Test](https://github.com/aiya000/bash-toys/actions/workflows/test-take-until-empty.yml/badge.svg)](https://github.com/aiya000/bash-toys/actions/workflows/test-take-until-empty.yml) |\n| [`vim-configure`](./bin/vim-configure) | Executes `./configure` for Vim source with modern flags | `$ vim-configure # run ./configure` | ![No Test](https://img.shields.io/badge/Test-N%2FA-lightgray) |\n| [`vim-configure-debug`](./bin/vim-configure-debug) | Execute Vim source configure script with debug flags | `$ vim-configure-debug # with debug` | ![No Test](https://img.shields.io/badge/Test-N%2FA-lightgray) |\n| [`vim-configure-macos`](./bin/vim-configure-macos) | Execute Vim source configure script with modern macOS flags | `$ vim-configure-macos # for macOS` | ![No Test](https://img.shields.io/badge/Test-N%2FA-lightgray) |\n\n### :small_blue_diamond: Sources ([./sources](https://github.com/aiya000/bash-toys/tree/main/sources))\n\n'Sources' are utility scripts that affect the parent shell (like the `cd` command).\n\n| Script | Description | Quick Example | Test |\n|--------|-------------|---------------|:----:|\n| [`alias-of`](./sources/alias-of.sh) | Creates an alias only if the command exists | `$ alias-of rg 'rg --color always' # if rg exists` | ![No Test](https://img.shields.io/badge/Test-N%2FA-lightgray) |\n| [`cd-finddir`](./sources/cd-finddir.sh) | Shows directories and `cd` to a selected one via interactive filter | `$ cd-finddir # fuzzy cd` | ![No Test](https://img.shields.io/badge/Test-N%2FA-lightgray) |\n| [`cd-to-git-root`](./sources/cd-to-git-root.sh) | Change directory to the git root, with WSL path recovery support | `$ cd-to-git-root # cd to repo root` | ![No Test](https://img.shields.io/badge/Test-N%2FA-lightgray) |\n| [`cd-to-node-root`](./sources/cd-to-node-root.sh) | Change directory to the nearest parent directory containing `package.json` | `$ cd-to-node-root # cd to pkg dir` | ![No Test](https://img.shields.io/badge/Test-N%2FA-lightgray) |\n| [`contains-value`](./sources/contains-value.sh) | Checks if an array contains a value | `$ contains-value \"${arr[@]}\" \"val\" # check in arr` | ![No Test](https://img.shields.io/badge/Test-N%2FA-lightgray) |\n| [`define-alt`](./sources/define-alt.sh) | Defines a shell variable named 'foo' if not defined; use `--export` to also export. Note: arrays cannot be passed to child processes — see [`doc/sources.md`](./doc/sources.md#define-alt) | `$ define-alt EDITOR vim # set if unset` | ![No Test](https://img.shields.io/badge/Test-N%2FA-lightgray) |\n| [`define-alt-export`](./sources/define-alt-export.sh) | Alias for `define-alt --export`; defines and exports variable if not defined | `$ define-alt-export EDITOR vim # export if unset` | ![No Test](https://img.shields.io/badge/Test-N%2FA-lightgray) |\n| [`force-unexport`](./sources/force-unexport.sh) | Unexports an environment variable | `$ force-unexport MY_VAR # remove env` | ![No Test](https://img.shields.io/badge/Test-N%2FA-lightgray) |\n| [`get-var`](./sources/get-var.sh) | Read and output the value of a variable by name | `$ get-var HOME # =\u003e /home/user` | [![Test](https://github.com/aiya000/bash-toys/actions/workflows/test-get-var.yml/badge.svg)](https://github.com/aiya000/bash-toys/actions/workflows/test-get-var.yml) |\n| [`i-have`](./sources/i-have.sh) | Check if a specified command exists in the system | `$ i-have bat \u0026\u0026 echo yes # check cmd` | ![No Test](https://img.shields.io/badge/Test-N%2FA-lightgray) |\n| [`is-array`](./sources/is-array.sh) | Detect if a variable is an array (supports Bash and Zsh) | `$ is-array arr \u0026\u0026 echo yes # check array` | ![No Test](https://img.shields.io/badge/Test-N%2FA-lightgray) |\n| [`load-my-env`](./sources/load-my-env.sh) | Load environment-specific settings and aliases for various tools and runtimes | `$ load-my-env # load settings` | ![No Test](https://img.shields.io/badge/Test-N%2FA-lightgray) |\n| [`nvim-parent-edit`](./sources/nvim-parent-edit.sh) | Open files in parent Neovim instance via RPC from child terminal | `$ nvim-parent-edit file.txt # edit in parent` | ![No Test](https://img.shields.io/badge/Test-N%2FA-lightgray) |\n| [`source-if-exists`](./sources/source-if-exists.sh) | Conditionally source a file if it exists | `$ source-if-exists ~/.local.sh # source if exists` | ![No Test](https://img.shields.io/badge/Test-N%2FA-lightgray) |\n\n### :small_blue_diamond: Recommended Scripts\n\nHere are some scripts that can boost your daily workflow:\n\n\u003cdetails\u003e\n\u003csummary\u003eFile Operations\u003c/summary\u003e \u003c!-- {{{ --\u003e\n\n**[`rm-dust`](./bin/rm-dust)** - Safe alternative to `rm`. Never lose files by accident again!\n\n```bash\n$ rm-dust important.txt        # Moves to dustbox instead of deleting\n$ rm-dust *.log                # Clean up logs safely\n$ alias rm=rm-dust             # Recommended: replace rm globally\n$ rm-dust --restore            # Interactively restore files from dustbox\n```\n\n**[`bak`](./bin/bak)** - Quick backup toggle. One command to backup, one to restore.\n\n```bash\n$ bak config.yaml              # Creates config.bak.yaml\n$ vim config.yaml              # Edit the original\n$ bak config.yaml              # Restore from backup if needed\n```\n\n**[`nvim-parent-edit`](./sources/nvim-parent-edit.sh)** - Edit files in parent Neovim from nested terminal. (Show also an example: [nvim.lua](https://github.com/aiya000/dotfiles/blob/906c7ed230e74c3dbaf2be7797d7537616470647/.config/nvim/lua/nvim.lua#L305-L307), [keymaps.lua](https://github.com/aiya000/dotfiles/blob/906c7ed230e74c3dbaf2be7797d7537616470647/.config/nvim/lua/keymaps.lua#L212-L217))\n\n```bash\n# Inside Neovim's :terminal\n$ nvim-parent-edit file.txt   # Opens in parent Neovim, not a nested instance (not Neovim in Neovim)\n$ nvim-parent-edit *.js       # Open multiple files\n```\n\n**[`fast-sync`](./bin/fast-sync)** - Efficient file sync by comparing file lists first.\n\n```bash\n$ fast-sync --init ~/photos    # Initialize sync state\n$ fast-sync ~/photos /backup   # Only syncs new/changed files\n# Much faster than full rsync for large directories\n```\n\n\u003c!-- }}} --\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eTesting\u003c/summary\u003e \u003c!-- {{{ --\u003e\n\n**[`expects`](./bin/expects)** - Jest-like assertions for shell scripts. Write readable tests!\n\nMany tests in this project ([./test](./test)) are written with `expects`.\n\n```bash\n$ x=10 ; expects \"$x\" to_be 42\nFAIL: expected {actual} to_be '42', but {actual} is '10'\n\n$ x=42\n$ expects \"$x\" to_be 42                          # No output on success (exit 0)\n$ expects \"$x\" to_be 42 \u0026\u0026 echo \"PASS\"           # Equality check\nPASS\n$ expects \"$x\" not to_be 0 \u0026\u0026 echo \"PASS\"        # Negation\nPASS\n$ expects \"hello world\" to_contain \"world\"       # String containment\n```\n\nAnd more assertions are available.\nSee [`expects`](./bin/expects).\n\n\u003c!-- }}} --\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eNotifications \u0026 Timers\u003c/summary\u003e \u003c!-- {{{ --\u003e\n\n**[`notify`](./bin/notify)** - Simple desktop notification. Works on macOS, Linux, and WSL.\n\n```bash\n$ notify \"Build Done\" \"Your project compiled successfully\"\n$ make \u0026\u0026 notify \"Success\" \"Build complete\" || notify \"Failed\" \"Build error\"\n```\n\n**[`notify-ntfy`](./bin/notify-ntfy)** - Send notifications to your phone via [ntfy.sh](https://ntfy.sh).\n\n```bash\n$ notify-ntfy \"Backup Done\" \"Server backup completed\"  # Sends to mobile\n$ long-running-task \u0026\u0026 notify-ntfy \"Done\" \"Task finished\"\n# Requires: export BASH_TOYS_NTFY_TOPIC=\"your-topic-name\"\n# See https://ntfy.sh for setup\n# See ./bin/notify-ntfy for usage\n```\n\n**[`notify-at`](./bin/notify-at)** - Schedule notifications with human-friendly time formats. (A wrapper for `notify`, and `at` command (Linux) or `launchd` (macOS).)\n\n```bash\n$ notify-at 15:00 \"Meeting\" \"Team standup starting\"         # Show notification to desktop at 3 PM\n$ notify-at 15:00 \"Meeting\" \"Team standup starting\" --local # --local (Show notification to desktop) by default (Same as above)\n$ notify-at \"01-15 09:00\" \"Reminder\" \"Project deadline\"     # Show notification to desktop on Jan 15 at 9 AM\n$ 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)\n$ notify-at 18:00 \"Dinner\" \"Cook\" 1h --mobile --local       # Show/Send notification to both mobile and desktop\n```\n\n**[`notify-cascade`](./bin/notify-cascade)** - Get reminded at multiple intervals before an event. (A wrapper for `notify-at`.)\n\n```bash\n$ notify-cascade 15:00 \"Meeting\" \"Standup\" 30m 10m 5m    # Show notification at 14:30, 14:50, and 14:55\n$ notify-cascade 18:00 \"Dinner\" \"Cook\" 1h 30m --mobile   # Send notification to mobile (requires notify-ntfy setup)\n$ notify-cascade 18:00 \"Dinner\" \"Cook\" 1h 30m --local    # Desktop only (default)\n$ notify-cascade 18:00 \"Dinner\" \"Cook\" 1h --mobile --local  # Both mobile and desktop\n```\n\n**[`pomodoro-timer`](./bin/pomodoro-timer)** - Simple Pomodoro technique timer with notifications.\n\n```bash\n$ pomodoro-timer              # Default 30 minutes\n$ pomodoro-timer 25           # Classic 25-minute pomodoro\n$ pomodoro-timer --rest 5     # 5-minute break timer\n$ pomodoro-timer --from 09:30 60   # Resume timer that started at 09:30 for 60 minutes\n```\n\n**[`pomodoro-cycle`](./bin/pomodoro-cycle)** - Run a full multi-step Pomodoro cycle with breaks between sessions.\n\n```bash\n$ pomodoro-cycle              # 3 steps × 30 min (default)\n$ pomodoro-cycle 3 25 25 25   # Classic 3 × 25 min pomodoro\n$ pomodoro-cycle 2 45 30      # step 1: 45 min, step 2: 30 min\n```\n\n\u003c!-- }}} --\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eText Processing\u003c/summary\u003e \u003c!-- {{{ --\u003e\n\n**[`skip`](./bin/skip)** \u0026 **[`slice`](./bin/slice)** - Simple but powerful text manipulation.\n\n```bash\n$ cat data.csv | skip 1                    # Skip header row\n$ echo \"a,b,c,d\" | slice , 2 3             # Extract fields 2-3: \"b,c\"\n$ ps aux | skip 1 | slice ' ' 1 2          # Get PID and user columns\n```\n\n**[`take-until-empty`](./bin/take-until-empty)** - Read until blank line. Perfect for parsing sections.\n\n```bash\n$ cat changelog.md | take-until-empty          # Get first section only\n$ git log --format=\"%B\" -1 | take-until-empty  # Get commit title only\n```\n\n**[`pathshorten`](./bin/pathshorten)** - Shorten paths like Vim's `pathshorten()`. Great for prompts!\n\n```bash\n$ pathshorten ~/Documents/Projects/myapp/src  # =\u003e ~/Docu/Proj/myap/src\n$ PS1=\"\\$(pathshorten \\$PWD) $ \"              # Use in bash prompt\n```\n\n\u003c!-- }}} --\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eNavigation \u0026 Development\u003c/summary\u003e \u003c!-- {{{ --\u003e\n\n**[`cd-finddir`](./sources/cd-finddir.sh)** - Fuzzy directory navigation. Never type long paths again!\n\n```bash\n$ cd-finddir                  # Shows directory picker\n...\nluarrow.lua/src/\nluarrow.lua/spec/\nluarrow.lua/scripts/\nluarrow.lua/luarrow.bak.lua/\nluarrow.lua/doc/\nluarrow.lua/\nchotto.lua/src/\nchotto.lua/spec/\nchotto.lua/scripts/\nchotto.lua/readme/\nchotto.lua/doc/\nchotto.lua/\n...\n\u003e (Type partial name to filter, select to cd)\n```\n\n**[`cd-to-git-root`](./sources/cd-to-git-root.sh)** \u0026 **[`git-root`](./bin/git-root)** - Quick access to repository root.\n\n```bash\n$ cd-to-git-root              # Jump to repo root from anywhere\n$ cat $(git-root)/README.md   # Reference files from repo root\n```\n\n**[`cd-to-node-root`](./sources/cd-to-node-root.sh)** - Jump to nearest `package.json` directory.\n\n```bash\n$ pwd\n/project/src/components/ui\n$ cd-to-node-root\n$ pwd\n/project                      # Jumped to package.json directory!\n$ npm test                    # Now you can run npm commands\n```\n\n**[`cat-which`](./bin/cat-which)** - Instantly view any script's source code.\n\n```bash\n$ cat-which rm-dust           # See how rm-dust works\n$ cat-which my-script         # Debug your own scripts\n```\n\n\u003c!-- }}} --\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eBackground Processes\u003c/summary\u003e \u003c!-- {{{ --\u003e\n\n**[`start`](./bin/start)** - Launch GUI apps without terminal noise.\n\n```bash\n$ start firefox               # Opens Firefox, returns prompt immediately\n$ start code .                # Open VS Code without blocking\n$ start vlc music.mp3         # Play music in background\n```\n\n**[`kill-list`](./bin/kill-list)** - Interactive process killer. No more memorizing PIDs!\n\n```bash\n$ kill-list\n  PID START                     COMMAND\n...\n12345 Thu Jan 30 10:15:00 2025  node server.js\n12346 Thu Jan 30 10:15:01 2025  npm run watch\n12347 Thu Jan 30 10:20:30 2025  python script.py\n...\n\u003e Select process to kill (fuzzy search, multi-select with Tab)\n```\n\n\u003c!-- }}} --\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eSecurity\u003c/summary\u003e \u003c!-- {{{ --\u003e\n\n**[`clamdscan-full`](./bin/clamdscan-full)** - Full system virus scan with ClamAV.\n\n```bash\n$ clamdscan-full /            # Scan entire system\n$ clamdscan-full ~/Downloads  # Scan specific directory\n# Requires ClamAV daemon (clamd) running\n```\n\n\u003c!-- }}} --\u003e\n\u003c/details\u003e\n\n## :bookmark_tabs: Show help for commands\n\nMost commands support `--help` option:\n\n```bash\n$ rm-dust --help\nrm-dust - Alternative to rm that moves files to dustbox instead of deletion\n\nUsage:\n  rm-dust FILE...\n  rm-dust --help\n...\n```\n\nIf a command doesn't have `--help`, use `bash-toys-help` to extract help from script comments:\n\n```bash\n$ bash-toys-help rm-dust\n\n# For 'source' commands (don't forget .sh extension)\n$ bash-toys-help cd-to-git-root.sh\n\n# Disable markdown rendering\n$ bash-toys-help --disable-glow rm-dust\n```\n\n## :bookmark_tabs: Installation\n\nIn this section, we assumed you are using bash and `~/.bashrc`.\nIf you are using zsh, replace `~/.bashrc` with `~/.zshrc`.\n\n### :small_blue_diamond: Install all tools\n\n1. Clone the repository\n\n```bash\n$ git clone --depth 1 https://github.com/aiya000/bash-toys.git /path/to/bash-toys\n```\n\n2. Source the `source-all.sh` script in your `.bashrc` or `.zshrc`\n\n```bash\n$ echo 'source /path/to/bash-toys/source-all.sh' \u003e\u003e ~/.bashrc\n```\n\n3. (**Optional**) Enable bash completions\n\n```bash\n$ echo 'source /path/to/bash-toys/source-completions-all.sh' \u003e\u003e ~/.bashrc\n```\n\n4. (**Optional**) Configure options if necessary\n\n```bash\n$ vim /path/to/bash-toys/define-options.sh\n```\n\nOr set in your `.bashrc`:\n\n```bash\nexport BASH_TOYS_DUSTBOX_DIR=\"$HOME/dustbox\"\nexport BASH_TOYS_MUSIC_PLAYER='afplay /System/Library/Sounds/Funk.aiff'\nexport BASH_TOYS_MUSIC_PLAYER_OPTIONS=''\n```\n\n\u003cdetails\u003e\n\u003csummary\u003eExample configuration\u003c/summary\u003e\n\n```bash\nexport BASH_TOYS_INTERACTIVE_FILTER=fzf\nexport BASH_TOYS_DUSTBOX_DIR=\"$HOME/dustbox\"\nexport BASH_TOYS_BATCAT_OPTIONS=''\n```\n\n\u003c/details\u003e\n\n### :small_blue_diamond: Options\n\nPlease see `./define-options.sh` and configure your options as needed.\n\n### :small_blue_diamond: Optional Dependencies\n\n- `vlc`: For `pomodoro-start` (if `$BASH_TOYS_MUSIC_PLAYER` is set to the default value)\n\n### :small_blue_diamond: Install each of the tools\n\nHere is how to install individual tools.\n\n1. Create base directory\n\n```bash\n$ mkdir -p ~/lib/bash-toys || true\n$ echo 'export PATH=$PATH:~/lib/bash-toys' \u003e\u003e ~/.bashrc\n```\n\n2. (**Optional**) Install dependencies if needed\n\n```bash\n$ curl https://raw.githubusercontent.com/aiya000/bash-toys/refs/heads/main/lib/fun.sh -o ~/lib/bash-toys/fun.sh\n$ echo 'source ~/lib/bash-toys/fun.sh' \u003e\u003e ~/.bashrc\n```\n\n3. (**Optional**) Configure environment variables if necessary\n\nSome scripts require environment variables to be configured. You can either:\n\nDownload and source `define-options.sh`:\n\n```bash\n$ curl https://raw.githubusercontent.com/aiya000/bash-toys/refs/heads/main/define-options.sh -o ~/lib/bash-toys/define-options.sh\n$ echo 'source ~/lib/bash-toys/define-options.sh' \u003e\u003e ~/.bashrc\n```\n\nOr set the variables directly in your `.bashrc` (or `.zshrc` for zsh):\n\n```bash\nexport BASH_TOYS_INTERACTIVE_FILTER=fzf\nexport BASH_TOYS_DUSTBOX_DIR=\"$HOME/dustbox\"\nexport BASH_TOYS_BATCAT_OPTIONS=''\n```\n\n4. Install a tool you want\n\n```bash\n$ curl https://raw.githubusercontent.com/aiya000/bash-toys/refs/heads/main/bin/bak -o ~/bin/bak\n```\n\nFor `./sources/*`, don't forget to `source`:\n\n```bash\n$ 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\n$ echo 'source /path/to/sources/cd-to-git-root.sh' \u003e\u003e ~/.bashrc\n```\n\n## :bookmark_tabs: Contributing\n\nWe welcome contributions! Please follow these steps.\n\n1. Create an issue for the feature you want to add\n1. Wait for maintainers to approve the feature\n1. Open a pull request!\n\n## :bookmark_tabs: License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n\n---\n\nHappy scripting! :dog2:\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faiya000%2Fbash-toys","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faiya000%2Fbash-toys","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faiya000%2Fbash-toys/lists"}