Skip to content

Instantly share code, notes, and snippets.

@weehong
Last active July 18, 2025 10:13
Show Gist options
  • Save weehong/e3cb7b12ddda0fbe53485622275f17b0 to your computer and use it in GitHub Desktop.
Save weehong/e3cb7b12ddda0fbe53485622275f17b0 to your computer and use it in GitHub Desktop.
A cross-platform bash script that automatically updates VMware Workstation IP addresses in your system's hosts file

VMware IP Address Updater

A cross-platform bash script to automatically update VMware Workstation IP addresses in your system's hosts file. Works seamlessly across Windows (Git Bash/MSYS), WSL, Linux, and macOS with customizable .env configuration.

✨ Features

  • 🔄 Cross-platform compatibility - Windows, WSL, Linux, macOS
  • 🎯 Interactive IP input - Prompts for IP address with validation
  • ⚙️ Environment configuration - Customize defaults via .env file
  • 🚫 Duplicate prevention - Ensures no duplicate entries in hosts file
  • 💾 Automatic backups - Creates backup before modifications
  • IP validation - Real-time validation of IP address format
  • 🎨 Colored output - Easy-to-read status messages
  • 🔐 Permission handling - Automatic sudo/admin rights management

🚀 Quick Start

Local Installation

# Download the script
curl -O https://gist.githubusercontent.com/weehong/e3cb7b12ddda0fbe53485622275f17b0/raw/56a6e826e53fb1675bc404ad11ba6dfe5ee4a8bf/host.sh

# Make executable
chmod +x update_vmware_ip.sh

# Create sample configuration file
./update_vmware_ip.sh -c

# Copy and customize .env file
cp .env.example .env
# Edit .env with your preferred defaults

# Run in interactive mode (recommended)
./update_vmware_ip.sh -i

Remote Execution (One-liner)

# Execute directly from URL with custom IP and domain
curl -fsSL https://gist.githubusercontent.com/weehong/e3cb7b12ddda0fbe53485622275f17b0/raw/56a6e826e53fb1675bc404ad11ba6dfe5ee4a8bf/host.sh | VMWARE_IP=192.168.1.1 VMWARE_DOMAIN=ipvon.server bash

# Or use with arguments
curl -fsSL https://gist.githubusercontent.com/weehong/e3cb7b12ddda0fbe53485622275f17b0/raw/56a6e826e53fb1675bc404ad11ba6dfe5ee4a8bf/host.sh | bash -s -- 192.168.1.1 ipvon.server

# Interactive mode remotely
curl -fsSL https://gist.githubusercontent.com/weehong/e3cb7b12ddda0fbe53485622275f17b0/raw/56a6e826e53fb1675bc404ad11ba6dfe5ee4a8bf/host.sh | bash -s -- -i

⚙️ Configuration

Environment File (.env)

Create a .env file in the same directory as the script to customize default values:

# VMware IP Address Updater Configuration
VMWARE_IP=192.168.170.133
VMWARE_DOMAIN=dev.vernon.personal

# Examples:
# VMWARE_DOMAIN=vm.company.local
# VMWARE_DOMAIN=workstation.dev
# VMWARE_DOMAIN=vmware.local

Built-in Defaults

If no .env file exists, the script uses these defaults:

  • IP: 192.168.1.100
  • Domain: vmware.local

📖 Usage

Remote Execution (Quick & Easy)

# One-liner execution with environment variables
curl -fsSL https://gist.githubusercontent.com/weehong/e3cb7b12ddda0fbe53485622275f17b0/raw/56a6e826e53fb1675bc404ad11ba6dfe5ee4a8bf/host.sh | VMWARE_IP=192.168.170.133 VMWARE_DOMAIN=dev.mycompany.local bash

# With direct arguments
curl -fsSL https://gist.githubusercontent.com/weehong/e3cb7b12ddda0fbe53485622275f17b0/raw/56a6e826e53fb1675bc404ad11ba6dfe5ee4a8bf/host.sh | bash -s -- 192.168.170.133 dev.mycompany.local

# Interactive mode from remote
curl -fsSL https://gist.githubusercontent.com/weehong/e3cb7b12ddda0fbe53485622275f17b0/raw/56a6e826e53fb1675bc404ad11ba6dfe5ee4a8bf/host.sh | bash -s -- -i

Environment Setup

# Create sample .env configuration
./update_vmware_ip.sh -c

# Show help and current configuration
./update_vmware_ip.sh --help

Interactive Mode (Recommended)

# Interactive mode - uses .env defaults, prompts for IP
./update_vmware_ip.sh -i

# Interactive with custom domain
./update_vmware_ip.sh -i vm.custom.domain

Command Line Mode

# Use .env file values (or built-in defaults)
./update_vmware_ip.sh

# Update IP only
./update_vmware_ip.sh 192.168.170.133

# Update both IP and domain
./update_vmware_ip.sh 192.168.170.133 vm.example.com

Configuration Priority

The script follows this priority order:

  1. Command line arguments (highest priority)
  2. Interactive input
  3. .env file values
  4. Built-in defaults (lowest priority)

🎯 Use Cases

Perfect for scenarios where you need to:

  • Quick VM setup: One-liner remote execution for instant IP updates
  • CI/CD pipelines: Automated VMware IP configuration in build scripts
  • Team onboarding: Share a single command for consistent environment setup
  • Update VMware Workstation IP addresses across multiple environments
  • Sync IP changes between Windows host and WSL instances
  • Manage development environments with changing VM IPs
  • Maintain team consistency with shared .env configurations
  • Avoid manual hosts file editing and potential errors
  • Prevent duplicate host entries

📋 Example Output

VMware IP Address Updater
==================================
[INFO] Loading configuration from .env file...
[INFO] Loaded IP from .env: 192.168.170.133
[INFO] Loaded domain from .env: dev.vernon.personal

Current configuration:
  Domain: dev.vernon.personal
  Current IP: 192.168.170.133

Existing hosts entry found:
  192.168.170.100 dev.vernon.personal

Enter new IP address (or press Enter to use 192.168.170.133): 192.168.170.150

Configuration:
  Target: 192.168.170.150 dev.vernon.personal
Detected platform: wsl
Hosts file: /mnt/c/Windows/System32/drivers/etc/hosts

[INFO] Creating backup: /mnt/c/Windows/System32/drivers/etc/hosts.bak
[INFO] Updating existing entry for dev.vernon.personal...
[✅] Hosts file updated successfully:
  192.168.170.150 dev.vernon.personal
[✅] Single entry confirmed - no duplicates detected

[INFO] Note: Changes will affect both Windows and WSL
[INFO] You may need to flush DNS cache: ipconfig /flushdns

🛡️ Safety Features

  • Automatic backups: Creates .bak files before any modification
  • Duplicate detection: Scans and removes duplicate entries
  • Validation: Ensures IP addresses are properly formatted
  • Permission checks: Verifies write access before attempting changes
  • Platform detection: Uses appropriate commands for each OS
  • Safe .env parsing: Validates configuration values before use

🖥️ Platform Support

Platform Hosts File Location Sudo Required
WSL /mnt/c/Windows/System32/drivers/etc/hosts Yes
Windows (Git Bash) /c/Windows/System32/drivers/etc/hosts Admin Rights
Linux /etc/hosts Yes
macOS /etc/hosts Yes

⚠️ Requirements

  • Windows: Run terminal as Administrator
  • WSL/Linux/macOS: Script will use sudo automatically
  • Bash: Compatible shell environment
  • Optional: .env file for custom defaults

🔄 DNS Cache Flushing

After updating hosts file, you may need to flush DNS cache:

# Windows
ipconfig /flushdns

# macOS
sudo dscacheutil -flushcache

# Linux (systemd-resolved)
sudo systemctl restart systemd-resolved

📁 File Structure

your-project/
├── update_vmware_ip.sh    # Main script
├── .env                   # Your configuration (optional)
├── .env.example          # Sample configuration
└── hosts.bak             # Automatic backup (created when script runs)

🤝 Contributing

Found a bug or have a feature request? Feel free to:

  • Report issues in the comments
  • Suggest improvements
  • Fork and enhance the script

📜 License

This script is provided as-is under MIT License. Use at your own risk and always backup your hosts file before making changes.


💡 Pro Tips:

  • Remote execution: Use curl -fsSL url | VMWARE_IP=x.x.x.x VMWARE_DOMAIN=hostname bash for instant setup
  • Use ./update_vmware_ip.sh -c to create sample configuration
  • Use interactive mode (-i) for the best experience with validation and feedback
  • Keep your .env file in version control (without sensitive IPs) for team consistency
  • The script automatically handles platform differences - just run it anywhere!
#!/usr/bin/env bash
# === VMware IP Address Updater ===
# Cross-platform script to update VMware Workstation IP in hosts file
# Supports Windows (Git Bash/MSYS), WSL, Linux, and macOS
# === DEFAULT CONFIG ===
DEFAULT_IP="192.168.1.100" # Default placeholder IP - update as needed
DEFAULT_DOMAIN="vmware.local" # Default domain name
# Accept environment variables for remote execution
if [[ -n "$VMWARE_IP" ]]; then
DEFAULT_IP="$VMWARE_IP"
print_info "Using IP from environment: $VMWARE_IP"
fi
if [[ -n "$VMWARE_DOMAIN" ]]; then
DEFAULT_DOMAIN="$VMWARE_DOMAIN"
print_info "Using domain from environment: $VMWARE_DOMAIN"
fi
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# === FUNCTIONS ===
print_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
print_success() {
echo -e "${GREEN}[✅]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[⚠️]${NC} $1"
}
print_error() {
echo -e "${RED}[❌]${NC} $1"
}
validate_ip() {
local ip="$1"
local regex='^([0-9]{1,3}\.){3}[0-9]{1,3}$'
if [[ $ip =~ $regex ]]; then
# Check each octet is between 0-255
IFS='.' read -ra ADDR <<< "$ip"
for i in "${ADDR[@]}"; do
if [[ $i -lt 0 ]] || [[ $i -gt 255 ]]; then
return 1
fi
done
return 0
fi
return 1
}
# Load configuration from .env file if it exists
load_env_config() {
local script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
local env_file="$script_dir/.env"
if [[ -f "$env_file" ]]; then
print_info "Loading configuration from .env file..."
# Source the .env file safely
while IFS='=' read -r key value; do
# Skip empty lines and comments
[[ -z "$key" || "$key" =~ ^[[:space:]]*# ]] && continue
# Remove quotes and whitespace
key=$(echo "$key" | xargs)
value=$(echo "$value" | sed 's/^["'\'']//' | sed 's/["'\'']$//' | xargs)
case "$key" in
"VMWARE_IP")
if validate_ip "$value"; then
DEFAULT_IP="$value"
print_info "Loaded IP from .env: $DEFAULT_IP"
else
print_warning "Invalid IP in .env file: $value (using default)"
fi
;;
"VMWARE_DOMAIN")
if [[ -n "$value" ]]; then
DEFAULT_DOMAIN="$value"
print_info "Loaded domain from .env: $DEFAULT_DOMAIN"
fi
;;
esac
done < "$env_file"
else
print_info "No .env file found. Using default configuration."
print_info "Create .env file with VMWARE_IP and VMWARE_DOMAIN to customize defaults."
fi
}
create_sample_env() {
local script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
local env_file="$script_dir/.env"
local sample_file="$script_dir/.env.example"
print_info "Creating sample .env.example file..."
cat > "$sample_file" << EOF
# VMware IP Address Updater Configuration
# Copy this file to .env and modify as needed
# Default IP address for your VMware VM
VMWARE_IP=192.168.1.100
# Default domain name for your VMware VM
VMWARE_DOMAIN=vmware.local
# Examples:
# VMWARE_IP=192.168.170.133
# VMWARE_DOMAIN=dev.mycompany.local
# VMWARE_DOMAIN=vm.personal.network
EOF
print_success "Created .env.example file"
print_info "Copy to .env and customize: cp .env.example .env"
}
check_admin_rights() {
local hosts_file="$1"
if [[ ! -w "$hosts_file" ]]; then
return 1
fi
return 0
}
detect_platform() {
if grep -qi microsoft /proc/version 2>/dev/null; then
# WSL - use Windows hosts file
echo "wsl"
elif [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "win32" ]]; then
# Git Bash on Windows (MSYS)
echo "windows"
elif [[ "$OSTYPE" == "darwin"* ]]; then
# macOS
echo "macos"
else
# Native Linux
echo "linux"
fi
}
get_hosts_config() {
local platform="$1"
case "$platform" in
"wsl")
HOSTS_FILE="/mnt/c/Windows/System32/drivers/etc/hosts"
SUDO="sudo"
BACKUP_SUFFIX=".bak"
;;
"windows")
HOSTS_FILE="/c/Windows/System32/drivers/etc/hosts"
SUDO=""
BACKUP_SUFFIX=".bak"
;;
"macos")
HOSTS_FILE="/etc/hosts"
SUDO="sudo"
BACKUP_SUFFIX=".bak"
;;
"linux")
HOSTS_FILE="/etc/hosts"
SUDO="sudo"
BACKUP_SUFFIX=".bak"
;;
*)
print_error "Unsupported platform: $platform"
exit 1
;;
esac
}
backup_hosts_file() {
local hosts_file="$1"
local backup_file="${hosts_file}${BACKUP_SUFFIX}"
if [[ -f "$hosts_file" ]]; then
print_info "Creating backup: $backup_file"
if [[ -n "$SUDO" ]]; then
$SUDO cp "$hosts_file" "$backup_file"
else
cp "$hosts_file" "$backup_file" 2>/dev/null || {
print_warning "Could not create backup (insufficient permissions)"
}
fi
fi
}
prompt_for_ip() {
local current_ip="$1"
local domain="$2"
echo ""
print_info "Current configuration:"
echo " Domain: $domain"
echo " Current IP: $current_ip"
echo ""
# Check if domain exists in hosts file and show current entry
local existing_entry=$(grep -E "^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+[[:space:]]+$domain" "$HOSTS_FILE" 2>/dev/null)
if [[ -n "$existing_entry" ]]; then
print_info "Existing hosts entry found:"
echo " $existing_entry"
echo ""
fi
while true; do
read -p "Enter new IP address (or press Enter to use $current_ip): " user_input
# If user just pressed Enter, use current IP
if [[ -z "$user_input" ]]; then
echo "$current_ip"
return 0
fi
# Validate IP address format
if validate_ip "$user_input"; then
echo "$user_input"
return 0
else
print_error "Invalid IP address format. Please enter a valid IP (e.g., 192.168.1.100)"
fi
done
}
remove_duplicate_entries() {
local hosts_file="$1"
local domain="$2"
local sudo_cmd="$3"
# Count existing entries for the domain
local count=$(grep -c -E "[[:space:]]$domain([[:space:]]|$)" "$hosts_file" 2>/dev/null || echo "0")
if [[ $count -gt 1 ]]; then
print_warning "Found $count duplicate entries for $domain. Removing duplicates..."
# Create a temporary file to store the cleaned content
local temp_file=$(mktemp)
# Remove all lines containing the domain
grep -v -E "[[:space:]]$domain([[:space:]]|$)" "$hosts_file" > "$temp_file" 2>/dev/null
# Copy cleaned content back
if [[ -n "$sudo_cmd" ]]; then
$sudo_cmd cp "$temp_file" "$hosts_file"
else
cp "$temp_file" "$hosts_file"
fi
# Clean up
rm -f "$temp_file"
print_info "Removed $count duplicate entries"
fi
}
update_hosts_entry() {
local hosts_file="$1"
local domain="$2"
local entry="$3"
local sudo_cmd="$4"
# Check if we can write to the file
if ! check_admin_rights "$hosts_file" && [[ -z "$sudo_cmd" ]]; then
print_error "Insufficient permissions to modify $hosts_file"
print_info "Please run this script as Administrator (Windows) or with sudo (Linux/macOS)"
return 1
fi
# First, remove any duplicate entries for this domain
remove_duplicate_entries "$hosts_file" "$domain" "$sudo_cmd"
# Check if domain exists and update or append
local existing_entry=$(grep -E "^[^#]*[[:space:]]$domain([[:space:]]|$)" "$hosts_file" 2>/dev/null)
if [[ -n "$existing_entry" ]]; then
print_info "Updating existing entry for $domain..."
print_info "Old entry: $existing_entry"
# Create a more robust sed command that only matches the exact domain
local escaped_domain=$(echo "$domain" | sed 's/[.[\*^$()+?{|]/\\&/g')
# Use different sed syntax for different platforms
if [[ "$OSTYPE" == "darwin"* ]]; then
# macOS requires different sed syntax
$sudo_cmd sed -i "$BACKUP_SUFFIX" "s/^[^#]*[[:space:]]$escaped_domain\([[:space:]].*\)\{0,1\}$/$entry/" "$hosts_file"
else
# Linux/Windows
$sudo_cmd sed -i"$BACKUP_SUFFIX" "s/^[^#]*[[:space:]]$escaped_domain\([[:space:]].*\)\{0,1\}$/$entry/" "$hosts_file"
fi
else
print_info "Adding new entry for $domain..."
# Add entry to hosts file
if [[ -n "$sudo_cmd" ]]; then
echo "$entry" | $sudo_cmd tee -a "$hosts_file" >/dev/null
else
echo "$entry" >> "$hosts_file" 2>/dev/null || {
print_error "Failed to add entry (insufficient permissions)"
return 1
}
fi
fi
# Final check to ensure no duplicates were created
local final_count=$(grep -c -E "[[:space:]]$domain([[:space:]]|$)" "$hosts_file" 2>/dev/null || echo "0")
if [[ $final_count -gt 1 ]]; then
print_warning "Duplicate entries detected after update. Cleaning up..."
remove_duplicate_entries "$hosts_file" "$domain" "$sudo_cmd"
# Re-add the correct entry
if [[ -n "$sudo_cmd" ]]; then
echo "$entry" | $sudo_cmd tee -a "$hosts_file" >/dev/null
else
echo "$entry" >> "$hosts_file"
fi
fi
return 0
}
verify_entry() {
local hosts_file="$1"
local domain="$2"
local result=$(grep "$domain" "$hosts_file" 2>/dev/null)
if [[ -n "$result" ]]; then
print_success "Hosts file updated successfully:"
echo " $result"
return 0
else
print_error "Failed to verify entry in hosts file"
return 1
fi
}
show_usage() {
echo "Usage: $0 [OPTIONS] [IP_ADDRESS] [DOMAIN]"
echo ""
echo "Options:"
echo " -i, --interactive Prompt for IP address input"
echo " -c, --create-env Create sample .env.example file"
echo " -h, --help Show this help message"
echo ""
echo "Examples:"
echo " $0 # Use default/env values"
echo " $0 -i # Interactive mode - prompt for IP"
echo " $0 -c # Create .env.example file"
echo " $0 192.168.1.100 # Update IP only"
echo " $0 192.168.1.100 vm.example.com # Update both IP and domain"
echo " $0 --interactive vm.custom.domain # Interactive IP with custom domain"
echo ""
echo "Configuration:"
echo " Default IP: $DEFAULT_IP"
echo " Default Domain: $DEFAULT_DOMAIN"
echo ""
echo "Environment File (.env):"
echo " VMWARE_IP=192.168.1.100 # Set default IP address"
echo " VMWARE_DOMAIN=vmware.local # Set default domain name"
echo ""
echo "Note: This script ensures no duplicate entries are created in the hosts file."
echo " Configuration from .env file overrides built-in defaults."
}
# === MAIN SCRIPT ===
main() {
local interactive_mode=false
local create_env_mode=false
local args=()
# Parse command line arguments
while [[ $# -gt 0 ]]; do
case $1 in
-h|--help)
show_usage
exit 0
;;
-i|--interactive)
interactive_mode=true
shift
;;
-c|--create-env)
create_env_mode=true
shift
;;
-*)
print_error "Unknown option: $1"
show_usage
exit 1
;;
*)
args+=("$1")
shift
;;
esac
done
# Handle create-env mode
if [[ "$create_env_mode" == true ]]; then
create_sample_env
exit 0
fi
# Load environment configuration
load_env_config
# Initialize configuration after loading .env
IP="$DEFAULT_IP"
DOMAIN="$DEFAULT_DOMAIN"
ENTRY="$IP $DOMAIN"
# Restore positional parameters
set -- "${args[@]}"
print_info "VMware IP Address Updater"
echo "=================================="
# Handle domain argument first (if provided)
if [[ -n "$2" ]]; then
DOMAIN="$2"
elif [[ -n "$1" ]] && [[ "$interactive_mode" == true ]]; then
# If interactive mode and only one argument, treat it as domain
if ! validate_ip "$1"; then
DOMAIN="$1"
set -- # Clear arguments since we used it as domain
fi
fi
# Handle IP address
if [[ "$interactive_mode" == true ]]; then
# Interactive mode - prompt for IP
IP=$(prompt_for_ip "$IP" "$DOMAIN")
elif [[ -n "$1" ]]; then
# Command line argument provided
if validate_ip "$1"; then
IP="$1"
else
print_error "Invalid IP address format: $1"
print_info "Use -i for interactive mode or provide a valid IP address"
exit 1
fi
fi
# Update entry string
ENTRY="$IP $DOMAIN"
echo ""
print_info "Configuration:"
echo " Target: $ENTRY"
# Detect platform
PLATFORM=$(detect_platform)
print_info "Detected platform: $PLATFORM"
# Get platform-specific configuration
get_hosts_config "$PLATFORM"
print_info "Hosts file: $HOSTS_FILE"
# Check if hosts file exists
if [[ ! -f "$HOSTS_FILE" ]]; then
print_error "Hosts file not found: $HOSTS_FILE"
exit 1
fi
# Show current state before modification
echo ""
print_info "Current hosts file entries for '$DOMAIN':"
local current_entries=$(grep -E "[[:space:]]$DOMAIN([[:space:]]|$)" "$HOSTS_FILE" 2>/dev/null || echo " (none found)")
if [[ "$current_entries" != " (none found)" ]]; then
echo "$current_entries" | sed 's/^/ /'
else
echo " (none found)"
fi
# Create backup
backup_hosts_file "$HOSTS_FILE"
echo ""
print_info "Updating hosts file..."
# Update hosts entry
if update_hosts_entry "$HOSTS_FILE" "$DOMAIN" "$ENTRY" "$SUDO"; then
echo ""
# Verify the change
verify_entry "$HOSTS_FILE" "$DOMAIN"
# Check for any remaining duplicates and warn
local final_count=$(grep -c -E "[[:space:]]$DOMAIN([[:space:]]|$)" "$HOSTS_FILE" 2>/dev/null || echo "0")
if [[ $final_count -gt 1 ]]; then
print_warning "Multiple entries still exist for $DOMAIN. Manual cleanup may be required."
elif [[ $final_count -eq 1 ]]; then
print_success "Single entry confirmed - no duplicates detected"
fi
echo ""
# Platform-specific additional notes
case "$PLATFORM" in
"wsl"|"windows")
print_info "Note: Changes will affect both Windows and WSL"
print_info "You may need to flush DNS cache: ipconfig /flushdns"
;;
"macos")
print_info "You may need to flush DNS cache: sudo dscacheutil -flushcache"
;;
"linux")
print_info "You may need to restart network services if using systemd-resolved"
;;
esac
else
print_error "Failed to update hosts file"
exit 1
fi
}
# Run main function with all arguments
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment