Created
August 5, 2026 15:25
-
-
Save blattmann/7bc45de2d0a8dc7908b28e816d118ea2 to your computer and use it in GitHub Desktop.
bash settings for windows
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Point to .bashrc where all our magic is stored (linux defaults ;) ) | |
| if [ -r ~/.bashrc ]; then | |
| source ~/.bashrc | |
| fi |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # ============================================== | |
| # .bashrc — Windows 11 / Git Bash / WSL | |
| # | |
| # Last Update: 2026/08/05 | |
| # Description: Developer shell configuration | |
| # | |
| # Features: | |
| # • Custom PATH management | |
| # • Portable Git Bash and WSL path detection | |
| # • NVM auto-switch per project | |
| # • Git branch and Node version in prompt | |
| # • Switchable color themes | |
| # • Project aliases and dynamic "go" navigation | |
| # • Alt+Shift+K to clear console | |
| # ============================================== | |
| # ---------------------------- | |
| # USER VARIABLES | |
| # ---------------------------- | |
| export DEV_USER="{YOUR-USER}" | |
| export DEV_DIRECTORY="{YOUR-DEV-DIRECTORY-ROOT-FOLDER}" | |
| # Example: | |
| # export DEV_DIRECTORY="Documents/www" | |
| export NPM_BASE64_AUTH="{YOUR-AUTH-KEY}" | |
| # See the internal setup instructions for information about obtaining this key. | |
| export NPM_REGISTRY_URL="https://lorem-ipsum.example/repository/npm/" | |
| # ---------------------------- | |
| # SHELL ENVIRONMENT DETECTION | |
| # ---------------------------- | |
| IS_WSL=false | |
| IS_GIT_BASH=false | |
| if grep -qi microsoft /proc/version 2>/dev/null; then | |
| IS_WSL=true | |
| elif [[ "$OSTYPE" == msys* || "$OSTYPE" == cygwin* ]]; then | |
| IS_GIT_BASH=true | |
| fi | |
| # ---------------------------- | |
| # GLOBAL DEFINITIONS | |
| # ---------------------------- | |
| if [ -f /etc/bashrc ]; then | |
| . /etc/bashrc | |
| elif [ -f /etc/bash.bashrc ]; then | |
| . /etc/bash.bashrc | |
| fi | |
| # ---------------------------- | |
| # PATH & ENVIRONMENT | |
| # ---------------------------- | |
| case ":$PATH:" in | |
| *":$HOME/.local/bin:"*) ;; | |
| *) PATH="$HOME/.local/bin:$PATH" ;; | |
| esac | |
| case ":$PATH:" in | |
| *":$HOME/bin:"*) ;; | |
| *) PATH="$HOME/bin:$PATH" ;; | |
| esac | |
| export PATH | |
| export SYSTEMD_PAGER= | |
| export HISTCONTROL="ignoredups:erasedups" | |
| export HISTSIZE=10000 | |
| export HISTFILESIZE=20000 | |
| # ---------------------------- | |
| # COLOR DEFINITIONS | |
| # ---------------------------- | |
| Black='0;30m' | |
| Red='0;31m' | |
| Green='0;32m' | |
| Yellow='0;33m' | |
| Blue='0;34m' | |
| Purple='0;35m' | |
| Cyan='0;36m' | |
| White='0;37m' | |
| PROMPT_THEME="${PROMPT_THEME:-dark}" | |
| case "$PROMPT_THEME" in | |
| dark) | |
| USER_COLOR="$Green" | |
| CWD_COLOR="$Yellow" | |
| GIT_COLOR="$Cyan" | |
| NODE_COLOR="$Purple" | |
| ;; | |
| light) | |
| USER_COLOR="$Blue" | |
| CWD_COLOR="$Purple" | |
| GIT_COLOR="$Red" | |
| NODE_COLOR="$Green" | |
| ;; | |
| minimal) | |
| USER_COLOR="$White" | |
| CWD_COLOR="$White" | |
| GIT_COLOR="$White" | |
| NODE_COLOR="$White" | |
| ;; | |
| *) | |
| USER_COLOR="$Green" | |
| CWD_COLOR="$Yellow" | |
| GIT_COLOR="$Cyan" | |
| NODE_COLOR="$Purple" | |
| ;; | |
| esac | |
| # ---------------------------- | |
| # WINDOWS & DEVELOPMENT PATHS | |
| # ---------------------------- | |
| if $IS_WSL; then | |
| export WINDOWS_USER_PATH="/mnt/c/Users/$DEV_USER" | |
| else | |
| export WINDOWS_USER_PATH="/c/Users/$DEV_USER" | |
| fi | |
| # The DEV_PATH variable points to the root development directory. | |
| export USER_PATH="$WINDOWS_USER_PATH" | |
| export DEV_PATH="$USER_PATH/$DEV_DIRECTORY" | |
| # Project-specific paths | |
| export TESTPROJECT="$DEV_PATH/testproject" | |
| # ---------------------------- | |
| # PROJECT & FOLDER ALIASES | |
| # ---------------------------- | |
| # Navigate to the development root folder. | |
| alias home='cd "$DEV_PATH"' | |
| alias www='cd "$DEV_PATH"' | |
| # Project-specific aliases | |
| alias testproject='cd "$TESTPROJECT/Testfolder"' | |
| # Navigate to a folder relative to DEV_PATH. | |
| go() { | |
| local relative_path="${1:-}" | |
| local target | |
| if [[ -z "$relative_path" ]]; then | |
| cd "$DEV_PATH" || return | |
| return | |
| fi | |
| target="$DEV_PATH/$relative_path" | |
| if [[ -d "$target" ]]; then | |
| cd "$target" || return | |
| else | |
| printf '\e[31mPath not found:\e[0m %s\n' "$target" >&2 | |
| return 1 | |
| fi | |
| } | |
| # Tab completion for the go command. | |
| _go_complete() { | |
| local current_word | |
| local relative_parent | |
| local search_directory | |
| local entry | |
| local relative_result | |
| current_word="${COMP_WORDS[COMP_CWORD]}" | |
| if [[ "$current_word" == */* ]]; then | |
| relative_parent="${current_word%/*}" | |
| search_directory="$DEV_PATH/$relative_parent" | |
| else | |
| relative_parent="" | |
| search_directory="$DEV_PATH" | |
| fi | |
| COMPREPLY=() | |
| while IFS= read -r entry; do | |
| entry="${entry%/}" | |
| if [[ -n "$relative_parent" ]]; then | |
| relative_result="$relative_parent/${entry##*/}" | |
| else | |
| relative_result="${entry##*/}" | |
| fi | |
| COMPREPLY+=("$relative_result/") | |
| done < <( | |
| compgen -d -- "$search_directory/${current_word##*/}" 2>/dev/null | |
| ) | |
| } | |
| complete -o nospace -F _go_complete go | |
| # ---------------------------- | |
| # SHELL SHORTCUTS | |
| # ---------------------------- | |
| alias bashrcedit='nano ~/.bashrc' | |
| alias bashrcsave='source ~/.bashrc' | |
| alias cp='cp -iv' | |
| alias mv='mv -iv' | |
| alias mkdir='mkdir -pv' | |
| alias l='ls -lah' | |
| alias la='ls -a' | |
| alias ll='ls -FGlAhp' | |
| alias c='clear' | |
| alias ..='cd ../' | |
| alias ...='cd ../../' | |
| alias .3='cd ../../../' | |
| alias .4='cd ../../../../' | |
| alias .5='cd ../../../../../' | |
| alias .6='cd ../../../../../../' | |
| alias which='type -all' | |
| # Display each PATH entry on its own line. | |
| path() { | |
| printf '%s\n' "${PATH//:/$'\n'}" | |
| } | |
| # ---------------------------- | |
| # FILE MANAGEMENT UTILITIES | |
| # ---------------------------- | |
| zipf() { | |
| if [[ -z "${1:-}" ]]; then | |
| echo "Usage: zipf <file-or-directory>" >&2 | |
| return 1 | |
| fi | |
| zip -r "${1%/}.zip" "$1" | |
| } | |
| extract() { | |
| local archive="${1:-}" | |
| if [[ -z "$archive" ]]; then | |
| echo "Usage: extract <archive>" >&2 | |
| return 1 | |
| fi | |
| if [[ ! -f "$archive" ]]; then | |
| printf "'%s' is not a valid file\n" "$archive" >&2 | |
| return 1 | |
| fi | |
| case "$archive" in | |
| *.tar.bz2 | *.tbz2) | |
| tar xjf "$archive" | |
| ;; | |
| *.tar.gz | *.tgz) | |
| tar xzf "$archive" | |
| ;; | |
| *.tar.xz | *.txz) | |
| tar xJf "$archive" | |
| ;; | |
| *.tar) | |
| tar xf "$archive" | |
| ;; | |
| *.bz2) | |
| bunzip2 "$archive" | |
| ;; | |
| *.gz) | |
| gunzip "$archive" | |
| ;; | |
| *.zip) | |
| unzip "$archive" | |
| ;; | |
| *.rar) | |
| unrar e "$archive" | |
| ;; | |
| *.7z) | |
| 7z x "$archive" | |
| ;; | |
| *) | |
| printf "'%s' cannot be extracted by extract()\n" "$archive" >&2 | |
| return 1 | |
| ;; | |
| esac | |
| } | |
| # ---------------------------- | |
| # NODE / NVM CONFIGURATION | |
| # ---------------------------- | |
| if $IS_WSL; then | |
| export NVM_HOME="$WINDOWS_USER_PATH/Apps/nvm" | |
| export NVM_SYMLINK="$WINDOWS_USER_PATH/Apps/nodejs" | |
| else | |
| export NVM_HOME="$WINDOWS_USER_PATH/Apps/nvm" | |
| export NVM_SYMLINK="$WINDOWS_USER_PATH/Apps/nodejs" | |
| fi | |
| append_to_path() { | |
| local directory="${1:-}" | |
| [[ -n "$directory" ]] || return | |
| case ":$PATH:" in | |
| *":$directory:"*) ;; | |
| *) PATH="$PATH:$directory" ;; | |
| esac | |
| } | |
| append_to_path "$NVM_HOME" | |
| append_to_path "$NVM_SYMLINK" | |
| export PATH | |
| # Find the newest installed Node version. | |
| detect_latest_node() { | |
| local latest="" | |
| if [[ -d "$NVM_HOME" ]]; then | |
| latest=$( | |
| find "$NVM_HOME" \ | |
| -mindepth 1 \ | |
| -maxdepth 1 \ | |
| -type d \ | |
| -printf '%f\n' 2>/dev/null | | |
| grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | | |
| sort -V | | |
| tail -n 1 | |
| ) | |
| fi | |
| printf '%s\n' "$latest" | |
| } | |
| # Default Node version. | |
| # This value is updated by setnode(). | |
| export NODE_VERSION="${NODE_VERSION:-v22.12.0}" | |
| node_version_directory() { | |
| printf '%s/%s\n' "$NVM_HOME" "$NODE_VERSION" | |
| } | |
| update_path_for_node() { | |
| local node_directory | |
| node_directory="$(node_version_directory)" | |
| append_to_path "$node_directory" | |
| export PATH | |
| } | |
| update_path_for_node | |
| # Run the selected Windows Node executable directly. | |
| node() { | |
| local executable | |
| executable="$(node_version_directory)/node.exe" | |
| if [[ ! -f "$executable" ]]; then | |
| printf '\e[31mNode executable not found:\e[0m %s\n' "$executable" >&2 | |
| return 1 | |
| fi | |
| "$executable" "$@" | |
| } | |
| npm() { | |
| local executable | |
| executable="$(node_version_directory)/npm.cmd" | |
| if [[ ! -f "$executable" ]]; then | |
| printf '\e[31mnpm executable not found:\e[0m %s\n' "$executable" >&2 | |
| return 1 | |
| fi | |
| "$executable" "$@" | |
| } | |
| npx() { | |
| local executable | |
| executable="$(node_version_directory)/npx.cmd" | |
| if [[ ! -f "$executable" ]]; then | |
| printf '\e[31mnpx executable not found:\e[0m %s\n' "$executable" >&2 | |
| return 1 | |
| fi | |
| "$executable" "$@" | |
| } | |
| # Switch the active Node version. | |
| # | |
| # Usage: | |
| # setnode 22.12.0 | |
| # setnode v22.12.0 | |
| setnode() { | |
| local requested_version="${1:-}" | |
| local normalized_version | |
| local node_directory | |
| if [[ -z "$requested_version" ]]; then | |
| echo "Usage: setnode <version>" >&2 | |
| return 1 | |
| fi | |
| normalized_version="${requested_version#v}" | |
| normalized_version="v$normalized_version" | |
| node_directory="$NVM_HOME/$normalized_version" | |
| if [[ ! -d "$node_directory" ]]; then | |
| printf '\e[31mNode version is not installed:\e[0m %s\n' "$normalized_version" >&2 | |
| return 1 | |
| fi | |
| export NODE_VERSION="$normalized_version" | |
| sed -i \ | |
| "s/^export NODE_VERSION=.*/export NODE_VERSION=\"${normalized_version}\"/" \ | |
| "$HOME/.bashrc" | |
| update_path_for_node | |
| printf '\e[32mSwitched to Node %s\e[0m\n' "$normalized_version" | |
| node -v | |
| } | |
| # Install and switch Node when entering a directory containing .nvmrc. | |
| auto_nvmrc() { | |
| local nvmrc_path="$PWD/.nvmrc" | |
| local requested_version | |
| local normalized_version | |
| [[ -f "$nvmrc_path" ]] || return | |
| requested_version="$( | |
| tr -d 'v[:space:]' < "$nvmrc_path" | |
| )" | |
| [[ -n "$requested_version" ]] || return | |
| normalized_version="v$requested_version" | |
| if [[ "$NODE_VERSION" == "$normalized_version" ]]; then | |
| return | |
| fi | |
| printf '\e[36mDetected .nvmrc → Node %s\e[0m\n' "$normalized_version" | |
| if [[ ! -d "$NVM_HOME/$normalized_version" ]]; then | |
| if [[ ! -f "$NVM_HOME/nvm.exe" ]]; then | |
| printf '\e[31mnvm.exe was not found:\e[0m %s\n' "$NVM_HOME/nvm.exe" >&2 | |
| return 1 | |
| fi | |
| printf '\e[33mInstalling Node %s...\e[0m\n' "$normalized_version" | |
| "$NVM_HOME/nvm.exe" install "$requested_version" | |
| if [[ $? -ne 0 ]]; then | |
| printf '\e[31mFailed to install Node %s.\e[0m\n' "$normalized_version" >&2 | |
| return 1 | |
| fi | |
| fi | |
| setnode "$requested_version" | |
| } | |
| auto_nvmrc_prompt() { | |
| auto_nvmrc | |
| } | |
| if [[ -n "${PROMPT_COMMAND:-}" ]]; then | |
| case ";$PROMPT_COMMAND;" in | |
| *";auto_nvmrc_prompt;"*) ;; | |
| *) PROMPT_COMMAND="auto_nvmrc_prompt; $PROMPT_COMMAND" ;; | |
| esac | |
| else | |
| PROMPT_COMMAND="auto_nvmrc_prompt" | |
| fi | |
| # ---------------------------- | |
| # GIT UTILITIES | |
| # ---------------------------- | |
| removeMerged() { | |
| local current_branch | |
| local branch | |
| current_branch="$(git branch --show-current 2>/dev/null)" | |
| git branch --merged 2>/dev/null | | |
| sed 's/^[*+[:space:]]*//' | | |
| while IFS= read -r branch; do | |
| [[ -n "$branch" ]] || continue | |
| [[ "$branch" == "$current_branch" ]] && continue | |
| case "$branch" in | |
| main | master | develop | development) | |
| continue | |
| ;; | |
| esac | |
| git branch -d -- "$branch" | |
| done | |
| } | |
| forceRemove() { | |
| local current_branch | |
| local branch | |
| current_branch="$(git branch --show-current 2>/dev/null)" | |
| git branch --no-merged 2>/dev/null | | |
| sed 's/^[*+[:space:]]*//' | | |
| while IFS= read -r branch; do | |
| [[ -n "$branch" ]] || continue | |
| [[ "$branch" == "$current_branch" ]] && continue | |
| case "$branch" in | |
| main | master | develop | development) | |
| continue | |
| ;; | |
| esac | |
| git branch -D -- "$branch" | |
| done | |
| } | |
| git_branch() { | |
| local branch | |
| branch="$(git branch --show-current 2>/dev/null)" | |
| if [[ -n "$branch" ]]; then | |
| printf '[%s]' "$branch" | |
| fi | |
| } | |
| # ---------------------------- | |
| # GIT PROMPT SUPPORT | |
| # ---------------------------- | |
| if [[ -f "$HOME/.git-prompt.sh" ]]; then | |
| if ! grep -q '<html' "$HOME/.git-prompt.sh" 2>/dev/null; then | |
| . "$HOME/.git-prompt.sh" | |
| fi | |
| fi | |
| # Provide a fallback when Git's prompt helper is unavailable. | |
| if ! declare -F __git_ps1 >/dev/null 2>&1; then | |
| __git_ps1() { | |
| local format="${1:-(%s)}" | |
| local branch | |
| branch="$(git branch --show-current 2>/dev/null)" | |
| if [[ -n "$branch" ]]; then | |
| printf "$format" "$branch" | |
| fi | |
| } | |
| fi | |
| # ---------------------------- | |
| # PROMPT CONFIGURATION | |
| # ---------------------------- | |
| node_version() { | |
| if [[ -n "${NODE_VERSION:-}" ]]; then | |
| printf '[%s]' "$NODE_VERSION" | |
| return | |
| fi | |
| local detected_version | |
| detected_version="$(detect_latest_node)" | |
| if [[ -n "$detected_version" ]]; then | |
| printf '[%s]' "$detected_version" | |
| fi | |
| } | |
| export PS1='\[\e[${USER_COLOR}\]\u:\[\e[${CWD_COLOR}\]\W/\[\e[${GIT_COLOR}\]$(__git_ps1 "(%s)")\[\e[${NODE_COLOR}\]$(node_version)\[\e[0m\]$ ' | |
| # ---------------------------- | |
| # KEYBOARD SHORTCUTS | |
| # ---------------------------- | |
| clear_console() { | |
| clear | |
| printf '\e[1;32mConsole cleared ✅\e[0m\n' | |
| sleep 0.3 | |
| clear | |
| } | |
| if [[ $- == *i* ]]; then | |
| bind -x '"\eK":clear_console' | |
| fi | |
| # ---------------------------- | |
| # MISC SETTINGS | |
| # ---------------------------- | |
| export EDITOR="nano" | |
| export VISUAL="$EDITOR" | |
| export BLOCKSIZE="1k" | |
| # ---------------------------- | |
| # NPM REGISTRY CONFIGURATION | |
| # ---------------------------- | |
| set_npmrc_value() { | |
| local npmrc="$1" | |
| local key_pattern="$2" | |
| local entry="$3" | |
| if grep -Eq "$key_pattern" "$npmrc" 2>/dev/null; then | |
| sed -i "s|${key_pattern}.*|${entry}|" "$npmrc" | |
| else | |
| printf '%s\n' "$entry" >> "$npmrc" | |
| fi | |
| } | |
| configure_npm_registry() { | |
| local npmrc="$HOME/.npmrc" | |
| local registry_url | |
| local host_no_protocol | |
| local registry_entry | |
| local auth_entry | |
| local always_auth_entry | |
| registry_url="${NPM_REGISTRY_URL%/}/" | |
| host_no_protocol="${registry_url#http://}" | |
| host_no_protocol="${host_no_protocol#https://}" | |
| registry_entry="registry=${registry_url}" | |
| auth_entry="//${host_no_protocol}:_auth=${NPM_BASE64_AUTH}" | |
| always_auth_entry="//${host_no_protocol}:always-auth=true" | |
| mkdir -p "$(dirname "$npmrc")" | |
| touch "$npmrc" | |
| set_npmrc_value \ | |
| "$npmrc" \ | |
| '^registry=' \ | |
| "$registry_entry" | |
| set_npmrc_value \ | |
| "$npmrc" \ | |
| "^//${host_no_protocol//\//\\/}:_auth=" \ | |
| "$auth_entry" | |
| set_npmrc_value \ | |
| "$npmrc" \ | |
| "^//${host_no_protocol//\//\\/}:always-auth=" \ | |
| "$always_auth_entry" | |
| } | |
| if [[ "$NPM_BASE64_AUTH" != "{YOUR-AUTH-KEY}" ]] && | |
| [[ -n "$NPM_BASE64_AUTH" ]] && | |
| [[ "$NPM_REGISTRY_URL" != "https://lorem-ipsum.example/repository/npm/" ]]; then | |
| configure_npm_registry | |
| fi | |
| # ============================================== | |
| # EOF — clean, consistent, and extensible | |
| # ============================================== |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment