#!/bin/sh # The MIT License (MIT) # # Copyright (c) 2026 pacman64 # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. # sourcery # # # Add `magic` live-shell shortcuts/commands by sourcing this script. # # This is a collection of arguably useful shell functions and shortcuts: # some of these extra commands can be real time/effort savers, ideally # letting you concentrate on getting things done. # # Some of these commands depend on my other scripts from the `pac-tools`, # others either rely on widely-preinstalled command-line apps, or ones # which are available on most of the major command-line `package` managers. # # To use this script, you're supposed to `source` it, so its definitions # stay for your whole shell session: for that, you can run `source sourcery` # or `. sourcery` (no quotes either way) directly or at shell startup. # # Almost all commands defined in this script work with `bash`, `zsh`, and # even `dash`, which is debian linux's default non-interactive shell. Many # of its commands even seem to work on busybox's shell. case "$1" in -h|--h|-help|--help) awk ' /^case / { exit } /^# +sourcery$/, /^$/ { gsub(/^# ?/, ""); print } ' "$0" ;; esac # dash doesn't support regex-matching syntax, forcing to use case statements case "$0" in -bash|-dash|-sh|bash|dash|sh|/bin/sh) # script is being sourced with bash, dash, or ash, which is good : ;; *) case "$ZSH_EVAL_CONTEXT" in *:file) # script is being sourced with zsh, which is good : ;; *) # script is being run normally, which is a waste of time printf "\033[7mDon't run this script directly: instead source it\033[0m\n" printf "\033[7mby running '. sourcery' (without the single quotes).\033[0m\n" printf "\n" printf "\033[7mBefore doing that, you may want to see the help,\033[0m\n" printf "\033[7mby running 'sourcery -h' (without the single quotes).\033[0m\n" # exiting during shell-startup may deny shell access, even if # the script is being run, instead of being sourced directly ;; esac ;; esac # stty -ixon # use a bash-specific simple prompt, showing the current folder in the tab's # title, instead of before each command # case "$0" in # -bash) export PS1="\[\e[0m\e]0;\w\a\]\$ " ;; # esac # Time And Memory format-string for command `/usr/bin/time -f` # export TAM='real %e user %U sys %S mem %M exit %x' # use a single-line output for keyword `time` # export TIMEFORMAT='real %2R user %2U sys %2S' # use a single-line output for keyword `time` # export TIMEFORMAT='real %2lR user %2lU sys %2lS' # prevent `less` from saving commands # export LESSHISTFILE="-" # command-history settings; prevent the shell from saving commands # export HISTTIMEFORMAT="%T " # export HISTFILE="/dev/null" # unset HISTFILE # tell `go` to mind its own business # export GOTELEMETRY=off command -v c > /dev/null || alias c='cat' command -v e > /dev/null || alias e='echo' command -v man > /dev/null && alias h='man' # H for help (manual) command -v r > /dev/null || alias r='reset' # alias b='bar' # `bar` is defined later in this script # alias h='naman' # `naman` is defined later in this script # alias w='wat' # `wat` is defined later in this script # quick single-digit shortcuts to my `side-by-side` tools alias 0='sbs 0' alias 1='bsbs 1' alias 2='bsbs 2' alias 3='bsbs 3' alias 4='bsbs 4' alias 5='bsbs 5' alias 6='bsbs 6' alias 7='bsbs 7' alias 8='bsbs 8' alias 9='bsbs 9' alias goto='cf' # `cf` (Change Folder) is defined later in this script # alias goto='gotobf' # `gotobf` is defined later in this script # Less with Header n runs `less` with line numbers, ANSI styles, without # line-wraps, always showing the first n lines as a sticky-header on top alias lh1='less --header=1 -MKNiCRS' alias lh2='less --header=2 -MKNiCRS' alias lh3='less --header=3 -MKNiCRS' alias lh4='less --header=4 -MKNiCRS' alias lh5='less --header=5 -MKNiCRS' alias lh6='less --header=6 -MKNiCRS' alias lh7='less --header=7 -MKNiCRS' alias lh8='less --header=8 -MKNiCRS' alias lh9='less --header=9 -MKNiCRS' # View with Header n runs `less` without line numbers, ANSI styles, without # line-wraps, always showing the first n lines as a sticky-header on top alias vh1='less --header=1 -MKiCRS' alias vh2='less --header=2 -MKiCRS' alias vh3='less --header=3 -MKiCRS' alias vh4='less --header=4 -MKiCRS' alias vh5='less --header=5 -MKiCRS' alias vh6='less --header=6 -MKiCRS' alias vh7='less --header=7 -MKiCRS' alias vh8='less --header=8 -MKiCRS' alias vh9='less --header=9 -MKiCRS' # Awk Begin Print each expression given, without reading any input command -v abp > /dev/null || abp() { [ $# -eq 0 ] || awk "$(printf "BEGIN { print(%s) }\n" "$@")" } # Awk Print each expression given command -v ap > /dev/null || ap() { [ $# -eq 0 ] || awk "$(printf "BEGIN { print(%s) }\n" "$@")" } # APK UPdate/grade apkup() { apk update && sudo apk upgrade "$@"; } # APT UPdate/grade aptup() { sudo apt update && sudo apt upgrade "$@"; sudo -k; } # emit each argument given as its own line of output command -v args > /dev/null || args() { [ $# -eq 0 ] || printf "%s\n" "$@"; } # emit a styled BAR, with an optional `echo`-like message command -v bar > /dev/null || bar() { printf "\033[7m%-80s\033[0m\n" "$*"; } # CAlculate Nice, using my tools `ca` and `nn` can() { ca "$@" | nn; } # Change Folder using the path given, or using my `bf` tool to pick a folder command -v bf > /dev/null && cf() { local where local code if [ $# -gt 1 ]; then printf "multiple arguments/folders not supported\n" >&2 return 1 fi if [ $# -eq 0 ]; then where="$(bf .)" code=$? else where="$1" code=0 fi if [ "${code}" -ne 0 ]; then return "${code}" fi if [ ! -d "${where}" ]; then where="$(dirname "${where}")" fi cd "${where}" return $? } # CLear Screen command -v cls > /dev/null || alias cls='reset' # Colored RipGrep command -v crg > /dev/null || alias crg='rg --color=always' # CURL Silent avoids the progress bar, but still shows errors command -v curl > /dev/null && alias curls='curl --silent --show-error' # DEDUPlicate lines, emitting each unique line only the first time command -v dedup > /dev/null || dedup() { awk ' BEGIN { for (i = 1; i < ARGC; i++) if (s[ARGV[i]]++) delete ARGV[i] } !c[$0]++ ' "$@" } # dictionary-DEFINE the word given, using the local-first remote-second `dict` command -v define > /dev/null || define() { local word local gap=0 local options='-MKiCRS' if [ $# -eq 0 ]; then printf "no words given\n" >&2 return 1 fi if [ $# -eq 1 ]; then options='--header=1 -MKiCRS' fi for word in "$@"; do [ "${gap}" -gt 0 ] && printf "\n" gap=1 printf "%s\n" "${word}" dict "${word}" done | { less ${options} 2> /dev/null || less -RIMS 2> /dev/null || cat; } } # DIFF-Y compares/shows 2 files side-by-side command -v diffy > /dev/null || alias diffy='diff -y' # DIVide 2 numbers 2 ways, also showing the complement command -v div > /dev/null || div() { awk -v x="${1:-1}" -v y="${2:-1}" ' BEGIN { gsub(/_/, "", x) gsub(/_/, "", y) failed = 0 if (x ~ /^[+-]? *$/) { printf("invalid number \"%s\"\n", x) > "/dev/stderr" failed = 1 } if (y ~ /^[+-]? *$/) { printf("invalid number \"%s\"\n", y) > "/dev/stderr" failed = 1 } if ((x == 0) || (y == 0)) { print "can'"'"'t divide by zero" > "/dev/stderr" failed = 1 } if (failed) exit 1 a = x / y b = y / x c = (a < b) ? 1 - a : 1 - b printf "%f\n", (a < b) ? a : b printf "%f\n", (a < b) ? b : a printf "%f\n", c exit } ' } # emit a line with a repeating dot-like symbol in it dots() { [ "${1:-80}" -gt 0 ] && printf "%${1:-80}s\n" "" | sed 's- -·-g'; } # show the current Date and Time on separate lines dt() { printf "%s\n%s\n" "$(date +'%a %b %d')" "$(date +%T)"; } # EDit RUN shell commands, using an interactive editor edrun() { local leak='{ printf("\x1b[7m%s\x1b[0m\n", $0) > "/dev/stderr"; print }' # dash doesn't support the process-sub syntax # . <( micro -readonly true -filetype shell | awk "${leak}" ) micro -readonly true -filetype shell | awk "${leak}" | . /dev/fd/0 } # Extended-mode INteractive GREP runs `ugrep` in live/TUI mode command -v ugrep > /dev/null && alias eingrep='ugrep -E -Q' # run the Fuzzy Finder (fzf) in multi-choice mode, with custom keybindings command -v ff > /dev/null || ff() { local keys='ctrl-a:select-all,ctrl-space:toggle,F10:abort,F12:abort' fzf --reverse -m --bind "${keys}" "$@" } # convert FeeT into meters ft() { echo "${@:-1}" | sed -E 's-_--g; s- +-\n-g' \ | awk '/./ { printf "%.2f\n", 0.3048 * $0 }' } # convert a mix of FeeT and INches into meters ftin() { local ft="${1:-0}" ft="$(echo "${ft}" | sed 's-_--g')" local in="${2:-0}" in="$(echo "${in}" | sed 's-_--g')" awk "BEGIN { print 0.3048 * ${ft} + 0.0254 * ${in}; exit }" } # convert GALlons into liters gal() { echo "${@:-1}" | sed -E 's-_--g; s- +-\n-g' \ | awk '/./ { printf "%.2f\n", 3.785411784 * $0 }' } # GO Build Stripped: a common use-case for the go compiler alias gobs='go build -ldflags "-s -w" -trimpath' # GO DEPendencieS: show all dependencies in a go project alias godeps="go list -f '{{ join .Deps \"\\n\" }}" # GO IMPortS: show all imports in a go project alias goimps="go list -f '{{ join .Imports \"\\n\" }}'" # go to the folder picked using an interactive TUI; uses my tool `bf` command -v bf > /dev/null && gotobf() { local where local code where="$(bf "${1:-.}")" code=$? [ "${code}" -ne 0 ] && return "${code}" where="$(realpath "${where}")" if [ ! -d "${where}" ]; then where="$(dirname "${where}")" fi cd "${where}" || return } # GO TO FuZzy Finder, goes to the folder picked using an interactive TUI command -v fzf > /dev/null && gotofzf() { local where local code local msg='Pick Folder to go into' local keys='ctrl-a:select-all,ctrl-space:toggle,F10:abort,F12:abort' local picker="fzf --reverse --header-first --bind ${keys}" where="$(find "${1:-.}" -type f | ${picker} --header="${msg}")" code=$? [ "${code}" -ne 0 ] && return "${code}" where="$(realpath "${where}")" if [ ! -d "${where}" ]; then where="$(dirname "${where}")" fi cd "${where}" || return } # History Fuzzy-finder runs the command picked from the shell history command -v fzf > /dev/null && hf() { local keys='ctrl-a:select-all,ctrl-space:toggle,F10:abort,F12:abort' local options='--reverse --height 80% --tmux center,80% --header-first' local picker="fzf ${options} --bind ${keys}" eval "$( history \ | ${picker} --header='Pick previous shell command to re-run' \ | awk -v htf="${HISTTIMEFORMAT}" ' { $1 = "" if (htf ~ /[^ ]/) $2 = "" gsub(/^ */, "") printf("\x1b[7m%s\x1b[0m\n", $0) > "/dev/stderr" print } ' )" } # convert seconds into a colon-separated Hours-Minutes-Seconds triple hms() { echo "${@:-0}" \ | sed -E 's-_--g; s- +-\n-g' \ | awk ' /./ { x = $0 h = (x - x % 3600) / 3600 m = (x % 3600) / 60 s = x % 60 printf "%02d:%02d:%05.2f\n", h, m, s } ' } # find all hyperlinks inside HREF attributes in the input text href() { local arg for arg in "${@:--}"; do grep -i -E -o 'href="[^"]+"' "${arg}" done | awk '{ gsub(/^href="|"\r?$/, ""); print }' } # ignore the command given; useful to turn pipe-steps into passthrus ignore() { cat -; } # INSTall APT packages instapt() { sudo apt install "$@"; sudo -k; } # show a `dad` JOKE from the web, sometimes even a very funny one command -v joke > /dev/null || joke() { curl --silent --show-error https://icanhazdadjoke.com \ | fold -s \ | awk '{ gsub(/ *\r?$/, ""); print }' } # Less, showing line-numbers by default command -v l > /dev/null || l() { # busybox's `less` doesn't support options `-C` and `-K` if [ -L /usr/bin/less ]; then less -NRIMS "$@" else less -MKNiCRS "$@" 2> /dev/null fi } # Less Header-mode, showing line-numbers and (always) showing the first line command -v lh > /dev/null || lh() { # busybox's `less` doesn't support options `-C` and `-K` if [ -L /usr/bin/less ]; then less -NRIMS "$@" else less -MKNiCRS --header=1 "$@" 2> /dev/null fi } # convert pounds (LB) into kilograms lb() { echo "${@:-1}" | sed -E 's-_--g; s- +-\n-g' \ | awk '/./ { printf "%.2f\n", 0.45359237 * $0 }' } # Live Extended-mode Grep runs `ugrep` in live/TUI mode command -v ugrep > /dev/null && alias leg='ugrep -E -Q' # MARK all web/hyperLINKS (both https:// and http://) across text lines # as usable/clickable items on modern terminals marklinks() { local re='https?://[A-Za-z0-9+_.:%-]+(/[A-Za-z0-9+_.%/,#?&=-]*)*' awk 1 "$@" | sed -E 's-('"${re}"')-\x1b]8;;\1\x1b\\\1\x1b]8;;\x1b\\-g' } # merge stderr into stdout, which is useful for piped commands command -v merrge > /dev/null || merrge() { [ $# -eq 0 ] || "$@" 2>&1; } # convert MIles into kilometers mi() { echo "${@:-1}" | sed -E 's-_--g; s- +-\n-g' \ | awk '/./ { printf "%.2f\n", 1.609344 * $0 }' } # Make In Folder, also showing time and max memory used command -v mif > /dev/null || mif() { local f='real %e user %U sys %S mem %M exit %x' local folder="${1:-.}" [ $# -gt 0 ] && shift # busybox `env` doesn't support option `-C` ( cd "${folder}" && /usr/bin/time -f "$f" make "$@"; ) } # NArrow MANual, keeps `man` narrow, even if the window/tab is wide when run command -v naman > /dev/null || naman() { local w w="$(tput -T xterm cols 2> /dev/null || echo 168)" w="$((w / 2 - 4))" [ "${w}" -lt 80 ] && w=80 MANWIDTH="${w}" man "$@" } # NIL output: run command, discarding its output, or just read/discard stdin command -v nil > /dev/null || nil() { if [ $# -gt 0 ]; then "$@" > /dev/null elif [ -p /dev/stdin ]; then cat - > /dev/null fi } # Nice Processes shows/lists all current processes shown by `ps` np() { local res local code res="$(ps "${@:-aux}")" code=$? [ "${code}" -ne 0 ] && return "${code}" echo "${res}" | awk ' BEGIN { d = strftime("%a %b %d") t = strftime("%H:%M:%S") fmt = "\x1b[38;2;128;128;128m\x1b[7m%30s%s %s%30s\x1b[0m\n\n" zrepl = "\x1b[38;2;135;135;175m&\x1b[0m" printf fmt, "", d, t, "" } (NR - 1) % 5 == 1 { print "" } /^root / { gsub(/^/, "\x1b[38;2;52;101;164m") gsub(/ +/, "&\x1b[0m\x1b[38;2;52;101;164m") gsub(/$/, "\x1b[0m") } { gsub(/ \? /, zrepl) gsub(/0\.0/, zrepl) gsub(/0:00/, zrepl) printf "%3d %s\n", NR - 1, $0 } ' } # Nice Size(s), using my tool `nn` command -v ns > /dev/null || ns() { wc -c "$@" | nn; } # (Nice) What Are These (?) shows what the names given to it are/do, coloring # the syntax of shell functions using `bat` command -v bat > /dev/null && nwat() { local arg local gap=0 if [ $# -eq 0 ]; then echo "$0" return 0 fi for arg in "$@"; do [ "${gap}" -gt 0 ] && printf "\n" gap=1 printf "\033[7m%-80s\033[0m\n" "${arg}" while alias "${arg}" > /dev/null 2> /dev/null; do arg="$(alias "${arg}" | sed -E "s-^[^=]+=['\"](.+)['\"]\$-\\1-")" done if echo "${arg}" | grep -q ' '; then printf "%s\n" "${arg}" continue fi # busybox `sh` doesn't support `declare` if type -f "${arg}" 2> /dev/null; then continue fi if type "${arg}" > /dev/null 2> /dev/null; then type "${arg}" | awk ' { l[NR] = $0 } END { for (i = (NR == 1) ? 1 : 2; i <= NR; i++) print l[i] } ' \ | bat -l sh \ --style=plain --theme='Monokai Extended Light' \ --wrap=never --color=always continue fi if which "${arg}" > /dev/null 2> /dev/null; then which "${arg}" continue fi done } # PasteBoard COPY, (almsot) like on macos command -v pbcopy > /dev/null || alias pbcopy='wl-copy' # PasteBoard PASTE, (almost) like on macos command -v pbpaste > /dev/null || alias pbpaste='wl-paste' # Podman Container List command -v pcl > /dev/null || alias pcl='podman container list' # Podman Image List command -v pil > /dev/null || alias pil='podman image list' # Podman OFF command -v poff > /dev/null || alias poff='podman container kill' # POdman OFf command -v poof > /dev/null || alias poof='podman container kill' # Python Print each expression given command -v pp > /dev/null || pp() { [ $# -eq 0 ] || python -c "$(printf "print(%s)\n" "$@")" } # QUIET runs the command given, discarding its standard error command -v quiet > /dev/null || quiet() { [ $# -eq 0 ] || "$@" 2> /dev/null; } # keep only lines between the 2 line numbers given, inclusively rangelines() { { [ $# -eq 2 ] || [ $# -eq 3 ]; } && [ "${1}" -le "${2}" ] && { tail -n +"${1}" "${3:--}" | head -n $(("${2}" - "${1}" + 1)) } } # RANdom MANual page ranman() { find "/usr/share/man/man${1:-1}" -type f \ | shuf -n 1 \ | xargs basename \ | sed 's-\.gz$--' \ | xargs man } # RANdomly PICK n lines, or 1 line by default ranpick() { local n="${1:-1}" [ $# -gt 0 ] && shift shuf -n "${n}" "$@" } # Read-Only Micro (text editor) command -v rom > /dev/null || alias rom='micro -readonly true' # Read-Only Nano (text editor) command -v ron > /dev/null || alias ron='nano --view' # Read-Only Top command -v htop > /dev/null && alias rot='htop --readonly' # Run In Folder command -v rif > /dev/null || rif() { local folder="${1:-.}" [ $# -gt 0 ] && shift # busybox `env` doesn't support option `-C` [ $# -eq 0 ] || ( cd "${folder}" && "$@"; ) } # emit a line with a RULER-like pattern, using an optional count given command -v ruler > /dev/null || ruler() { [ "${1:-80}" -gt 0 ] && { printf "%${1:-80}s\n" "" \ | sed -E 's- {10}-····╵····│-g; s- {5}-····╵-1; s- -·-g' } } # SystemCTL command -v sctl > /dev/null || alias sctl='systemctl' # Silent CURL spares you the progress bar, but still tells you about errors command -v curl > /dev/null && alias scurl='curl --silent --show-error' # emit a line with the number of spaces given in it spaces() { [ "${1:-80}" -gt 0 ] && printf "%${1:-80}s\n" ""; } # turn SUDo privileges OFF right away: arguments also cause `sudo` to run with # what's given, before relinquishing existing privileges command -v sudoff > /dev/null || sudoff() { local code=0 if [ $# -gt 0 ]; then sudo "$@" code=$? fi sudo -k return "${code}" } # Time And Memory (and exit) runs the command given, and shows info on stderr command -v tam > /dev/null || tam() { /usr/bin/time -f 'real %e user %U sys %S mem %M exit %x' "$@" } # go UP n folders, or go up 1 folder by default up() { if [ "${1:-1}" -le 0 ]; then cd . else cd "$(printf "%${1:-1}s" "" | sed 's- -../-g')" || return $? fi } # UNIQUE deduplicate lines, emitting each unique line only the first time command -v unique > /dev/null || unique() { awk ' BEGIN { for (i = 1; i < ARGC; i++) if (s[ARGV[i]]++) delete ARGV[i] } !c[$0]++ ' "$@" } # View text, using `less` command -v v > /dev/null || v() { # busybox's `less` doesn't support options `-C` and `-K` if [ -L /usr/bin/less ]; then less -RIMS "$@" else less -MKiCRS "$@" 2> /dev/null fi } # View in Header-mode using `less`, always showing the first line command -v vh > /dev/null || vh() { # busybox's `less` doesn't support options `-C` and `-K` if [ -L /usr/bin/less ]; then less -RIMS "$@" else less -MKiCRS --header=1 "$@" 2> /dev/null fi } # check shell scripts for common gotchas, avoiding complaints about using # the `local` keyword, which is widely supported in practice command -v vetshell > /dev/null || alias vetshell='shellcheck -e 3043' # What Are These (?) shows what the names given to it are/do wat() { local arg local gap=0 if [ $# -eq 0 ]; then echo "$0" return 0 fi for arg in "$@"; do [ "${gap}" -gt 0 ] && printf "\n" gap=1 printf "\033[7m%-80s\033[0m\n" "${arg}" while alias "${arg}" > /dev/null 2> /dev/null; do arg="$(alias "${arg}" | sed -E "s-^[^=]+=['\"](.+)['\"]\$-\\1-")" done if echo "${arg}" | grep -q ' '; then printf "%s\n" "${arg}" continue fi # busybox `sh` doesn't support `declare` if type -f "${arg}" 2> /dev/null; then continue fi if type "${arg}" > /dev/null 2> /dev/null; then type "${arg}" | awk ' { l[NR] = $0 } END { for (i = (NR == 1) ? 1 : 2; i <= NR; i++) print l[i] } ' continue fi if which "${arg}" > /dev/null 2> /dev/null; then which "${arg}" continue fi done } # find all WEB/hyperLINKS (both https:// and http://) across text lines weblinks() { local arg local re='https?://[A-Za-z0-9+_.:%-]+(/[A-Za-z0-9+_.%/,#?&=-]*)*' for arg in "${@:--}"; do grep -i -E -o "${re}" "${arg}" done } # WGET to standard Output, instead of saving to a file alias wgeto='wget -O -' # Xargs 0 runs `xargs` in null-delimited-arguments mode command -v x0 > /dev/null || alias x0='xargs -0' # Xargs Lines uses whole lines from the standard input as the extra arguments command -v xl > /dev/null || xl() { awk -v ORS='\000' '{ gsub(/\r$/, ""); print }' \ | xargs -0 "$@" } ############################################################################## # Sound-related Extras # ############################################################################## # if my `playwave` tool isn't available, some sound-playing commands defined # later need a substitute command -v playwave > /dev/null || playwave() { mpv --really-quiet "${@:--}" } if command -v sboard > /dev/null; then # play a repeating and annoying high-pitched beep sound a few times a second, # lasting the number of seconds given, or for 1 second by default; uses my # tools `sboard` and `playwave` beeps() { sboard -o beeps "${1:-1}" "${2:-1}" | playwave; } # play a repeating synthetic-bell-like sound lasting the number of seconds # given, or for 1 second by default; uses my tools `sboard` and `playwave` bell() { sboard -o bell "${1:-1}" "${2:-1}" | playwave; } # play a busy-phone-line sound lasting the number of seconds given, or for 1 # second by default; uses my tools `sboard` and `playwave` busy() { sboard -o busy "${1:-1}" "${2:-1}" | playwave; } # play a heartbeat-like sound lasting the number of seconds given, or for 1 # second by default; uses my tools `sboard` and `playwave` heartbeat() { sboard -o heartbeat "${1:-1}" "${2:-1}" | playwave; } # play a stereotypical once-a-second laser sound for the number of seconds # given, or for 1 second (once) by default; uses my tools `sboard` and # `playwave` laser() { sboard -o laser "${1:-1}" "${2:-1}" | playwave; } # play a white-noise sound lasting the number of seconds given, or for 1 # second by default; uses my tools `sboard` and `playwave` noise() { sboard -o noise "${1:-1}" "${2:-1}" | playwave; } # play a ready-phone-line sound lasting the number of seconds given, or for 1 # second by default; uses my tools `sboard` and `playwave` ready() { sboard -o ready "${1:-1}" "${2:-1}" | playwave; } # play a tone/sine-wave sound lasting the number of seconds given, or for 1 # second by default: after the optional duration, the next optional arguments # are the volume and the tone-frequency; uses my tools `sboard`, `playwave`, # and `waveout` tone() { if [ "${3:-440}" -eq 440 ]; then sboard -o tone "${1:-1}" "${2:-1}" else waveout "${1:-1}" "${2:-1} \* sin(${3:-440} \* tau \* t)" fi \ | playwave } fi # play a ringtone-style sound lasting the number of seconds given, or for 1 # second by default; uses my tools `waveout`, and `playwave` command -v ringtone > /dev/null || ringtone() { local f='sin(2048 * tau * t) * exp(-50 * (t % 0.1))' waveout "${1:-1}" "${2:-1} * $f" | playwave }