最終更新 2 weeks ago

Cross-platform Zsh setup scripts and managed dotfiles with Oh My Zsh framework, Starship prompt, aliases, functions, and path configuration.

修正履歴 a687400630e8624842c9e1727696f73d724c46c6

README.md Raw

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 Raw
1#!/bin/bash
2set -euo pipefail
3
4# =============================
5# COLORS & LOGGING
6# =============================
7RED='\033[0;31m'
8GREEN='\033[0;32m'
9YELLOW='\033[1;33m'
10BLUE='\033[0;34m'
11NC='\033[0m'
12
13log() { echo -e "${BLUE}[INFO]${NC} $*"; }
14ok() { echo -e "${GREEN}[OK]${NC} $*"; }
15warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
16err() { echo -e "${RED}[ERROR]${NC} $*" >&2; }
17
18# =============================
19# CONFIG
20# =============================
21CONFIG_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# =============================
33check_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# =============================
43configure_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
57install_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
69install_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
77install_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
86clean_macos_aliases() {
87 local alias_file="$HOME/.alias"
88 if [[ -f "$alias_file" ]]; then
89 log "Patching .alias for macOS..."
90 sed -i '' '/# System update & cleanup/,/sudo apt-get clean/d' "$alias_file"
91 sed -i '' '/# xclip shortcuts/,/xclip -selection clipboard/d' "$alias_file"
92 sed -i '' '/./,$!d' "$alias_file"
93 ok "macOS incompatibilities removed from .alias"
94 fi
95}
96
97clean_pathrc() {
98 local pathrc_file="$HOME/.pathrc"
99 if [[ -f "$pathrc_file" ]]; then
100 log "Patching .pathrc to remove 'open' function..."
101 # Deletes the function block from name down to the closing brace }
102 sed -i '' '/^open[ (].*/,/^}/d' "$pathrc_file"
103 ok "'open' function removed from .pathrc"
104 fi
105}
106
107download_configs() {
108 log "Downloading custom config files..."
109 local backup="$HOME/.config_backup_$(date +%Y%m%d_%H%M%S)"
110 mkdir -p "$backup"
111
112 for entry in "${CONFIG_FILES[@]}"; do
113 local f="${entry%%|*}"
114 local url="${entry##*|}"
115
116 if [[ -f "$HOME/$f" ]]; then
117 cp "$HOME/$f" "$backup/"
118 fi
119 log "Fetching $f ..."
120 curl -fsSL "$url" -o "$HOME/$f" || warn "Failed to download $f"
121 done
122
123 clean_macos_aliases
124 clean_pathrc
125
126 ok "Configs downloaded (Backup at $backup)"
127}
128
129update_zshrc() {
130 local zshrc="$HOME/.zshrc"
131 log "Updating .zshrc..."
132
133 if [[ ! -f "$zshrc" ]]; then
134 warn ".zshrc not found, creating new one..."
135 touch "$zshrc"
136 fi
137
138 grep -q 'ZSH_THEME="spaceship"' "$zshrc" || sed -i '' 's/ZSH_THEME=".*"/ZSH_THEME="spaceship"/' "$zshrc"
139 grep -q 'zsh-autosuggestions' "$zshrc" || sed -i '' 's/plugins=(/plugins=(zsh-autosuggestions /' "$zshrc"
140 grep -q 'zsh-syntax-highlighting' "$zshrc" || sed -i '' 's/plugins=(/plugins=(zsh-syntax-highlighting /' "$zshrc"
141 ok ".zshrc updated with plugins and theme"
142}
143
144switch_shell() {
145 log "Starting Zsh session..."
146 echo -e "${YELLOW}Type 'exit' to return to this installer menu.${NC}"
147 echo "----------------------------------------"
148 zsh -l
149 echo "----------------------------------------"
150 ok "Returned from Zsh session"
151}
152
153# =============================
154# INTERACTIVE MENU
155# =============================
156show_menu() {
157 echo "==========================================="
158 echo "macOS Zsh Setup - Choose what to do"
159 echo "==========================================="
160 echo " 0) Run ALL steps (1-7)"
161 echo " 1) Configure Git"
162 echo " 2) Install Oh My Zsh"
163 echo " 3) Install plugins (autosuggestions, syntax highlighting)"
164 echo " 4) Install Spaceship theme"
165 echo " 5) Download custom configs (~/.alias, .vimrc, etc.)"
166 echo " 6) Update ~/.zshrc for plugins & theme"
167 echo " 7) Switch to Zsh (Temporary Sub-shell)"
168 echo " 8) Quit"
169 echo "==========================================="
170}
171
172run_choices() {
173 local input
174 read -p "Select: " input
175 input="${input//,/ }"
176
177 local -a to_run=()
178 local -a to_exclude=()
179
180 # Parse inputs
181 for item in $input; do
182 if [[ "$item" == !* ]]; then
183 to_exclude+=("${item:1}")
184 elif [[ "$item" == "0" ]]; then
185 to_run+=(1 2 3 4 5 6 7)
186 else
187 to_run+=("$item")
188 fi
189 done
190
191 # Fixed: Loop logic to handle empty arrays in older Bash versions
192 if [[ ${#to_run[@]} -gt 0 ]]; then
193 for choice in "${to_run[@]}"; do
194 local skip=false
195
196 # Only loop if exclusions exist to avoid "unbound variable" error
197 if [[ ${#to_exclude[@]} -gt 0 ]]; then
198 for ex in "${to_exclude[@]}"; do
199 if [[ "$choice" == "$ex" ]]; then
200 skip=true; break
201 fi
202 done
203 fi
204
205 $skip && continue
206
207 case "$choice" in
208 1) configure_git ;;
209 2) install_oh_my_zsh ;;
210 3) install_plugins ;;
211 4) install_theme ;;
212 5) download_configs ;;
213 6) update_zshrc ;;
214 7) switch_shell ;;
215 8) exit 0 ;;
216 *) warn "Skipping invalid option: $choice" ;;
217 esac
218 echo
219 done
220 fi
221}
222
223# =============================
224# MAIN
225# =============================
226main() {
227 check_requirements
228 while true; do
229 show_menu
230 run_choices
231 read -p "Do you want to run more options? (y/n): " again
232 [[ "$again" =~ ^[Yy]$ ]] || break
233 done
234 ok "macOS configuration complete!"
235}
236
237main "$@"
zsh_ubuntu.sh Raw
1#!/bin/bash
2set -euo pipefail
3
4# =============================
5# COLORS & LOGGING
6# =============================
7RED='\033[0;31m'
8GREEN='\033[0;32m'
9YELLOW='\033[1;33m'
10BLUE='\033[0;34m'
11NC='\033[0m'
12
13log() { echo -e "${BLUE}[INFO]${NC} $*"; }
14ok() { echo -e "${GREEN}[OK]${NC} $*"; }
15warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
16err() { echo -e "${RED}[ERROR]${NC} $*" >&2; }
17
18# =============================
19# FLAGS & CONFIG
20# =============================
21SKIP_PACKAGES=false
22SKIP_GIT_CONFIG=false
23SKIP_SHELL_CHANGE=false
24CUSTOM_CONFIG=false
25SKIP_XCLIP=false
26INSTALL_HOMEBREW=false
27
28declare -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# =============================
40detect_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# =============================
52check_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# =============================
67update_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
75install_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
83set_timezone() {
84 log "Setting timezone to Asia/Singapore..."
85 sudo timedatectl set-timezone Asia/Singapore
86 ok "Timezone set to Asia/Singapore"
87}
88
89install_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
103configure_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
113install_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
121configure_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
131install_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
140install_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
148install_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
157download_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
171update_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
188switch_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# =============================
205show_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
228run_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# =============================
284main() {
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
295main "$@"