## Author: druckdev ## Created: 2019-10-27 (originally 2019-08-28 as functions.zsh) ## change into dir and print accordingly function cl() { cd "$@" && ls } ## Copy file and append .bkp extension function bkp() { for file in "$@"; do cp -i "$file" "$file.bkp" done } ## Launches program and detaches it from the shell function launch() { # eval "$@" ## does not work with special characters? launch_command="$1" shift $launch_command "$@" &>/dev/null & disown } ## Compares two pdfs based on the result of pdftotext function pdfdiff() { if [[ $# -eq 2 && -r "$1" && -r "$2" ]]; then diff <(pdftotext "$1" -) <(pdftotext "$2" -) else echo "something went wrong" 2>&1 return 1 fi } ## Gets Passwd from bitwarden and copies it into the clipboard function bwpwd() { if bw "get" "password" "$@" >/dev/null; then bw "get" "password" "$@" | tr -d '\n' | setclip else bw "get" "password" "$@" fi } ## creates directory and changes into it function mkcd () { # Create directory mkdir "$@" # shift arguments if mkdir options were used while [ $# -gt 1 ]; do shift done if [ -d "$1" ]; then cd "$1" pwd fi } ## Send a message over telegram by using the -e flag function msg() { if [ $# -ge 2 ]; then telegram-cli -W -e "msg $*" | grep -E "${${*/ /.*}//_/ }" # | grep -E "$(echo "$*" | sed 's/ /.*/; s/_/ /g')" else printf "\033[1;31mPlease specify a contact and a message.\n\033[0m" >&2 fi } ## Execute tg -e command but cuts of the uninteresting parts function tg() { tg="telegram-cli" if [ "$1" = "-e" ]; then shift $tg -N -W -e "$@" | tail -n +9 | head -n -2 else $tg -N -W "$@" fi } ## Encode and decode qr-codes function qr() { if [[ $# -eq 1 && -r "$1" ]]; then zbarimg "$1" else qrencode "$@" fi } ## Edit config file function conf() { # default to vim if no editor is set local CONF_EDITOR=${EDITOR:-vim} # parse otions while getopts "e:" opt 2>/dev/null; do case $opt in e) CONF_EDITOR="$OPTARG";; *) printf "\033[1;31mUsage: $0 [-e ] [/subdirs] []\n\033[0m" >&2 return 1 ;; esac done shift $(($OPTIND - 1 )) # CONF_EDITOR=( $(resolve -s $CONF_EDITOR) ) # conf needs an argument if [ $# -eq 0 ]; then printf "\033[1;31mPlease specify a config.\n\033[0m" >&2 return 1 fi # search for program name in XDG_CONFIG_HOME and $HOME local CONF_DIR="$(_get_config_dir "$1")" if [ $? -ne 0 ]; then printf "\033[1;31mFalling back to $HOME.\n\033[0m" >&2 CONF_DIR="$HOME" fi # open file with specified name if if [ $# -gt 1 ]; then if [ -r "$CONF_DIR/$2" ]; then $CONF_EDITOR "$CONF_DIR/$2" return 0 else printf "\033[1;31mCould not find config file with that name.\n\033[0m" >&2 return 1 fi fi # possible config-file names + same in hidden local -a CONF_PATTERNS CONF_PATTERNS=( "$1.conf" "$1.config" "${1}rc" "config" "conf" "$1.yml" "$1.yaml" "config.ini" "$1" ) # check if config file exists for config in $CONF_PATTERNS; do if [ -r "$CONF_DIR/$config" ]; then $CONF_EDITOR "$CONF_DIR/$config" return 0 elif [ -r "$CONF_DIR/.$config" ]; then $CONF_EDITOR "$CONF_DIR/.$config" return 0 fi done # if no config was found in a location other than HOME, look again in HOME. # (For cases like default vim with ~/.vim/ and ~/.vimrc) if [ "$CONF_DIR" != "$HOME" ];then for config in $CONF_PATTERNS; do # Only look for hidden files if [ -r "$HOME/.$config" ]; then $CONF_EDITOR "$HOME/.$config" return 0 fi done fi printf "\033[1;31mCould not find config file.\n\033[0m" >&2 return 1 } ## Change into config dir function c() { CONF_DIR="$(_get_config_dir $*)" if [ $? -eq 0 ]; then cd "$CONF_DIR" else printf "$CONF_DIR" >&2 return 1 fi } ## Get config directory function _get_config_dir() { if [ $# -gt 1 ]; then printf "\033[1;31mPlease specify one config.\n\033[0m" >&2 return 1 elif [ $# -eq 0 ]; then echo "${XDG_CONFIG_HOME:-$HOME/.config}" elif [ -d "${XDG_CONFIG_HOME:-$HOME/.config}/$1" ]; then echo "${XDG_CONFIG_HOME:-$HOME/.config}/$1" elif [ -d "$HOME/.$1" ]; then echo "$HOME/.$1" else printf "\033[1;31mCould not find config home.\n\033[0m" >&2 return 1 fi } ## Function that resolves a command to the end function resolve() { # TODO: comment!! # In script mode only the result and its arguments are printed # The result can then be used directly by other scripts without further # manipulation typeset SCRIPT_MODE VERBOSE_MODE 1>&2 while getopts "sv" opt 2>/dev/null; do case $opt in s) SCRIPT_MODE=1;; v) VERBOSE_MODE=1;; *) echo "Unknown flag!" >&2 return 1;; esac done shift $(( $OPTIND - 1 )) if (( $SCRIPT_MODE )) && (( $VERBOSE_MODE )); then echo "Script and verbose mode do no work together." >&2 return 1 fi typeset THIS THIS_COMMAND THIS_ARGUMENTS 1>&2 # When receiving a command with arguments, do not differ between # one and multiple arguments. THIS="$*" THIS_COMMAND="${THIS%% *}" # ${THIS%%* } would result in THIS_COMMAND when no arguements are specified. # We want an empty string in this case. THIS_ARGUMENTS="${THIS#${THIS_COMMAND}}" # Resolve all aliases while [[ "$(which $THIS_COMMAND | head -n1)" =~ "^${THIS_COMMAND}: aliased to " ]]; do if (( $VERBOSE_MODE )); then echo $THIS_COMMAND$THIS_ARGUMENTS fi THIS="$(which "$THIS_COMMAND" | cut -d' ' -f4-)" THIS_COMMAND="${THIS%% *}" THIS_ARGUMENTS="${THIS#${THIS_COMMAND}}$THIS_ARGUMENTS" done command_type="$(type $THIS_COMMAND)" if [[ "$command_type" =~ "^$THIS_COMMAND is a shell function from " ]]; then if (( $SCRIPT_MODE )); then echo -n "$THIS_COMMAND$THIS_ARGUMENTS" return 0 elif (( $VERBOSE_MODE )); then echo "$THIS_COMMAND$THIS_ARGUMENTS" else echo "$* is resolved to:\n$THIS_COMMAND$THIS_ARGUMENTS" fi echo -n "${command_type}:" from_file="$(echo $command_type | cut -d' ' -f7-)" # from_file=${command_type##* } grep -En -m1 "(function[ \t]+${THIS_COMMAND}[ \t]*(\(\)|)[ \t]*{|${THIS_COMMAND}[ \t]*\(\)[ \t]*{)" "$from_file" \ | cut -d: -f1 else if (( $VERBOSE_MODE )); then echo "$THIS_COMMAND$THIS_ARGUMENTS" fi THIS_COMMAND="$(which $THIS_COMMAND)" if [ $? -ne 0 ]; then echo "${THIS_COMMAND%% *} not found." >&2 return 1 fi if (( $VERBOSE_MODE )); then echo -n "$THIS_COMMAND" NEXT_STEP="$(file -bh $THIS_COMMAND | cut -d' ' -f4-)" if [ "${NEXT_STEP:0:1}" != '/' ]; then NEXT_STEP="${THIS_COMMAND%/*}/$NEXT_STEP" fi while [[ "$(file -bh $THIS_COMMAND)" =~ "^symbolic link to" && "$NEXT_STEP" != "$THIS_COMMAND" ]]; do THIS_COMMAND=$NEXT_STEP NEXT_STEP="$(file -bh $THIS_COMMAND | cut -d' ' -f4-)" if [ "${NEXT_STEP:0:1}" != '/' ]; then NEXT_STEP="${THIS_COMMAND%/*}/$NEXT_STEP" fi echo -n "\n$THIS_COMMAND" done echo $THIS_ARGUMENTS return 0 fi THIS_COMMAND="$(realpath $THIS_COMMAND)" if (( $SCRIPT_MODE )); then echo -n "$THIS_COMMAND$THIS_ARGUMENTS" return 0 fi echo "$* is resolved to:\n$THIS_COMMAND$THIS_ARGUMENTS" fi } ## Grep a keyword at the beginning of a line (ignoring whitespace) in a man page function mangrep() { mangrep_file="$1" mangrep_pattern="$2" shift shift man -P 'less -p "^ *'"${mangrep_pattern}\"" "$@" "${mangrep_file}" unset mangrep_{file,pattern} } ## Grep in zsh history file function histgrep() { grep "$@" "${HISTFILE:-$HOME/.zsh_history}" } function format() { # TODO: respect manual changes made in meld CLANG_FORMAT_FILE="$HOME/Projects/C/.clang.format" FORMAT="{$(sed -E '/^\s*$/d' "$CLANG_FORMAT_FILE" | tr '\n' ',' | sed 's/,$//')}" if [ $# -eq 1 ]; then meld <(clang-format -style="$FORMAT" $1) $1 fi echo -n "Are you happy? [yn] " read yn if [ $yn = "y" ]; then clang-format -i -style="$FORMAT" $1 fi } function urlenc() { python3 -c "from urllib import parse; print(parse.quote('$@'), end='')" } function urldec() { python3 -c "from urllib import parse; print(parse.unquote('$@'), end='')" } glog() { # One line format for fzf list view # abbreviated commit hash (yellow), title and ref names local onelineformat='--pretty=format:%C(yellow)%h %Creset%s%C(auto)%d' # Verbose format for the preview window on the right local format=( '--pretty=format:%C(yellow)' # newline created by this eaten by %- '%-commit: %H%C(auto)' # yellow commit hash '%-D%Cblue' # auto colored ref names (if any) 'Author: %aN %aE%Cred' # blue author mail 'AuthorDate: %ad%Cblue' # red author date 'Commit: %cN %cE%Cred' # blue commiter mail 'CommitDate: %cd%Creset%C(bold)' # red commit date '' ' %s%Creset' # bold white subject ' ' # space is here so that the empty line is not eaten when no body '%-b' # body '--------------------------------------------------' '' ) # Before being able to operate on the string itself we need to remove all # ansi color escape sequences to not confuse sed. local del_ansi='s/\[[0-9]{0,2}m//g' # Ignore the graph part at the beginning, then capture the commit hash and # throw away the rest of the line. local commit_hash='s/^[ */\\|]*([a-z0-9]*).*$/\1/' local dateshort='--date=format:%F' # year local date='--date=format:%F %T %z' # year time timezone local colors='--color=always' local binds=( 'ctrl-space:toggle-preview' 'ctrl-j:preview-down' 'ctrl-k:preview-up' ) # Display a colorful ascii graph of the commits in the above format and pipe # that into fzf. # Display ansi colors, reverse the layout so that the newest commit is at # the top, and add a preview command, that: # Takes the commit line, extracts the commit hash by using both patterns # from above and saves that in a variable. When the variable is not empty # (we are not on a line that contains only graph elements), execute git-show # on the commit hash. commit="$(\ git log "$onelineformat" --graph "$dateshort" "$colors" \ | fzf --ansi --reverse --bind "${(j:,:)binds}" --preview=" out=\"\$(echo {} | sed -Ee \"$del_ansi\" -e \"$commit_hash\")\" if [ \"\$out\" ]; then git show \"${(j:%n:)format}\" \"$date\" $colors \"\$out\" fi " )" # If fzf exits successfully, put the abbreviated commit hash into the # clipboard and write it into stdout. if ! (( $? )); then commit="$(sed -Ee "$del_ansi" -e "$commit_hash" <<<"$commit")" if command -v xclip &>/dev/null; then echo -n "$commit" | xclip -selection clip fi echo "$commit" fi } safe-remove() { [ $# -gt 0 ] || return 1 [ -e "$1" ] || return 1 sync if ! udisksctl unmount -b "$1"; then lsof "$1" return 1 fi udisksctl power-off -b "/dev/$(lsblk -no pkname "$1")" }