#!/bin/bash # ─< Helper functions >───────────────────────────────────────────────────────────────── declare -a echo_messages function echo_error() { local message="\033[0;1;31m❌ ERROR:\033[0;31m\t${*}\033[0m" echo -e "$message" echo_messages+=("$message") } function echo_warning() { local message="\033[0;1;33m⚠️ WARNING:\033[0;33m\t${*}\033[0m" echo -e "$message" echo_messages+=("$message") } function echo_info() { local message="\033[0;1;34mℹ️ INFO:\033[0;34m\t${*}\033[0m" echo -e "$message" echo_messages+=("$message") } # Function to print all stored messages function print_echo_messages() { echo -e "\033[38;5;196mL\033[38;5;202mo\033[38;5;208mg\033[38;5;214m \033[38;5;220mo\033[38;5;226mu\033[38;5;118mt\033[38;5;46mp\033[38;5;48mu\033[38;5;51mt\033[38;5;45m:" for msg in "${echo_messages[@]}"; do echo -e "$msg" done } # ─< check if command exists >──────────────────────────────────────────────────────────── command_exists() { command -v "$1" >/dev/null 2>&1 } # ─< Silent execution >───────────────────────────────────────────────────────────────── silentexec() { "$@" >/dev/null 2>&1 } # ─< Check if the given command exists silently >───────────────────────────────────────── command_exists() { command -v "$@" >/dev/null 2>&1 } # ─< Check if the user is root and set sudo variable if necessary >─────────────────────── check_root() { if [ "$(id -u)" -ne 0 ]; then if command_exists sudo; then echo_warning "User is not root. Using sudo for privileged operations." _sudo="sudo" else echo_error "No sudo found and you're not root! Can't install packages." return 1 fi else echo_info "Root access confirmed." _sudo="" fi } _sensible.bash_() { # Sensible Bash - An attempt at saner Bash defaults # Maintainer: mrzool # Repository: https://github.com/mrzool/bash-sensible # Version: 0.2.2 # Unique Bash version check if ((BASH_VERSINFO[0] < 4)); then echo "sensible.bash: Looks like you're running an older version of Bash." echo "sensible.bash: You need at least bash-4.0 or some options will not work correctly." echo "sensible.bash: Keep your software up-to-date!" fi ## GENERAL OPTIONS ## # Prevent file overwrite on stdout redirection # Use `>|` to force redirection to an existing file set -o noclobber # Update window size after every command shopt -s checkwinsize # Automatically trim long paths in the prompt (requires Bash 4.x) PROMPT_DIRTRIM=2 # Enable history expansion with space # E.g. typing !! will replace the !! with your last command bind Space:magic-space # Turn on recursive globbing (enables ** to recurse all directories) shopt -s globstar 2>/dev/null # Case-insensitive globbing (used in pathname expansion) shopt -s nocaseglob ## SMARTER TAB-COMPLETION (Readline bindings) ## # Perform file completion in a case insensitive fashion bind "set completion-ignore-case on" # Treat hyphens and underscores as equivalent bind "set completion-map-case on" # Display matches for ambiguous patterns at first tab press bind "set show-all-if-ambiguous on" # Immediately add a trailing slash when autocompleting symlinks to directories bind "set mark-symlinked-directories on" ## SANE HISTORY DEFAULTS ## # Append to the history file, don't overwrite it shopt -s histappend # Save multi-line commands as one command shopt -s cmdhist # Record each line as it gets issued PROMPT_COMMAND='history -a' # Huge history. Doesn't appear to slow things down, so why not? HISTSIZE=500000 HISTFILESIZE=100000 # Avoid duplicate entries HISTCONTROL="erasedups:ignoreboth" # Don't record some commands export HISTIGNORE="&:[ ]*:exit:ls:bg:fg:history:clear" # Use standard ISO 8601 timestamp # %F equivalent to %Y-%m-%d # %T equivalent to %H:%M:%S (24-hours format) HISTTIMEFORMAT='%F %T ' # Enable incremental history search with up/down arrows (also Readline goodness) # Learn more about this here: http://codeinthehole.com/writing/the-most-important-command-line-tip-incremental-history-searching-with-inputrc/ bind '"\e[A": history-search-backward' bind '"\e[B": history-search-forward' bind '"\e[C": forward-char' bind '"\e[D": backward-char' ## BETTER DIRECTORY NAVIGATION ## # Prepend cd to directory names automatically shopt -s autocd 2>/dev/null # Correct spelling errors during tab-completion shopt -s dirspell 2>/dev/null # Correct spelling errors in arguments supplied to cd shopt -s cdspell 2>/dev/null # This defines where cd looks for targets # Add the directories you want to have fast access to, separated by colon # Ex: CDPATH=".:~:~/projects" will look for targets in the current working directory, in home and in the ~/projects folder CDPATH=".:~" # This allows you to bookmark your favorite places across the file system # Define a variable containing a path and you will be able to cd into it regardless of the directory you're in shopt -s cdable_vars # Examples: # export dotfiles="$HOME/dotfiles" # export projects="$HOME/projects" # export documents="$HOME/Documents" # export dropbox="$HOME/Dropbox" } _init() { # make less more friendly for non-text input files, see lesspipe(1) [ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)" # set variable identifying the chroot you work in (used in the prompt below) if [ -z "${debian_chroot:-}" ] && [ -r /etc/debian_chroot ]; then debian_chroot=$(cat /etc/debian_chroot) fi # If this is an xterm set the title to user@host:dir case "$TERM" in xterm* | rxvt*) PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h: \w\a\]$PS1" ;; *) ;; esac # ─< ssh completions >──────────────────────────────────────────────────────────────────── if [ -f "$HOME/.bash/ssh" ]; then . "$HOME/.bash/ssh" fi # ─< zoxide >───────────────────────────────────────────────────────────────────────────── if command_exists zoxide; then eval "$(zoxide init bash)" fi # ─< fzf >──────────────────────────────────────────────────────────────────────────────── if command_exists fzf; then eval "$(fzf --bash)" fi # ─< oh-my-posh initialization >──────────────────────────────────────────────────────────── if command_exists oh-my-posh; then eval "$(oh-my-posh init bash --config '~/.bash/themes/zen.toml')" else if command_exists curl; then curl -s https://ohmyposh.dev/install.sh | $_sudo bash -s -- -d /usr/bin/ fi fi } _alias() { # ─< set keybinding mode >──────────────────────────────────────────────────────────────── # set -o emacs # set -o vim # If set, the pattern "**" used in a pathname expansion context will # match all files and zero or more directories and subdirectories. #shopt -s globstar alias alert='notify-send --urgency=low -i "$([ $? = 0 ] && echo terminal || echo error)" "$(history|tail -n1|sed -e '\''s/^\s*[0-9]\+\s*//;s/[;&|]\s*alert$//'\'')"' # ─< colored everything >─────────────────────────────────────────────────────────────────── alias ip="ip --color=always" # ─< easier dir up >──────────────────────────────────────────────────────────────────────── alias ..="cd .." # ─< weather >────────────────────────────────────────────────────────────────────────────── alias www="curl wttr.in/Ulm" # ─< check for rg >───────────────────────────────────────────────────────────────────────── if command_exists rg; then alias grep="rg --color=always" else alias grep="grep --color=always" fi # ─< linutil >──────────────────────────────────────────────────────────────────────────── alias linutil="curl -fsSL https://christitus.com/linux | sh" # ─< telnet (starwars) >──────────────────────────────────────────────────────────────────── if command_exists telnet; then alias starwars="telnet -a telehack.com" fi # ─< neovim >───────────────────────────────────────────────────────────────────────────── if command_exists nvim; then alias cnvim="command nvim" if [ -d "$HOME/.config/nvchad" ]; then alias nvchad='NVIM_APPNAME="nvchad" command nvim' fi fi # ─< cli explorer >─────────────────────────────────────────────────────────────────────── if command_exists yazi; then echo_info "yazi is the explorer of choise" alias lf="yazi || ya pack -i" function y() { local tmp="$(mktemp -t "yazi-cwd.XXXXXX")" cwd yazi "$@" --cwd-file="$tmp" if cwd="$(command cat -- "$tmp")" && [ -n "$cwd" ] && [ "$cwd" != "$PWD" ]; then builtin cd -- "$cwd" fi rm -f -- "$tmp" } elif command_exists ranger; then echo_info "ranger is the explorer of choise" alias lf="ranger" elif command_exists lf; then echo_info "lf is the explorer of choise" fi # ─< rsync >──────────────────────────────────────────────────────────────────────────────── if command_exists rsync; then alias scp="rsync -avP" alias cp="scp" fi # ─< bat alias >──────────────────────────────────────────────────────────────────────────── if command_exists batcat; then alias cat="batcat --color=always -p --paging=never" alias less="batcat --paging always --color=always" alias gd="batcat --diff" elif command_exists bat; then alias cat="bat --color=always -p" alias less="bat --paging always --color=always" alias gd="bat --diff" fi if command_exists trash; then alias rm="trash" alias t="trash" elif command_exists trash-cli; then alias rm="trash-cli" alias t="trash-cli" else echo_error "-- You do not have trash or trash-cli installed! Your 'rm' will be permanent! --" fi # ─< g stands for GIT >───────────────────────────────────────────────────────────────────── if command_exists git; then alias g="git" alias gs="git status -sb" alias gc="git clone --recurse-submodule --depth=1" gcl() { if [ -z "$2" ]; then git clone --depth=1 "https://github.com/$1" else git clone --depth=1 "https://github.com/$1" "$2" fi } gck() { if [ -z "$2" ]; then git clone --recurse-submodules --depth=1 "https://git.k4li.de/$1" else git clone --recurse-submodules --depth=1 "https://git.k4li.de/$1" "$2" fi } alias ga="git add" alias gp="git pull --recurse-submodule" alias gms='git maintenance start' alias gsu="git submodule foreach git pull && git add . && git commit -m ' updated 📌submodules' && echo '-- Committed changes, pushing now..' && sleep 1 && git push" alias gcm="git commit -m" alias gpu="git push --recurse-submodule=on-demand" if command_exists lazygit; then alias lg="lazygit" fi fi # ─< d stands for docker >────────────────────────────────────────────────────────────────── if command_exists docker; then alias up="docker compose up" alias down="docker compose down" alias pull="docker compose pull" alias d="docker" alias dr="docker run --rm -it" alias drs="docker compose down && docker compose up -d --remove-orphans --force-recreate" alias ds="docker ps -a" alias dc="docker compose" alias appupdate="docker compose pull && docker compose up -d --force-recreate" fi # ─< colorized ls >───────────────────────────────────────────────────────────────────────── if command_exists exa; then alias ls="exa --icons -l --git" alias l="exa --long --no-filesize --no-permissions --no-time --git --colour-scale --icons" alias ll="exa --all --long --no-filesize --no-permissions --no-time --git --colour-scale --icons" alias tree="exa --icons -l --tree" elif command_exists lsd; then alias ls="lsd -l -1 -h1 --almost-all --git" alias l="lsd -1" alias ll="lsd -1 --almost-all" alias clearl="command clear && l" alias tree="lsd --tree" elif command_exists eza; then alias ls="eza --icons --long --git" alias l="eza --icons -l" alias ll="eza --icons -laa" alias tree="eza --icons -l --tree" else alias ls="ls --color=always -lAph" alias l="ls --color=always -lph -w1" alias ll="ls --color=always -lph" fi # ─< t stands for tmux >──────────────────────────────────────────────────────────────────── if command_exists tmux; then local tmux_y="$(echo '-- tmux-session active! | connecting to active session --')" local tmux_n="$(echo '-- no tmux-session found! | creating one --')" ta() { # command tmux list-sessions >/dev/null 2>&1 if command tmux list-sessions >/dev/null 2>&1; then if command_exists notify-send; then notify-send "TMUX" "${tmux_y}" sleep 0.5 tmux attach else echo_info "${tmux_y}" sleep 0.5 tmux attach fi else if command_exists notify-send; then notify-send "TMUX" "${tmux_n}" sleep 0.5 tmux else echo_info "$tmux_n" sleep 0.5 tmux fi fi } alias ts="tmux source ~/.tmux.conf" fi } # ─< ble.sh -- https://github.com/akinomyoga/ble.sh >───────────────────────────────────── d_blesh() { for deeps in git make; do if ! command_exists $deeps; then echo_error "$deeps was missing from the system, cannot setup shell properly! (blesh setup)" exit 1 fi done } i_blesh() { local tmp_dir="$(mktemp -d)" cd "$tmp_dir" || echo_error "$tmp_dir is not valid!" git clone --recursive --depth 1 --shallow-submodules https://github.com/akinomyoga/ble.sh.git make -C ble.sh install PREFIX=~/.local cd "$HOME" || return 0 } _blesh() { if [[ ! -f $HOME/.local/share/blesh/ble.sh ]]; then d_blesh # Dependency check for blesh echo_info "Installing blesh.. Depending on your internet connection and the performance of the client, this could take a minute or two.." if silentexec i_blesh; then # installing blesh silently bash fi else # . "$HOME/.local/share/blesh/ble.sh" --attach=none . "$HOME/.local/share/blesh/ble.sh" fi _games() { if command_exists curl; then echo_info "Games available. Try 'alias | grep g'" alias g2048='bash --norc -c "$(curl -sSL https://git.k4li.de/pika/scripts/raw/branch/main/bash/games/2048.sh)"' alias gwordle='bash --norc -c "$(curl -sSL https://git.k4li.de/pika/scripts/raw/branch/main/bash/games/wordle.sh)"' fi } } _color_prompt_() { # set a fancy prompt (non-color, unless we know we "want" color) case "$TERM" in xterm-color | *-256color) color_prompt=yes ;; esac # uncomment for a colored prompt, if the terminal has the capability; turned # off by default to not distract the user: the focus in a terminal window # should be on the output of commands, not on the prompt force_color_prompt=yes if [ -n "$force_color_prompt" ]; then if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then # We have color support; assume it's compliant with Ecma-48 # (ISO/IEC-6429). (Lack of such support is extremely rare, and such # a case would tend to support setf rather than setaf.) color_prompt=yes else color_prompt= fi if [ "$color_prompt" = yes ]; then # PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ ' PS1="\[$(tput setaf 196)\]\u\[$(tput setaf 247)\]@\[$(tput setaf 203)\]\h \[$(tput setaf 49)\]\w \[$(tput sgr0)\]$ " else PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ ' fi unset color_prompt force_color_prompt fi } _end() { # ─< fastfetch >──────────────────────────────────────────────────────────────────────────── if command_exists fastfetch; then alias ff="fastfetch" alias clearff="command clear & fastfetch" alias clearf="command clear & fastfetch" if fastfetch --config os >/dev/null 2>&1; then alias f="fastfetch --config os" else git clone https://git.k4li.de/mirrors/fastfetch.git "$HOME/.local/share/fastfetch" >/dev/null 2>&1 exec $SHELL fi command clear & fastfetch fi if command_exists cowsay; then alias clear='clear && cowsay -f tux "$(uptime --pretty)"' cowsay -f tux "$(uptime --pretty)" fi print_echo_messages } # ╭────────╮ # │ CODING │ # ╰────────╯ _coding_() { # ─< h stands for HUGO >────────────────────────────────────────────────────────────────── if command_exists hugo; then alias h='hugo' alias hs='hugo server -D --noHTTPCache --disableFastRender' fi # Function to get the IP address get_ip() { ip a | grep 'inet ' | grep -v '127.0.0.1' | awk '{print $2}' | cut -d/ -f1 | head -n 1 } # Check if php is available, then create the alias if command -v php >/dev/null 2>&1; then alias phprun="php artisan serve --host=$(get_ip) --port=8000" fi # Check if npm is available, then create the alias if command -v npm >/dev/null 2>&1; then alias npmrun="npm run dev -- --host=$(get_ip) --port=8001" fi } get_packager() { . /etc/os-release case "$ID" in # Debian-based ubuntu | debian | pop | kali | zorin | rhinoh) if command_exists nala; then alias search="nala search" alias install="$_sudo nala install --assume-yes" alias update="$_sudo nala update && $_sudo nala upgrade --full" alias remove="$_sudo nala purge" else alias search="apt-cache search" alias install="$_sudo apt-get install --yes" alias update="$_sudo apt-get update && $_sudo apt-get upgrade" alias remove="$_sudo apt-get purge" fi alias unbreak="$_sudo dpkg --configure -a" ;; # Arch-based arch | manjaro | endevouros | garuda) if command_exists paru; then alias search="paru -Ss" alias install="paru -S --noconfirm" alias update="paru -Syu" alias remove="paru -R" elif command_exists yay; then alias search="yay -Ss" alias install="yay -S --noconfirm" alias update="yay -Syu" alias remove="yay -R" else alias search="$_sudo pacman -Ss" alias install="$_sudo pacman -S --noconfirm" alias update="$_sudo pacman -Syu" alias remove="$_sudo pacman -R" fi ;; # RHEL-based fedora | centos) alias search="dnf search" alias install="$_sudo dnf install" alias update="$_sudo dnf update" alias remove="$_sudo dnf remove" ;; # openSUSE opensuse-*) alias search="zypper search" alias install="$_sudo zypper install --no-confirm" alias update="$_sudo zypper update" alias remove="$_sudo zypper remove" ;; # Alpine alpine) alias install="$_sudo apk add" alias update="$_sudo apk update && $_sudo apk upgrade" alias remove="$_sudo apk del" ;; esac } get_alias() { _sensible.bash_ _coding_ _alias _games } main() { _blesh _init _color_prompt_ check_root get_packager get_alias _end } main