mirror of
https://github.com/chenasraf/dotfiles.git
synced 2026-05-18 01:29:06 +00:00
654 lines
13 KiB
Bash
Executable File
654 lines
13 KiB
Bash
Executable File
#!/usr/bin/env zsh
|
|
|
|
source $DOTFILES/autoload_completions.sh
|
|
# source $DOTFILES/plugins/colors.plugin.zsh
|
|
# source ~/.dotfiles/plugins/tmux.plugin.zsh
|
|
|
|
motd() {
|
|
out=$(run-parts $DOTFILES/scripts/motd)
|
|
echo $out
|
|
}
|
|
|
|
# Functions
|
|
|
|
# show all man entries under a specific section
|
|
# e.g. mansect 7
|
|
mansect() { man -aWS ${1?man section not provided} \* | xargs basename | sed "s/\.[^.]*$//" | sort -u; }
|
|
|
|
# mkdir -p then navigate to said directory
|
|
mkcd() {
|
|
mkdir -p -- "$1" && cd -P -- "$1"
|
|
}
|
|
|
|
# find out which process is listening on a specific port
|
|
listening() {
|
|
if [[ $# -eq 0 ]]; then
|
|
lsof -iTCP -sTCP:LISTEN -n -P
|
|
elif [[ $# -eq 1 ]]; then
|
|
lsof -iTCP -sTCP:LISTEN -n -P | grep -i --color $1
|
|
else
|
|
echo "Usage: listening [pattern]"
|
|
fi
|
|
}
|
|
|
|
# kill process listening on a specific port
|
|
kill-listening() {
|
|
if [[ $# -eq 0 ]]; then
|
|
echo "Usage: kill-listening <port>"
|
|
return 1
|
|
fi
|
|
listening $1 | awk '{print $2}' | xargs kill
|
|
}
|
|
|
|
# example echo '1' | prepend 'result: '
|
|
prepend() {
|
|
echo -n "$@"
|
|
cat -
|
|
}
|
|
|
|
# transform to lowercase
|
|
lcase() {
|
|
echo "$@" | tr '[:upper:]' '[:lower:]'
|
|
}
|
|
|
|
# transform to uppercase
|
|
ucase() {
|
|
echo "$@" | tr '[:lower:]' '[:upper:]'
|
|
}
|
|
|
|
# return 0 or 1 based on result of command
|
|
int_res() {
|
|
# get all but last
|
|
c=$(($# - 1))
|
|
out="$(lcase $(bash -c "${@:1:$c}"))"
|
|
check="$(lcase ${@: -1})"
|
|
if [[ $out =~ $check ]]; then
|
|
return 0
|
|
else
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# check if system is mac
|
|
is_mac() {
|
|
int_res "uname -s" "darwin"
|
|
return $?
|
|
}
|
|
|
|
# check if system is linux
|
|
is_linux() {
|
|
int_res "uname -s" "linux"
|
|
return $?
|
|
}
|
|
|
|
# edit a dotfile script and source if there were changes
|
|
# supports autocomplete for any editable files
|
|
rc() {
|
|
if [[ $# -eq 0 ]]; then
|
|
echo_red "Usage: rc [-n] [-q] <dotfile>"
|
|
return 1
|
|
fi
|
|
no_src=0
|
|
quiet=0
|
|
|
|
while [[ $# -gt 1 ]]; do
|
|
case $1 in
|
|
-n)
|
|
no_src=1
|
|
;;
|
|
-q)
|
|
quiet=1
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
if [[ -f "$DOTFILES/$1.sh" ]]; then
|
|
file="$DOTFILES/$1.sh"
|
|
elif [[ -f "$DOTFILES/$1.zsh" ]]; then
|
|
file="$DOTFILES/$1.zsh"
|
|
else
|
|
file="$DOTFILES/$1"
|
|
fi
|
|
|
|
if [[ -f $file ]]; then
|
|
hash=$(md5 $file)
|
|
echo "Opening $file..."
|
|
nvim $file
|
|
newhash=$(md5 $file)
|
|
|
|
if [[ $? -eq 0 && $hash != $newhash ]]; then
|
|
if [[ $no_src -ne 1 ]]; then
|
|
if [[ $quiet -ne 1 ]]; then
|
|
src $1
|
|
else
|
|
src -q $1
|
|
fi
|
|
fi
|
|
else
|
|
echo "No changes made"
|
|
return 2
|
|
fi
|
|
return 0
|
|
fi
|
|
echo_red "File not found: $file"
|
|
return 1
|
|
}
|
|
|
|
# source a dotfile script
|
|
# supports autocomplete for any editable files
|
|
src() {
|
|
if [[ $# -eq 0 ]]; then
|
|
echo_red "Usage: src [-q] <dotfile>"
|
|
return 1
|
|
fi
|
|
|
|
while [[ $# -gt 1 ]]; do
|
|
case $1 in
|
|
-q)
|
|
quiet=1
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
if [[ -f "$DOTFILES/$1.sh" ]]; then
|
|
file="$DOTFILES/$1.sh"
|
|
elif [[ -f "$DOTFILES/$1.zsh" ]]; then
|
|
file="$DOTFILES/$1.zsh"
|
|
else
|
|
file="$DOTFILES/$1"
|
|
fi
|
|
|
|
if [[ -f $file ]]; then
|
|
if [[ $quiet -ne 1 ]]; then
|
|
echo "Reloading $file..."
|
|
fi
|
|
source "$file"
|
|
return 0
|
|
fi
|
|
echo_red "File not found: $file"
|
|
return 1
|
|
}
|
|
|
|
# same as rc, but for plugin files
|
|
prc() {
|
|
rc "plugins/$1.plugin"
|
|
return $?
|
|
}
|
|
|
|
# same as src, but for plugin files
|
|
sprc() {
|
|
src "plugins/$1.plugin"
|
|
return $?
|
|
}
|
|
|
|
# select random number between min and max
|
|
rand() {
|
|
if [[ $# -eq 0 ]]; then
|
|
echo_red "Usage: rand [min = 0] <max>"
|
|
return 1
|
|
fi
|
|
if [[ $# -eq 1 ]]; then
|
|
min=$((0))
|
|
max=$(($1))
|
|
else
|
|
min=$(($1))
|
|
max=$(($2))
|
|
fi
|
|
echo $(($RANDOM % ($max - $min + 1) + $min))
|
|
}
|
|
|
|
# select random element from list
|
|
randline() {
|
|
if [[ $# -eq 0 ]]; then
|
|
echo_red "Usage: randline <file>"
|
|
return 1
|
|
fi
|
|
linenum=$(($RANDOM % $(wc -l <$1) + 1))
|
|
echo $(cat $1 | head -n $linenum | tail -n 1)
|
|
}
|
|
|
|
# find $1 and replace with $2 in file $3, output to stdout
|
|
find-replace() {
|
|
if [[ $# -ne 3 || $1 == '-h' ]]; then
|
|
echo_red "Find and replace text from file and output the result. Does not modify the file."
|
|
echo_red "Usage: find-replace <find> <replace> <file>"
|
|
return 1
|
|
fi
|
|
find=$1
|
|
replace=$2
|
|
file=$3
|
|
sed "s/$find/$replace/g" $file
|
|
}
|
|
|
|
# find $1 and replace with $2 in file $3, output to file
|
|
find-replace-file() {
|
|
if [[ $# -lt 3 || $1 == '-h' ]]; then
|
|
echo_red "Find and replace text in file. Modifies the file."
|
|
echo_red "Usage: find-replace-file <find> <replace> <file> [file]..."
|
|
return 1
|
|
fi
|
|
|
|
files=( "${@:3}" )
|
|
find=$1
|
|
replace=$2
|
|
|
|
for file in $files; do
|
|
echo "Replacing $find with $replace in $file..."
|
|
out=$(find-replace $find $replace $file)
|
|
echo $out >$file
|
|
done
|
|
}
|
|
|
|
# same as run-parts from debian, but for osx
|
|
if is_mac; then
|
|
run-parts() {
|
|
verbose=0
|
|
if [[ $# -eq 0 ]]; then
|
|
echo "Usage: run-parts <dir>"
|
|
return 1
|
|
fi
|
|
if [[ $1 == "-v" ]]; then
|
|
verbose=1
|
|
shift
|
|
fi
|
|
out=""
|
|
for f in $1/*; do
|
|
if [[ -x $f ]]; then
|
|
if [[ $verbose == 1 ]]; then
|
|
echo "Running $f..."
|
|
fi
|
|
source $f
|
|
fi
|
|
done
|
|
}
|
|
fi
|
|
|
|
# search for a file in a directory
|
|
search-file() {
|
|
if [[ $# -eq 0 ]]; then
|
|
echo "Usage: find-file [dir] <file>"
|
|
return 1
|
|
fi
|
|
if [[ $# -eq 1 ]]; then
|
|
dir=$(pwd)
|
|
file=$1
|
|
else
|
|
dir=$1
|
|
file=$2
|
|
fi
|
|
find $dir -name $file 2>/dev/null
|
|
return $?
|
|
}
|
|
|
|
# find a file in the current directory or on one of its ancestors.
|
|
# usefule for finding project root based on config file (e.g. package.json, pubspec.yaml, pyproject.toml)
|
|
find-up() {
|
|
if [[ $# -eq 0 ]]; then
|
|
echo "Usage: find-up <file>"
|
|
return 1
|
|
fi
|
|
file=$1
|
|
dir=$(pwd)
|
|
while [[ $dir != "/" ]]; do
|
|
if [[ -f $dir/$file ]]; then
|
|
echo $dir/$file
|
|
return 0
|
|
fi
|
|
dir=$(dirname $dir)
|
|
if [[ $dir == "/" ]]; then
|
|
break
|
|
fi
|
|
done
|
|
return 1
|
|
}
|
|
|
|
# open project directory
|
|
prjd() {
|
|
sub="$@"
|
|
if [[ -z "$sub" ]]; then
|
|
read sub
|
|
fi
|
|
dv="$(wd path dv $sub)"
|
|
pushd "$dv"
|
|
}
|
|
|
|
# open project directory in nvim
|
|
prj() {
|
|
pushd "$(wd path dv $@)"
|
|
nvim .
|
|
popd
|
|
}
|
|
|
|
# open docker logs for specified container
|
|
docker-log() {
|
|
image="$1"
|
|
shift
|
|
docker logs --follow $@ "$image"
|
|
}
|
|
|
|
# docker exec command for specified container
|
|
docker-exec() {
|
|
image="$1"
|
|
executable="$2"
|
|
shift 2
|
|
rest=$@
|
|
docker exec -ti $rest "$image" "$executable"
|
|
}
|
|
|
|
# open docker bash shell for specified container
|
|
docker-bash() {
|
|
image="$1"
|
|
shift
|
|
docker-exec "$image" /bin/bash $@
|
|
}
|
|
|
|
# open docker sh shell for specified container
|
|
docker-sh() {
|
|
image="$1"
|
|
shift
|
|
docker-exec "$image" /bin/sh $@
|
|
}
|
|
|
|
# get path of docker volume
|
|
docker-volume-path() {
|
|
image="$1"
|
|
shift
|
|
docker volume inspect "$image" | jq -r '.[0].Mountpoint'
|
|
}
|
|
|
|
# cd to docker volume
|
|
docker-volume-cd() {
|
|
image="$1"
|
|
shift
|
|
cd $(docker-volume-path "$image")
|
|
}
|
|
|
|
# autocompletions
|
|
autoload _docker-exec
|
|
autoload _docker-volume-path
|
|
autoload _prj
|
|
autoload _src
|
|
autoload _srcp
|
|
|
|
# reload entire shell
|
|
reload-zsh() {
|
|
source $HOME/.zshrc
|
|
}
|
|
|
|
# run a command and report the time it took
|
|
bench() {
|
|
if [[ $# -eq 0 ]]; then
|
|
echo_red "Usage: bench [-v] <command>"
|
|
return 1
|
|
fi
|
|
verbose=0
|
|
while [[ $# -gt 1 ]]; do
|
|
case $1 in
|
|
-v)
|
|
verbose=1
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
command=$1
|
|
shift
|
|
echo "Benchmarking $command..."
|
|
bin="/usr/bin/time"
|
|
flags=''
|
|
if [[ $verbose -eq 1 ]]; then
|
|
flags='-h -l'
|
|
fi
|
|
|
|
# TODO implement
|
|
# xargs $bin $flags $command $@
|
|
|
|
/usr/bin/time -h -l $command $@
|
|
}
|
|
|
|
# join strings with delimiter
|
|
strjoin() {
|
|
if [[ $# -eq 0 ]]; then
|
|
echo_red "Usage: strjoin <delimiter> <string>..."
|
|
return 1
|
|
fi
|
|
delimiter=$1
|
|
shift
|
|
echo "$*" | tr ' ' $delimiter
|
|
}
|
|
|
|
# short xarg
|
|
# usage: xrg "[args]" "[template with {}]"
|
|
xrg () {
|
|
if [[ $# -ne 2 ]]; then
|
|
echo_red "Usage: xrg \"[args]\" \"[template with {}]\""
|
|
fi
|
|
printf "%s\n" "$1" | xargs -I {} bash -c "$2"
|
|
}
|
|
|
|
# ask for confirmation before running a command, Y is default
|
|
# returns 0 if confirmed or typed Y, 1 if not
|
|
ask() {
|
|
echo -n "$1 [Y/n] "
|
|
read REPLY
|
|
if [[ $REPLY =~ ^[Yy]$ ]] || [[ -z $REPLY ]]; then
|
|
return 0
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
# ask for confirmation before running a command, N is default
|
|
# returns 0 if typed Y, 1 if not
|
|
ask_no() {
|
|
echo -n "$1 [y/N] "
|
|
read REPLY
|
|
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
|
return 0
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
# get user input and output it
|
|
get_user_input() {
|
|
echo -n "$1 "
|
|
read REPLY
|
|
echo $REPLY
|
|
}
|
|
|
|
# copy file to clipboard
|
|
pbfile() {
|
|
file="$1"
|
|
more $file | pbcopy | echo "=> $file copied to clipboard."
|
|
}
|
|
|
|
# output the main pubkey file or use $1 to output a specific one
|
|
pubkey_file() {
|
|
file="$HOME/.ssh/id_casraf.pub"
|
|
if [[ $# -eq 1 ]]; then
|
|
file="$HOME/.ssh/id_$1.pub"
|
|
fi
|
|
echo $file
|
|
}
|
|
|
|
# copy pubkey to clipboard, use $1 to specify a specific key
|
|
pubkey() {
|
|
file=$(pubkey_file $1)
|
|
more $file | pbcopy | echo "=> Public key copied to clipboard."
|
|
}
|
|
|
|
# add pubkey to allowed signers
|
|
allow-signing() {
|
|
file=$(pubkey_file $1)
|
|
echo "$(git config --get user.email) namespaces=\"git\" $(cat $file)" >> ~/.ssh/allowed_signers
|
|
}
|
|
|
|
# kill tmux session by name, or running session
|
|
trm() {
|
|
sess=$1
|
|
if [[ -z $sess ]]; then
|
|
tmux kill-session
|
|
return $?
|
|
fi
|
|
tmux kill-session -t $sess
|
|
}
|
|
|
|
# enable touchID usage for sudo.
|
|
# doesn't work inside a tmux session
|
|
enable_touchid_sudo() {
|
|
# Navigate to the directory containing the PAM configuration files
|
|
pushd /etc/pam.d
|
|
|
|
if [[ -f "sudo_local" ]]; then
|
|
echo "sudo_local already exists. Touch ID for sudo is already enabled."
|
|
popd
|
|
return
|
|
fi
|
|
|
|
# Copy the template file to create a new sudo_local file
|
|
echo "Copying sudo_local.template to sudo_local. Please enter your sudo password if prompted."
|
|
sudo cp sudo_local.template sudo_local
|
|
if [[ $? -ne 0 ]]; then
|
|
echo "Failed to copy sudo_local.template. Ensure it exists and you have permissions."
|
|
popd
|
|
return
|
|
fi
|
|
|
|
# Use sed to uncomment the line containing 'pam_tid.so'
|
|
echo "Enabling Touch ID in sudo_local. You might need to enter your sudo password again."
|
|
sudo sed -i '' 's/#\(.*pam_tid.so\)/\1/' sudo_local
|
|
if [[ $? -ne 0 ]]; then
|
|
echo "Failed to enable Touch ID in sudo_local. Check your permissions and file content."
|
|
popd
|
|
return
|
|
fi
|
|
defaults write com.apple.security.authorization ignoreArd -bool TRUE
|
|
|
|
echo "Touch ID has been successfully enabled for sudo. Changes should persist through system updates."
|
|
}
|
|
|
|
# disable touchID usage for sudo and reverts back to default sudo configuration
|
|
disable_touchid_sudo() {
|
|
# Navigate to the directory containing the PAM configuration files
|
|
pushd /etc/pam.d
|
|
|
|
# Check if sudo_local exists before attempting to remove it
|
|
if [[ -f "sudo_local" ]]; then
|
|
echo "Removing sudo_local to revert to default sudo configuration. Please enter your sudo password if prompted."
|
|
sudo rm sudo_local
|
|
if [ $? -ne 0 ]; then
|
|
echo "Failed to remove sudo_local. Ensure you have permissions."
|
|
popd
|
|
return
|
|
fi
|
|
defaults write com.apple.security.authorization ignoreArd -bool FALSE
|
|
echo "sudo_local has been successfully removed. The system has reverted to the default sudo configuration."
|
|
else
|
|
echo "sudo_local does not exist. No changes needed."
|
|
fi
|
|
popd
|
|
}
|
|
|
|
# remove the home directory from a path
|
|
strip-home() {
|
|
repl="~"
|
|
if [[ "$1" == "-e" ]]; then
|
|
repl=""
|
|
shift
|
|
fi
|
|
dir="$1"
|
|
echo ${dir/$HOME/$repl}
|
|
}
|
|
|
|
# encode a uri component
|
|
uriencode() {
|
|
len="${#1}"
|
|
for ((n = 0; n < len; n++)); do
|
|
c="${1:$n:1}"
|
|
case $c in
|
|
[a-zA-Z0-9.~_-]) printf "$c" ;;
|
|
*) printf '%%%02X' "'$c"
|
|
esac
|
|
done
|
|
}
|
|
|
|
posix_compliant() {
|
|
strg="${*}"
|
|
printf '%s' "${strg%%[%+]*}"
|
|
j="${strg#"${strg%%[%+]*}"}"
|
|
strg="${j#?}"
|
|
case "${j}" in "%"* )
|
|
printf '%b' "\\0$(printf '%o' "0x${strg%"${strg#??}"}")"
|
|
strg="${strg#??}"
|
|
;; "+"* ) printf ' '
|
|
;; * ) return
|
|
esac
|
|
if [ -n "${strg}" ] ; then posix_compliant "${strg}"; fi
|
|
}
|
|
|
|
# decode a uri component
|
|
uridecode() {
|
|
posix_compliant "${*}"
|
|
}
|
|
|
|
# convert markdown to html and output to stdout
|
|
md2html() {
|
|
file=${1:-README.md}
|
|
pandoc $file
|
|
}
|
|
|
|
# convert markdown to html and open in browser
|
|
mdp() {
|
|
file=${1:-README.md}
|
|
html_prefix="
|
|
<html>
|
|
<head>
|
|
<title>$file</title>
|
|
<style>
|
|
* {
|
|
font-family:Helvetica;
|
|
}
|
|
body {
|
|
margin:40px auto 0;
|
|
max-width:800px;
|
|
font-size:16;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
"
|
|
echo "Opening HTML preview for $file..."
|
|
f=$(mktemp).html
|
|
echo $html_prefix>$f
|
|
md2html $file >>$f
|
|
echo "</body></html>" >>$f
|
|
open -u "file:///$f"
|
|
# echo "Opening file:///$f"
|
|
($SHELL -c "sleep 3; rm $f; exit 0" &)
|
|
}
|
|
|
|
# sets pnpm version on closest package.json to current version
|
|
set-pnpm-pkg-version() {
|
|
fl=$(find-up package.json)
|
|
if [[ -z $fl ]]; then
|
|
echo_red "No package.json found"
|
|
return 1
|
|
fi
|
|
|
|
jq -e '.packageManager' $fl NUL
|
|
existing=$(echo "$?")
|
|
if [[ $existing -eq 0 ]]; then
|
|
if ask "pnpm version already exists. Overwrite?"; then
|
|
jq '.packageManager = $version' --arg version "pnpm@$(pnpm -v)" $fl >$fl.tmp && mv $fl.tmp $fl
|
|
fi
|
|
else
|
|
jq '.packageManager = $version' --arg version "pnpm@$(pnpm -v)" $fl >$fl.tmp && mv $fl.tmp $fl
|
|
fi
|
|
}
|
|
|
|
# select random element from arguments
|
|
# always keep last, breaks syntax highlighting
|
|
randarg() {
|
|
echo "${${@}[$RANDOM % $# + 1]}"
|
|
}
|