README.md
· 372 B · Markdown
Originalformat
# Bash Script Installer for Zsh
The "Bash Script Installer" simplifies the setup of Zsh.
## Zsh
### Ubuntu
```zsh
bash -c "$(curl -fsSL https://opengist.rmrf.online/weehong/f0d940c3c1214bf5b7996195199fdc09/raw/HEAD/zsh_ubuntu.sh)"
```
### MacOS
```zsh
bash -c "$(curl -fsSL https://opengist.rmrf.online/weehong/f0d940c3c1214bf5b7996195199fdc09/raw/HEAD/zsh_macos.sh)"
```
Bash Script Installer for Zsh
The "Bash Script Installer" simplifies the setup of Zsh.
Zsh
Ubuntu
bash -c "$(curl -fsSL https://opengist.rmrf.online/weehong/f0d940c3c1214bf5b7996195199fdc09/raw/HEAD/zsh_ubuntu.sh)"
MacOS
bash -c "$(curl -fsSL https://opengist.rmrf.online/weehong/f0d940c3c1214bf5b7996195199fdc09/raw/HEAD/zsh_macos.sh)"
zsh_macos.sh
· 7.4 KiB · Bash
Originalformat
#!/bin/bash
set -euo pipefail
# =============================
# COLORS & LOGGING
# =============================
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log() { echo -e "${BLUE}[INFO]${NC} $*"; }
ok() { echo -e "${GREEN}[OK]${NC} $*"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
err() { echo -e "${RED}[ERROR]${NC} $*" >&2; }
# =============================
# CONFIG
# =============================
CONFIG_FILES=(
".alias|https://gist.githubusercontent.com/weehongkoh/72bdb76beacacf2ca3dd39a72395b9ee/raw/alias"
".func|https://gist.githubusercontent.com/weehongkoh/72bdb76beacacf2ca3dd39a72395b9ee/raw/func"
".pathrc|https://gist.githubusercontent.com/weehongkoh/72bdb76beacacf2ca3dd39a72395b9ee/raw/pathrc"
".sourcerc|https://gist.githubusercontent.com/weehongkoh/72bdb76beacacf2ca3dd39a72395b9ee/raw/sourcerc"
".vimrc|https://gist.githubusercontent.com/weehongkoh/72bdb76beacacf2ca3dd39a72395b9ee/raw/vimrc"
".zshrc|https://gist.githubusercontent.com/weehongkoh/72bdb76beacacf2ca3dd39a72395b9ee/raw/zshrc"
)
# =============================
# REQUIREMENTS
# =============================
check_requirements() {
if [[ $EUID -eq 0 ]]; then
err "Do not run as root on macOS"
exit 1
fi
}
# =============================
# INSTALLATION FUNCTIONS
# =============================
configure_git() {
log "Configuring Git..."
read -p "Git name (leave empty to skip): " name
read -p "Git email (leave empty to skip): " email
if [[ -n "$name" ]]; then
git config --global user.name "$name"
fi
if [[ -n "$email" ]]; then
git config --global user.email "$email"
fi
ok "Git configured"
}
install_oh_my_zsh() {
if [[ -d "$HOME/.oh-my-zsh" ]]; then
ok "Oh My Zsh already installed"
return
fi
log "Installing Oh My Zsh..."
RUNZSH=no CHSH=no KEEP_ZSHRC=yes \
sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
ok "Oh My Zsh installed"
}
install_plugins() {
log "Installing plugins..."
local dir="${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/plugins"
[[ -d "$dir/zsh-autosuggestions" ]] || git clone https://github.com/zsh-users/zsh-autosuggestions "$dir/zsh-autosuggestions"
[[ -d "$dir/zsh-syntax-highlighting" ]] || git clone https://github.com/zsh-users/zsh-syntax-highlighting "$dir/zsh-syntax-highlighting"
ok "Plugins installed"
}
install_theme() {
log "Installing Spaceship theme..."
local themes="${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/themes"
local dir="$themes/spaceship-prompt"
[[ -d "$dir" ]] || git clone https://github.com/spaceship-prompt/spaceship-prompt.git "$dir" --depth=1
ln -sf "$dir/spaceship.zsh-theme" "$themes/spaceship.zsh-theme"
ok "Theme installed"
}
clean_macos_aliases() {
local alias_file="$HOME/.alias"
if [[ -f "$alias_file" ]]; then
log "Patching .alias for macOS (removing apt-get and xclip blocks)..."
# Delete the multiline apt-get block (from the comment down to the final command)
sed -i '' '/# System update & cleanup/,/sudo apt-get clean/d' "$alias_file"
# Delete the xclip block
sed -i '' '/# xclip shortcuts/,/xclip -selection clipboard/d' "$alias_file"
ok "macOS incompatibilities removed from .alias"
fi
}
download_configs() {
log "Downloading custom config files..."
local backup="$HOME/.config_backup_$(date +%Y%m%d_%H%M%S)"
mkdir -p "$backup"
for entry in "${CONFIG_FILES[@]}"; do
local f="${entry%%|*}"
local url="${entry##*|}"
if [[ -f "$HOME/$f" ]]; then
cp "$HOME/$f" "$backup/"
fi
log "Fetching $f ..."
curl -fsSL "$url" -o "$HOME/$f" || warn "Failed to download $f"
done
# Automatically clean up the macOS incompatible aliases after downloading
clean_macos_aliases
ok "Configs downloaded (Backup at $backup)"
}
update_zshrc() {
local zshrc="$HOME/.zshrc"
log "Updating .zshrc..."
if [[ ! -f "$zshrc" ]]; then
warn ".zshrc not found, creating new one..."
touch "$zshrc"
fi
# macOS BSD sed requires '-i ''' for in-place editing
grep -q 'ZSH_THEME="spaceship"' "$zshrc" || sed -i '' 's/ZSH_THEME=".*"/ZSH_THEME="spaceship"/' "$zshrc"
grep -q 'zsh-autosuggestions' "$zshrc" || sed -i '' 's/plugins=(/plugins=(zsh-autosuggestions /' "$zshrc"
grep -q 'zsh-syntax-highlighting' "$zshrc" || sed -i '' 's/plugins=(/plugins=(zsh-syntax-highlighting /' "$zshrc"
ok ".zshrc updated with plugins and theme"
}
switch_shell() {
log "Starting Zsh session..."
echo -e "${YELLOW}Type 'exit' to return to this installer menu.${NC}"
echo "----------------------------------------"
zsh -l
echo "----------------------------------------"
ok "Returned from Zsh session"
}
# =============================
# INTERACTIVE MENU
# =============================
show_menu() {
echo "==========================================="
echo "macOS Zsh Setup - Choose what to do"
echo "==========================================="
echo " 0) Run ALL steps (1-7)"
echo " 1) Configure Git"
echo " 2) Install Oh My Zsh"
echo " 3) Install plugins (autosuggestions, syntax highlighting)"
echo " 4) Install Spaceship theme"
echo " 5) Download custom configs (~/.alias, .vimrc, etc.)"
echo " 6) Update ~/.zshrc for plugins & theme"
echo " 7) Switch to Zsh (Temporary Sub-shell)"
echo " 8) Quit"
echo "==========================================="
echo "Inputs: '0' (All), '1 3 4' (Specific), '0 !1 !5' (All except 1 and 5)"
}
run_choices() {
local input
read -p "Select: " input
input="${input//,/ }" # replace commas with spaces if used
local -a to_run=()
local -a to_exclude=()
# Parse positive selections and negative exclusions
for item in $input; do
if [[ "$item" == !* ]]; then
to_exclude+=("${item:1}") # Strip the '!' character
elif [[ "$item" == "0" ]]; then
to_run+=(1 2 3 4 5 6 7)
else
to_run+=("$item")
fi
done
# Protect against empty arrays crashing older bash versions with 'set -u'
if [[ ${#to_run[@]} -gt 0 ]]; then
for choice in "${to_run[@]}"; do
local skip=false
if [[ ${#to_exclude[@]} -gt 0 ]]; then
for ex in "${to_exclude[@]}"; do
if [[ "$choice" == "$ex" ]]; then
skip=true
break
fi
done
fi
$skip && continue
case "$choice" in
1) configure_git ;;
2) install_oh_my_zsh ;;
3) install_plugins ;;
4) install_theme ;;
5) download_configs ;;
6) update_zshrc ;;
7) switch_shell ;;
8) log "Exiting..."; exit 0 ;;
*) warn "Skipping invalid option: $choice" ;;
esac
echo
done
fi
}
# =============================
# MAIN
# =============================
main() {
check_requirements
while true; do
show_menu
run_choices
read -p "Do you want to run more options? (y/n): " again
[[ "$again" =~ ^[Yy]$ ]] || break
done
ok "macOS configuration complete!"
}
main "$@"
| 1 | #!/bin/bash |
| 2 | set -euo pipefail |
| 3 | |
| 4 | # ============================= |
| 5 | # COLORS & LOGGING |
| 6 | # ============================= |
| 7 | RED='\033[0;31m' |
| 8 | GREEN='\033[0;32m' |
| 9 | YELLOW='\033[1;33m' |
| 10 | BLUE='\033[0;34m' |
| 11 | NC='\033[0m' |
| 12 | |
| 13 | log() { echo -e "${BLUE}[INFO]${NC} $*"; } |
| 14 | ok() { echo -e "${GREEN}[OK]${NC} $*"; } |
| 15 | warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } |
| 16 | err() { echo -e "${RED}[ERROR]${NC} $*" >&2; } |
| 17 | |
| 18 | # ============================= |
| 19 | # CONFIG |
| 20 | # ============================= |
| 21 | CONFIG_FILES=( |
| 22 | ".alias|https://gist.githubusercontent.com/weehongkoh/72bdb76beacacf2ca3dd39a72395b9ee/raw/alias" |
| 23 | ".func|https://gist.githubusercontent.com/weehongkoh/72bdb76beacacf2ca3dd39a72395b9ee/raw/func" |
| 24 | ".pathrc|https://gist.githubusercontent.com/weehongkoh/72bdb76beacacf2ca3dd39a72395b9ee/raw/pathrc" |
| 25 | ".sourcerc|https://gist.githubusercontent.com/weehongkoh/72bdb76beacacf2ca3dd39a72395b9ee/raw/sourcerc" |
| 26 | ".vimrc|https://gist.githubusercontent.com/weehongkoh/72bdb76beacacf2ca3dd39a72395b9ee/raw/vimrc" |
| 27 | ".zshrc|https://gist.githubusercontent.com/weehongkoh/72bdb76beacacf2ca3dd39a72395b9ee/raw/zshrc" |
| 28 | ) |
| 29 | |
| 30 | # ============================= |
| 31 | # REQUIREMENTS |
| 32 | # ============================= |
| 33 | check_requirements() { |
| 34 | if [[ $EUID -eq 0 ]]; then |
| 35 | err "Do not run as root on macOS" |
| 36 | exit 1 |
| 37 | fi |
| 38 | } |
| 39 | |
| 40 | # ============================= |
| 41 | # INSTALLATION FUNCTIONS |
| 42 | # ============================= |
| 43 | configure_git() { |
| 44 | log "Configuring Git..." |
| 45 | read -p "Git name (leave empty to skip): " name |
| 46 | read -p "Git email (leave empty to skip): " email |
| 47 | |
| 48 | if [[ -n "$name" ]]; then |
| 49 | git config --global user.name "$name" |
| 50 | fi |
| 51 | if [[ -n "$email" ]]; then |
| 52 | git config --global user.email "$email" |
| 53 | fi |
| 54 | ok "Git configured" |
| 55 | } |
| 56 | |
| 57 | install_oh_my_zsh() { |
| 58 | if [[ -d "$HOME/.oh-my-zsh" ]]; then |
| 59 | ok "Oh My Zsh already installed" |
| 60 | return |
| 61 | fi |
| 62 | |
| 63 | log "Installing Oh My Zsh..." |
| 64 | RUNZSH=no CHSH=no KEEP_ZSHRC=yes \ |
| 65 | sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" |
| 66 | ok "Oh My Zsh installed" |
| 67 | } |
| 68 | |
| 69 | install_plugins() { |
| 70 | log "Installing plugins..." |
| 71 | local dir="${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/plugins" |
| 72 | [[ -d "$dir/zsh-autosuggestions" ]] || git clone https://github.com/zsh-users/zsh-autosuggestions "$dir/zsh-autosuggestions" |
| 73 | [[ -d "$dir/zsh-syntax-highlighting" ]] || git clone https://github.com/zsh-users/zsh-syntax-highlighting "$dir/zsh-syntax-highlighting" |
| 74 | ok "Plugins installed" |
| 75 | } |
| 76 | |
| 77 | install_theme() { |
| 78 | log "Installing Spaceship theme..." |
| 79 | local themes="${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/themes" |
| 80 | local dir="$themes/spaceship-prompt" |
| 81 | [[ -d "$dir" ]] || git clone https://github.com/spaceship-prompt/spaceship-prompt.git "$dir" --depth=1 |
| 82 | ln -sf "$dir/spaceship.zsh-theme" "$themes/spaceship.zsh-theme" |
| 83 | ok "Theme installed" |
| 84 | } |
| 85 | |
| 86 | clean_macos_aliases() { |
| 87 | local alias_file="$HOME/.alias" |
| 88 | if [[ -f "$alias_file" ]]; then |
| 89 | log "Patching .alias for macOS (removing apt-get and xclip blocks)..." |
| 90 | |
| 91 | # Delete the multiline apt-get block (from the comment down to the final command) |
| 92 | sed -i '' '/# System update & cleanup/,/sudo apt-get clean/d' "$alias_file" |
| 93 | |
| 94 | # Delete the xclip block |
| 95 | sed -i '' '/# xclip shortcuts/,/xclip -selection clipboard/d' "$alias_file" |
| 96 | |
| 97 | ok "macOS incompatibilities removed from .alias" |
| 98 | fi |
| 99 | } |
| 100 | |
| 101 | download_configs() { |
| 102 | log "Downloading custom config files..." |
| 103 | local backup="$HOME/.config_backup_$(date +%Y%m%d_%H%M%S)" |
| 104 | mkdir -p "$backup" |
| 105 | |
| 106 | for entry in "${CONFIG_FILES[@]}"; do |
| 107 | local f="${entry%%|*}" |
| 108 | local url="${entry##*|}" |
| 109 | |
| 110 | if [[ -f "$HOME/$f" ]]; then |
| 111 | cp "$HOME/$f" "$backup/" |
| 112 | fi |
| 113 | log "Fetching $f ..." |
| 114 | curl -fsSL "$url" -o "$HOME/$f" || warn "Failed to download $f" |
| 115 | done |
| 116 | |
| 117 | # Automatically clean up the macOS incompatible aliases after downloading |
| 118 | clean_macos_aliases |
| 119 | |
| 120 | ok "Configs downloaded (Backup at $backup)" |
| 121 | } |
| 122 | |
| 123 | update_zshrc() { |
| 124 | local zshrc="$HOME/.zshrc" |
| 125 | log "Updating .zshrc..." |
| 126 | |
| 127 | if [[ ! -f "$zshrc" ]]; then |
| 128 | warn ".zshrc not found, creating new one..." |
| 129 | touch "$zshrc" |
| 130 | fi |
| 131 | |
| 132 | # macOS BSD sed requires '-i ''' for in-place editing |
| 133 | grep -q 'ZSH_THEME="spaceship"' "$zshrc" || sed -i '' 's/ZSH_THEME=".*"/ZSH_THEME="spaceship"/' "$zshrc" |
| 134 | grep -q 'zsh-autosuggestions' "$zshrc" || sed -i '' 's/plugins=(/plugins=(zsh-autosuggestions /' "$zshrc" |
| 135 | grep -q 'zsh-syntax-highlighting' "$zshrc" || sed -i '' 's/plugins=(/plugins=(zsh-syntax-highlighting /' "$zshrc" |
| 136 | ok ".zshrc updated with plugins and theme" |
| 137 | } |
| 138 | |
| 139 | switch_shell() { |
| 140 | log "Starting Zsh session..." |
| 141 | echo -e "${YELLOW}Type 'exit' to return to this installer menu.${NC}" |
| 142 | echo "----------------------------------------" |
| 143 | zsh -l |
| 144 | echo "----------------------------------------" |
| 145 | ok "Returned from Zsh session" |
| 146 | } |
| 147 | |
| 148 | # ============================= |
| 149 | # INTERACTIVE MENU |
| 150 | # ============================= |
| 151 | show_menu() { |
| 152 | echo "===========================================" |
| 153 | echo "macOS Zsh Setup - Choose what to do" |
| 154 | echo "===========================================" |
| 155 | echo " 0) Run ALL steps (1-7)" |
| 156 | echo " 1) Configure Git" |
| 157 | echo " 2) Install Oh My Zsh" |
| 158 | echo " 3) Install plugins (autosuggestions, syntax highlighting)" |
| 159 | echo " 4) Install Spaceship theme" |
| 160 | echo " 5) Download custom configs (~/.alias, .vimrc, etc.)" |
| 161 | echo " 6) Update ~/.zshrc for plugins & theme" |
| 162 | echo " 7) Switch to Zsh (Temporary Sub-shell)" |
| 163 | echo " 8) Quit" |
| 164 | echo "===========================================" |
| 165 | echo "Inputs: '0' (All), '1 3 4' (Specific), '0 !1 !5' (All except 1 and 5)" |
| 166 | } |
| 167 | |
| 168 | run_choices() { |
| 169 | local input |
| 170 | read -p "Select: " input |
| 171 | input="${input//,/ }" # replace commas with spaces if used |
| 172 | |
| 173 | local -a to_run=() |
| 174 | local -a to_exclude=() |
| 175 | |
| 176 | # Parse positive selections and negative exclusions |
| 177 | for item in $input; do |
| 178 | if [[ "$item" == !* ]]; then |
| 179 | to_exclude+=("${item:1}") # Strip the '!' character |
| 180 | elif [[ "$item" == "0" ]]; then |
| 181 | to_run+=(1 2 3 4 5 6 7) |
| 182 | else |
| 183 | to_run+=("$item") |
| 184 | fi |
| 185 | done |
| 186 | |
| 187 | # Protect against empty arrays crashing older bash versions with 'set -u' |
| 188 | if [[ ${#to_run[@]} -gt 0 ]]; then |
| 189 | for choice in "${to_run[@]}"; do |
| 190 | |
| 191 | local skip=false |
| 192 | if [[ ${#to_exclude[@]} -gt 0 ]]; then |
| 193 | for ex in "${to_exclude[@]}"; do |
| 194 | if [[ "$choice" == "$ex" ]]; then |
| 195 | skip=true |
| 196 | break |
| 197 | fi |
| 198 | done |
| 199 | fi |
| 200 | |
| 201 | $skip && continue |
| 202 | |
| 203 | case "$choice" in |
| 204 | 1) configure_git ;; |
| 205 | 2) install_oh_my_zsh ;; |
| 206 | 3) install_plugins ;; |
| 207 | 4) install_theme ;; |
| 208 | 5) download_configs ;; |
| 209 | 6) update_zshrc ;; |
| 210 | 7) switch_shell ;; |
| 211 | 8) log "Exiting..."; exit 0 ;; |
| 212 | *) warn "Skipping invalid option: $choice" ;; |
| 213 | esac |
| 214 | echo |
| 215 | done |
| 216 | fi |
| 217 | } |
| 218 | |
| 219 | # ============================= |
| 220 | # MAIN |
| 221 | # ============================= |
| 222 | main() { |
| 223 | check_requirements |
| 224 | while true; do |
| 225 | show_menu |
| 226 | run_choices |
| 227 | read -p "Do you want to run more options? (y/n): " again |
| 228 | [[ "$again" =~ ^[Yy]$ ]] || break |
| 229 | done |
| 230 | ok "macOS configuration complete!" |
| 231 | } |
| 232 | |
| 233 | main "$@" |
zsh_ubuntu.sh
· 9.1 KiB · Bash
Originalformat
#!/bin/bash
set -euo pipefail
# =============================
# COLORS & LOGGING
# =============================
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log() { echo -e "${BLUE}[INFO]${NC} $*"; }
ok() { echo -e "${GREEN}[OK]${NC} $*"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
err() { echo -e "${RED}[ERROR]${NC} $*" >&2; }
# =============================
# FLAGS & CONFIG
# =============================
SKIP_PACKAGES=false
SKIP_GIT_CONFIG=false
SKIP_SHELL_CHANGE=false
CUSTOM_CONFIG=false
SKIP_XCLIP=false
INSTALL_HOMEBREW=false
declare -A CONFIG_FILES=(
[".alias"]="https://gist.githubusercontent.com/weehongkoh/72bdb76beacacf2ca3dd39a72395b9ee/raw/alias"
[".func"]="https://gist.githubusercontent.com/weehongkoh/72bdb76beacacf2ca3dd39a72395b9ee/raw/func"
[".pathrc"]="https://gist.githubusercontent.com/weehongkoh/72bdb76beacacf2ca3dd39a72395b9ee/raw/pathrc"
[".sourcerc"]="https://gist.githubusercontent.com/weehongkoh/72bdb76beacacf2ca3dd39a72395b9ee/raw/sourcerc"
[".vimrc"]="https://gist.githubusercontent.com/weehongkoh/72bdb76beacacf2ca3dd39a72395b9ee/raw/vimrc"
[".zshrc"]="https://gist.githubusercontent.com/weehongkoh/72bdb76beacacf2ca3dd39a72395b9ee/raw/zshrc"
)
# =============================
# OS DETECTION
# =============================
detect_os() {
if [ -f /etc/os-release ]; then
. /etc/os-release
echo "$ID"
else
echo "unknown"
fi
}
# =============================
# REQUIREMENTS
# =============================
check_requirements() {
# Fixed to prevent silent failure with 'set -e'
if [[ $EUID -eq 0 ]]; then
err "Do not run as root"
exit 1
fi
if ! command -v sudo >/dev/null; then
err "sudo required"
exit 1
fi
}
# =============================
# INSTALLATION FUNCTIONS
# =============================
update_system() {
$SKIP_PACKAGES && return
log "Updating system..."
sudo apt-get update -y
sudo apt-get upgrade -y
ok "System updated"
}
install_packages() {
$SKIP_PACKAGES && return
log "Installing core packages..."
sudo apt-get install -y \
zsh git vim curl wget unzip zip build-essential xz-utils
ok "Packages installed"
}
set_timezone() {
log "Setting timezone to Asia/Singapore..."
sudo timedatectl set-timezone Asia/Singapore
ok "Timezone set to Asia/Singapore"
}
install_xclip() {
local os
os=$(detect_os)
[[ "$os" != "ubuntu" && "$os" != "debian" ]] && return 0
$SKIP_XCLIP && return 0
command -v xclip >/dev/null && ok "xclip already installed" && return 0
read -r -p "Install xclip? (y/N): " r
case "$r" in
[Yy]) sudo apt-get install -y xclip; ok "xclip installed" ;;
*) log "Skipping xclip installation" ;;
esac
}
configure_git() {
$SKIP_GIT_CONFIG && return
log "Configuring Git..."
read -p "Git name (leave empty to skip): " name
read -p "Git email (leave empty to skip): " email
[[ -n "$name" ]] && git config --global user.name "$name"
[[ -n "$email" ]] && git config --global user.email "$email"
ok "Git configured"
}
install_homebrew() {
! $INSTALL_HOMEBREW && return
command -v brew >/dev/null && ok "Homebrew already installed" && return
log "Installing Homebrew..."
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
ok "Homebrew installed"
}
configure_shell() {
$SKIP_SHELL_CHANGE && return
log "Changing default shell to zsh..."
local zsh_path
zsh_path=$(command -v zsh)
grep -qx "$zsh_path" /etc/shells || echo "$zsh_path" | sudo tee -a /etc/shells >/dev/null
sudo chsh -s "$zsh_path" "$USER"
ok "Shell changed (requires logout/login to take effect)"
}
install_oh_my_zsh() {
[[ -d "$HOME/.oh-my-zsh" ]] && ok "Oh My Zsh already installed" && return
log "Installing Oh My Zsh..."
# Keep zshrc ensures we don't blow away configs if they exist
RUNZSH=no CHSH=no KEEP_ZSHRC=yes \
sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
ok "Oh My Zsh installed"
}
install_plugins() {
log "Installing plugins..."
local dir="${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/plugins"
[[ -d "$dir/zsh-autosuggestions" ]] || git clone https://github.com/zsh-users/zsh-autosuggestions "$dir/zsh-autosuggestions"
[[ -d "$dir/zsh-syntax-highlighting" ]] || git clone https://github.com/zsh-users/zsh-syntax-highlighting "$dir/zsh-syntax-highlighting"
ok "Plugins installed"
}
install_theme() {
log "Installing Spaceship theme..."
local themes="${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/themes"
local dir="$themes/spaceship-prompt"
[[ -d "$dir" ]] || git clone https://github.com/spaceship-prompt/spaceship-prompt.git "$dir" --depth=1
ln -sf "$dir/spaceship.zsh-theme" "$themes/spaceship.zsh-theme"
ok "Theme installed"
}
download_configs() {
log "Downloading custom config files..."
local backup="$HOME/.config_backup_$(date +%Y%m%d_%H%M%S)"
mkdir -p "$backup"
for f in "${!CONFIG_FILES[@]}"; do
if [[ -f "$HOME/$f" ]]; then
cp "$HOME/$f" "$backup/"
fi
log "Fetching $f ..."
curl -fsSL "${CONFIG_FILES[$f]}" -o "$HOME/$f" || warn "Failed to download $f"
done
ok "Configs downloaded (Backup at $backup)"
}
update_zshrc() {
local zshrc="$HOME/.zshrc"
log "Updating .zshrc..."
# Create zshrc if missing to prevent errors
if [[ ! -f "$zshrc" ]]; then
warn ".zshrc not found, creating new one..."
touch "$zshrc"
fi
# Use || true to prevent 'set -e' from exiting if grep finds nothing
grep -q 'ZSH_THEME="spaceship"' "$zshrc" || sed -i 's/ZSH_THEME=".*"/ZSH_THEME="spaceship"/' "$zshrc"
grep -q 'zsh-autosuggestions' "$zshrc" || sed -i 's/plugins=(/plugins=(zsh-autosuggestions /' "$zshrc"
grep -q 'zsh-syntax-highlighting' "$zshrc" || sed -i 's/plugins=(/plugins=(zsh-syntax-highlighting /' "$zshrc"
ok ".zshrc updated with plugins and theme"
}
switch_shell() {
$SKIP_SHELL_CHANGE && return
log "Starting Zsh session..."
echo -e "${YELLOW}Type 'exit' to return to this installer menu.${NC}"
echo "----------------------------------------"
# Run zsh as a subprocess, not exec
zsh -l
echo "----------------------------------------"
ok "Returned from Zsh session"
}
# =============================
# INTERACTIVE MENU
# =============================
show_menu() {
echo "==========================================="
echo "Zsh Installer - Choose what to do"
echo "==========================================="
echo " 0) Run ALL steps (1-13)"
echo " 1) Update system packages"
echo " 2) Install core packages (zsh, git, vim, etc.)"
echo " 3) Set Timezone (Asia/Singapore)"
echo " 4) Install xclip"
echo " 5) Configure Git"
echo " 6) Install Homebrew"
echo " 7) Configure shell (chsh - sets default shell)"
echo " 8) Install Oh My Zsh"
echo " 9) Install plugins (autosuggestions, syntax highlighting)"
echo "10) Install Spaceship theme"
echo "11) Download custom configs (~/.alias, .vimrc, etc.)"
echo "12) Update ~/.zshrc for plugins & theme"
echo "13) Switch to Zsh (Temporary Sub-shell)"
echo "14) Quit"
echo "==========================================="
echo "Inputs: '0' (All), '2 3 7' (Specific), '0 !4 !6' (All except 4 and 6)"
}
run_choices() {
local input
read -p "Select: " input
input="${input//,/ }" # replace commas with spaces
local -a to_run=()
local -a to_exclude=()
# Parse positive selections and negative exclusions
for item in $input; do
if [[ "$item" == !* ]]; then
to_exclude+=("${item:1}") # Strip the '!' character
elif [[ "$item" == "0" ]]; then
to_run+=(1 2 3 4 5 6 7 8 9 10 11 12 13)
else
to_run+=("$item")
fi
done
# Loop through intended runs and apply exclusions
for choice in "${to_run[@]}"; do
local skip=false
for ex in "${to_exclude[@]}"; do
if [[ "$choice" == "$ex" ]]; then
skip=true
break
fi
done
$skip && continue
case "$choice" in
1) update_system ;;
2) install_packages ;;
3) set_timezone ;;
4) install_xclip ;;
5) configure_git ;;
6) install_homebrew ;;
7) configure_shell ;;
8) install_oh_my_zsh ;;
9) install_plugins ;;
10) install_theme ;;
11) download_configs ;;
12) update_zshrc ;;
13) switch_shell ;;
14) log "Exiting..."; exit 0 ;;
*) warn "Skipping invalid option: $choice" ;;
esac
echo
done
}
# =============================
# MAIN
# =============================
main() {
check_requirements
while true; do
show_menu
run_choices
read -p "Do you want to run more options? (y/n): " again
[[ "$again" =~ ^[Yy]$ ]] || break
done
ok "Zsh installation/configuration complete!"
}
main "$@"
| 1 | #!/bin/bash |
| 2 | set -euo pipefail |
| 3 | |
| 4 | # ============================= |
| 5 | # COLORS & LOGGING |
| 6 | # ============================= |
| 7 | RED='\033[0;31m' |
| 8 | GREEN='\033[0;32m' |
| 9 | YELLOW='\033[1;33m' |
| 10 | BLUE='\033[0;34m' |
| 11 | NC='\033[0m' |
| 12 | |
| 13 | log() { echo -e "${BLUE}[INFO]${NC} $*"; } |
| 14 | ok() { echo -e "${GREEN}[OK]${NC} $*"; } |
| 15 | warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } |
| 16 | err() { echo -e "${RED}[ERROR]${NC} $*" >&2; } |
| 17 | |
| 18 | # ============================= |
| 19 | # FLAGS & CONFIG |
| 20 | # ============================= |
| 21 | SKIP_PACKAGES=false |
| 22 | SKIP_GIT_CONFIG=false |
| 23 | SKIP_SHELL_CHANGE=false |
| 24 | CUSTOM_CONFIG=false |
| 25 | SKIP_XCLIP=false |
| 26 | INSTALL_HOMEBREW=false |
| 27 | |
| 28 | declare -A CONFIG_FILES=( |
| 29 | [".alias"]="https://gist.githubusercontent.com/weehongkoh/72bdb76beacacf2ca3dd39a72395b9ee/raw/alias" |
| 30 | [".func"]="https://gist.githubusercontent.com/weehongkoh/72bdb76beacacf2ca3dd39a72395b9ee/raw/func" |
| 31 | [".pathrc"]="https://gist.githubusercontent.com/weehongkoh/72bdb76beacacf2ca3dd39a72395b9ee/raw/pathrc" |
| 32 | [".sourcerc"]="https://gist.githubusercontent.com/weehongkoh/72bdb76beacacf2ca3dd39a72395b9ee/raw/sourcerc" |
| 33 | [".vimrc"]="https://gist.githubusercontent.com/weehongkoh/72bdb76beacacf2ca3dd39a72395b9ee/raw/vimrc" |
| 34 | [".zshrc"]="https://gist.githubusercontent.com/weehongkoh/72bdb76beacacf2ca3dd39a72395b9ee/raw/zshrc" |
| 35 | ) |
| 36 | |
| 37 | # ============================= |
| 38 | # OS DETECTION |
| 39 | # ============================= |
| 40 | detect_os() { |
| 41 | if [ -f /etc/os-release ]; then |
| 42 | . /etc/os-release |
| 43 | echo "$ID" |
| 44 | else |
| 45 | echo "unknown" |
| 46 | fi |
| 47 | } |
| 48 | |
| 49 | # ============================= |
| 50 | # REQUIREMENTS |
| 51 | # ============================= |
| 52 | check_requirements() { |
| 53 | # Fixed to prevent silent failure with 'set -e' |
| 54 | if [[ $EUID -eq 0 ]]; then |
| 55 | err "Do not run as root" |
| 56 | exit 1 |
| 57 | fi |
| 58 | if ! command -v sudo >/dev/null; then |
| 59 | err "sudo required" |
| 60 | exit 1 |
| 61 | fi |
| 62 | } |
| 63 | |
| 64 | # ============================= |
| 65 | # INSTALLATION FUNCTIONS |
| 66 | # ============================= |
| 67 | update_system() { |
| 68 | $SKIP_PACKAGES && return |
| 69 | log "Updating system..." |
| 70 | sudo apt-get update -y |
| 71 | sudo apt-get upgrade -y |
| 72 | ok "System updated" |
| 73 | } |
| 74 | |
| 75 | install_packages() { |
| 76 | $SKIP_PACKAGES && return |
| 77 | log "Installing core packages..." |
| 78 | sudo apt-get install -y \ |
| 79 | zsh git vim curl wget unzip zip build-essential xz-utils |
| 80 | ok "Packages installed" |
| 81 | } |
| 82 | |
| 83 | set_timezone() { |
| 84 | log "Setting timezone to Asia/Singapore..." |
| 85 | sudo timedatectl set-timezone Asia/Singapore |
| 86 | ok "Timezone set to Asia/Singapore" |
| 87 | } |
| 88 | |
| 89 | install_xclip() { |
| 90 | local os |
| 91 | os=$(detect_os) |
| 92 | [[ "$os" != "ubuntu" && "$os" != "debian" ]] && return 0 |
| 93 | $SKIP_XCLIP && return 0 |
| 94 | command -v xclip >/dev/null && ok "xclip already installed" && return 0 |
| 95 | |
| 96 | read -r -p "Install xclip? (y/N): " r |
| 97 | case "$r" in |
| 98 | [Yy]) sudo apt-get install -y xclip; ok "xclip installed" ;; |
| 99 | *) log "Skipping xclip installation" ;; |
| 100 | esac |
| 101 | } |
| 102 | |
| 103 | configure_git() { |
| 104 | $SKIP_GIT_CONFIG && return |
| 105 | log "Configuring Git..." |
| 106 | read -p "Git name (leave empty to skip): " name |
| 107 | read -p "Git email (leave empty to skip): " email |
| 108 | [[ -n "$name" ]] && git config --global user.name "$name" |
| 109 | [[ -n "$email" ]] && git config --global user.email "$email" |
| 110 | ok "Git configured" |
| 111 | } |
| 112 | |
| 113 | install_homebrew() { |
| 114 | ! $INSTALL_HOMEBREW && return |
| 115 | command -v brew >/dev/null && ok "Homebrew already installed" && return |
| 116 | log "Installing Homebrew..." |
| 117 | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" |
| 118 | ok "Homebrew installed" |
| 119 | } |
| 120 | |
| 121 | configure_shell() { |
| 122 | $SKIP_SHELL_CHANGE && return |
| 123 | log "Changing default shell to zsh..." |
| 124 | local zsh_path |
| 125 | zsh_path=$(command -v zsh) |
| 126 | grep -qx "$zsh_path" /etc/shells || echo "$zsh_path" | sudo tee -a /etc/shells >/dev/null |
| 127 | sudo chsh -s "$zsh_path" "$USER" |
| 128 | ok "Shell changed (requires logout/login to take effect)" |
| 129 | } |
| 130 | |
| 131 | install_oh_my_zsh() { |
| 132 | [[ -d "$HOME/.oh-my-zsh" ]] && ok "Oh My Zsh already installed" && return |
| 133 | log "Installing Oh My Zsh..." |
| 134 | # Keep zshrc ensures we don't blow away configs if they exist |
| 135 | RUNZSH=no CHSH=no KEEP_ZSHRC=yes \ |
| 136 | sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" |
| 137 | ok "Oh My Zsh installed" |
| 138 | } |
| 139 | |
| 140 | install_plugins() { |
| 141 | log "Installing plugins..." |
| 142 | local dir="${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/plugins" |
| 143 | [[ -d "$dir/zsh-autosuggestions" ]] || git clone https://github.com/zsh-users/zsh-autosuggestions "$dir/zsh-autosuggestions" |
| 144 | [[ -d "$dir/zsh-syntax-highlighting" ]] || git clone https://github.com/zsh-users/zsh-syntax-highlighting "$dir/zsh-syntax-highlighting" |
| 145 | ok "Plugins installed" |
| 146 | } |
| 147 | |
| 148 | install_theme() { |
| 149 | log "Installing Spaceship theme..." |
| 150 | local themes="${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/themes" |
| 151 | local dir="$themes/spaceship-prompt" |
| 152 | [[ -d "$dir" ]] || git clone https://github.com/spaceship-prompt/spaceship-prompt.git "$dir" --depth=1 |
| 153 | ln -sf "$dir/spaceship.zsh-theme" "$themes/spaceship.zsh-theme" |
| 154 | ok "Theme installed" |
| 155 | } |
| 156 | |
| 157 | download_configs() { |
| 158 | log "Downloading custom config files..." |
| 159 | local backup="$HOME/.config_backup_$(date +%Y%m%d_%H%M%S)" |
| 160 | mkdir -p "$backup" |
| 161 | for f in "${!CONFIG_FILES[@]}"; do |
| 162 | if [[ -f "$HOME/$f" ]]; then |
| 163 | cp "$HOME/$f" "$backup/" |
| 164 | fi |
| 165 | log "Fetching $f ..." |
| 166 | curl -fsSL "${CONFIG_FILES[$f]}" -o "$HOME/$f" || warn "Failed to download $f" |
| 167 | done |
| 168 | ok "Configs downloaded (Backup at $backup)" |
| 169 | } |
| 170 | |
| 171 | update_zshrc() { |
| 172 | local zshrc="$HOME/.zshrc" |
| 173 | log "Updating .zshrc..." |
| 174 | |
| 175 | # Create zshrc if missing to prevent errors |
| 176 | if [[ ! -f "$zshrc" ]]; then |
| 177 | warn ".zshrc not found, creating new one..." |
| 178 | touch "$zshrc" |
| 179 | fi |
| 180 | |
| 181 | # Use || true to prevent 'set -e' from exiting if grep finds nothing |
| 182 | grep -q 'ZSH_THEME="spaceship"' "$zshrc" || sed -i 's/ZSH_THEME=".*"/ZSH_THEME="spaceship"/' "$zshrc" |
| 183 | grep -q 'zsh-autosuggestions' "$zshrc" || sed -i 's/plugins=(/plugins=(zsh-autosuggestions /' "$zshrc" |
| 184 | grep -q 'zsh-syntax-highlighting' "$zshrc" || sed -i 's/plugins=(/plugins=(zsh-syntax-highlighting /' "$zshrc" |
| 185 | ok ".zshrc updated with plugins and theme" |
| 186 | } |
| 187 | |
| 188 | switch_shell() { |
| 189 | $SKIP_SHELL_CHANGE && return |
| 190 | |
| 191 | log "Starting Zsh session..." |
| 192 | echo -e "${YELLOW}Type 'exit' to return to this installer menu.${NC}" |
| 193 | echo "----------------------------------------" |
| 194 | |
| 195 | # Run zsh as a subprocess, not exec |
| 196 | zsh -l |
| 197 | |
| 198 | echo "----------------------------------------" |
| 199 | ok "Returned from Zsh session" |
| 200 | } |
| 201 | |
| 202 | # ============================= |
| 203 | # INTERACTIVE MENU |
| 204 | # ============================= |
| 205 | show_menu() { |
| 206 | echo "===========================================" |
| 207 | echo "Zsh Installer - Choose what to do" |
| 208 | echo "===========================================" |
| 209 | echo " 0) Run ALL steps (1-13)" |
| 210 | echo " 1) Update system packages" |
| 211 | echo " 2) Install core packages (zsh, git, vim, etc.)" |
| 212 | echo " 3) Set Timezone (Asia/Singapore)" |
| 213 | echo " 4) Install xclip" |
| 214 | echo " 5) Configure Git" |
| 215 | echo " 6) Install Homebrew" |
| 216 | echo " 7) Configure shell (chsh - sets default shell)" |
| 217 | echo " 8) Install Oh My Zsh" |
| 218 | echo " 9) Install plugins (autosuggestions, syntax highlighting)" |
| 219 | echo "10) Install Spaceship theme" |
| 220 | echo "11) Download custom configs (~/.alias, .vimrc, etc.)" |
| 221 | echo "12) Update ~/.zshrc for plugins & theme" |
| 222 | echo "13) Switch to Zsh (Temporary Sub-shell)" |
| 223 | echo "14) Quit" |
| 224 | echo "===========================================" |
| 225 | echo "Inputs: '0' (All), '2 3 7' (Specific), '0 !4 !6' (All except 4 and 6)" |
| 226 | } |
| 227 | |
| 228 | run_choices() { |
| 229 | local input |
| 230 | read -p "Select: " input |
| 231 | input="${input//,/ }" # replace commas with spaces |
| 232 | |
| 233 | local -a to_run=() |
| 234 | local -a to_exclude=() |
| 235 | |
| 236 | # Parse positive selections and negative exclusions |
| 237 | for item in $input; do |
| 238 | if [[ "$item" == !* ]]; then |
| 239 | to_exclude+=("${item:1}") # Strip the '!' character |
| 240 | elif [[ "$item" == "0" ]]; then |
| 241 | to_run+=(1 2 3 4 5 6 7 8 9 10 11 12 13) |
| 242 | else |
| 243 | to_run+=("$item") |
| 244 | fi |
| 245 | done |
| 246 | |
| 247 | # Loop through intended runs and apply exclusions |
| 248 | for choice in "${to_run[@]}"; do |
| 249 | |
| 250 | local skip=false |
| 251 | for ex in "${to_exclude[@]}"; do |
| 252 | if [[ "$choice" == "$ex" ]]; then |
| 253 | skip=true |
| 254 | break |
| 255 | fi |
| 256 | done |
| 257 | |
| 258 | $skip && continue |
| 259 | |
| 260 | case "$choice" in |
| 261 | 1) update_system ;; |
| 262 | 2) install_packages ;; |
| 263 | 3) set_timezone ;; |
| 264 | 4) install_xclip ;; |
| 265 | 5) configure_git ;; |
| 266 | 6) install_homebrew ;; |
| 267 | 7) configure_shell ;; |
| 268 | 8) install_oh_my_zsh ;; |
| 269 | 9) install_plugins ;; |
| 270 | 10) install_theme ;; |
| 271 | 11) download_configs ;; |
| 272 | 12) update_zshrc ;; |
| 273 | 13) switch_shell ;; |
| 274 | 14) log "Exiting..."; exit 0 ;; |
| 275 | *) warn "Skipping invalid option: $choice" ;; |
| 276 | esac |
| 277 | echo |
| 278 | done |
| 279 | } |
| 280 | |
| 281 | # ============================= |
| 282 | # MAIN |
| 283 | # ============================= |
| 284 | main() { |
| 285 | check_requirements |
| 286 | while true; do |
| 287 | show_menu |
| 288 | run_choices |
| 289 | read -p "Do you want to run more options? (y/n): " again |
| 290 | [[ "$again" =~ ^[Yy]$ ]] || break |
| 291 | done |
| 292 | ok "Zsh installation/configuration complete!" |
| 293 | } |
| 294 | |
| 295 | main "$@" |