Skip to content

Instantly share code, notes, and snippets.

@blattmann
Created August 5, 2026 15:25
Show Gist options
  • Select an option

  • Save blattmann/7bc45de2d0a8dc7908b28e816d118ea2 to your computer and use it in GitHub Desktop.

Select an option

Save blattmann/7bc45de2d0a8dc7908b28e816d118ea2 to your computer and use it in GitHub Desktop.
bash settings for windows
# Point to .bashrc where all our magic is stored (linux defaults ;) )
if [ -r ~/.bashrc ]; then
source ~/.bashrc
fi
# ==============================================
# .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