#!/bin/bash # chrome-ua - print a realistic Chrome User-Agent string (local install or latest stable) # # Usage: # chrome-ua [-p|--platform mac|win|linux] [-a|--app PATH] # chrome-ua -l|--latest [-p|--platform mac|win|linux] # chrome-ua -h # # Options: # -l, --latest Fetch the latest stable Chrome version from Google's API # -p, --platform mac|win|linux UA shape and (with --latest) API path (default: mac) # -a, --app PATH Path to Chrome.app for local mode # -h, --help Show help message _chrome_ua() ( local SCRIPT_NAME; SCRIPT_NAME="$(basename "${BASH_SOURCE[0]}")" case "${BASH_SOURCE[0]}" in /dev/*|/proc/*) SCRIPT_NAME="" ;; esac case "$SCRIPT_NAME" in ""|bash|sh|zsh|dash) SCRIPT_NAME="chrome-ua" ;; esac _error() { echo "[ERR][$SCRIPT_NAME] $*" >&2; } _warn() { echo "[WRN][$SCRIPT_NAME] $*" >&2; } _show_help() { local s; [ -t 1 ] && s=$'\033[4m' local r; [ -t 1 ] && r=$'\033[24m' cat </dev/null 2>&1 || return 1 command -v jq >/dev/null 2>&1 || return 1 local url="https://versionhistory.googleapis.com/v1/chrome/platforms/$plat/channels/stable/versions?pageSize=1" local body; body="$(curl -sS --max-time 5 "$url" 2>/dev/null)" || return 1 local ver; ver="$(printf '%s' "$body" | jq -r '.versions[0].version // empty' 2>/dev/null)" [ -n "$ver" ] || return 1 printf '%s\n' "${ver%%.*}" } _expand_short_opts() { # $1 = string of short-opt letters that take a value (e.g. "nXHd"); "" for flag-only scripts # $2..$N = "$@" # Populates _EXPANDED; caller does: set -- "${_EXPANDED[@]}"; unset _EXPANDED local value_opts="$1"; shift _EXPANDED=() local passthru="" local arg local rest local c for arg in "$@"; do if [ -n "$passthru" ]; then _EXPANDED+=("$arg"); continue; fi case "$arg" in --) passthru=1; _EXPANDED+=("$arg") ;; --*|-|"") _EXPANDED+=("$arg") ;; -[a-zA-Z]?*) rest="${arg#-}" while [ -n "$rest" ]; do c="${rest%"${rest#?}"}"; rest="${rest#?}" _EXPANDED+=("-$c") case "$value_opts" in *"$c"*) [ -n "$rest" ] && _EXPANDED+=("$rest") rest="" ;; esac done ;; *) _EXPANDED+=("$arg") ;; esac done } local mode="local" local platform="mac" local app="/Applications/Google Chrome.app" _expand_short_opts "pa" "$@" set -- "${_EXPANDED[@]}"; unset _EXPANDED while [ $# -gt 0 ]; do case "$1" in -h|--help) _show_help return 0 ;; -l|--latest) mode="latest" shift ;; -p|--platform) shift if [ $# -eq 0 ] || [ -z "$1" ]; then _error "--platform requires a value (mac|win|linux). Run \`$SCRIPT_NAME -h\` for usage" return 2 fi platform="$1" shift ;; --platform=*) platform="${1#*=}" [ -n "$platform" ] || { _error "--platform requires a value (mac|win|linux). Run \`$SCRIPT_NAME -h\` for usage"; return 2; } shift ;; -a|--app) shift if [ $# -eq 0 ] || [ -z "$1" ]; then _error "--app requires a path. Run \`$SCRIPT_NAME -h\` for usage" return 2 fi app="$1" shift ;; --app=*) app="${1#*=}" [ -n "$app" ] || { _error "--app requires a path. Run \`$SCRIPT_NAME -h\` for usage"; return 2; } shift ;; *) _error "Unknown argument '$1'. Run \`$SCRIPT_NAME -h\` for usage" return 2 ;; esac done case "$platform" in mac|win|linux) ;; *) _error "Invalid --platform '$platform' (valid: mac|win|linux). Run \`$SCRIPT_NAME -h\` for usage" return 2 ;; esac local major="" if [ "$mode" = "latest" ]; then # Explicit --latest: fetch from API; env var does NOT block this if major="$(_fetch_latest_major "$platform")" && [ -n "$major" ]; then _ua_for "$platform" "$major" return 0 fi _warn "Failed to fetch latest Chrome version from Google's API; using pinned major=$FALLBACK_MAJOR" _ua_for "$platform" "$FALLBACK_MAJOR" return 0 fi # Default mode: read local Chrome, fall back to network, then to pinned if [ ! -d "$app" ]; then _warn "Chrome app not found at '$app'" else local plist="$app/Contents/Info.plist" local version; version="$(defaults read "$plist" CFBundleShortVersionString 2>/dev/null)" if [ -n "$version" ]; then major="${version%%.*}" _ua_for "$platform" "$major" return 0 fi _warn "Unable to read Chrome version from '$plist'" fi # Local read failed -- try network fallback unless env var blocks it if [ -z "${CHROME_UA_OFFLINE:-}" ]; then if major="$(_fetch_latest_major "$platform")" && [ -n "$major" ]; then _warn "Local read failed; fetched latest Chrome major=$major from Google's API" _ua_for "$platform" "$major" return 0 fi fi # Network also failed or blocked: pinned fallback _warn "Using pinned fallback major=$FALLBACK_MAJOR (update periodically)" _ua_for "$platform" "$FALLBACK_MAJOR" return 0 ) _chrome_ua "$@" __chrome_ua_rc=$? unset -f _chrome_ua if [ -n "${BASH_SOURCE[0]}" ] && [ "${BASH_SOURCE[0]}" != "$0" ]; then eval "unset __chrome_ua_rc; return $__chrome_ua_rc" fi eval "unset __chrome_ua_rc; exit $__chrome_ua_rc"