#!/bin/sh # weather - query current weather and forecasts for any location via wttr.in. # # wttr.in (https://github.com/chubin/wttr.in) is a console-oriented weather # service backed by World Weather Online data. It supports several output # formats: a graphical ANSI view for terminals (the default), one-line text # formats (built-in presets 1-4 or a custom %-notation string), a rich JSON # document (?format=j1 / j2) for scripts and APIs, plus PNG / HTML / # Prometheus metrics. # # This script is a thin, friendly wrapper around the wttr.in HTTP API. It # builds the request URL from the supplied location and options, fetches the # data with curl and (when JSON is requested) pretty-prints it with jq. # # Usage: # weather [options] [location] # # Options: # -l, --location LOC Location: city, airport code, domain, IP, # "lat,lon" coordinates, or "~Name" for a custom label. # Default: auto-detect from the request IP. # -u, --units U Unit system: m (metric, default), u (USCS/imperial), # M (metric with wind speed in m/s). # -L, --lang LANG Output language code, e.g. de, fr, ru, zh-cn. # -f, --format FMT Output format: # j1 -> rich JSON document (default for --json) # j2 -> JSON document, imperial units # 1-4 -> built-in one-line presets # "%..." -> custom one-line %-notation format # Omit to get the default graphical terminal view. # -j, --json Alias for --format j1 (emit JSON). # -0 Current weather only. # -1 Current weather + today's forecast. # -2 Current weather + today's + tomorrow's forecast. # -q, --quiet Quiet: no "Weather report" header / city name. # -A Force ANSI output (ignore User-Agent detection). # -h, --help Show this help text. # # Environment: # WTTRAPI Base endpoint. Default: https://wttr.in # WEATHER_TIMEOUT curl connect/read timeout in seconds. Default: 20 # # Dependencies: curl, jq (jq only needed for the JSON format). # # Examples: # weather # weather London # weather -u u -L de Berlin # weather -f 3 "New York" # weather -f "%l: %c %t (feels %f), wind %w %m" Paris # weather -j Tokyo set -eu WTTRAPI="${WTTRAPI:-https://wttr.in}" TIMEOUT="${WEATHER_TIMEOUT:-20}" # Single-letter options (view/quiet/units) concatenated into one run. SHORT="" LANG_CODE="" FORMAT="" LOCATION="" usage() { sed -n '/^# Usage:/,/^# Examples:/p' "$0" | sed 's/^# \{0,1\}//' } # --- Parse arguments. --- while [ "$#" -gt 0 ]; do case "$1" in -h|--help) usage exit 0 ;; -l|--location) [ "$#" -ge 2 ] || { echo "weather: $1 requires an argument" >&2; exit 1; } LOCATION="$2" shift 2 ;; -u|--units) [ "$#" -ge 2 ] || { echo "weather: $1 requires an argument" >&2; exit 1; } case "$2" in m|u|M) SHORT="${SHORT}$2" ;; *) echo "weather: unknown unit '$2' (use m, u or M)" >&2; exit 1 ;; esac shift 2 ;; -L|--lang) [ "$#" -ge 2 ] || { echo "weather: $1 requires an argument" >&2; exit 1; } LANG_CODE="$2" shift 2 ;; -f|--format) [ "$#" -ge 2 ] || { echo "weather: $1 requires an argument" >&2; exit 1; } FORMAT="$2" shift 2 ;; -j|--json) FORMAT="j1" shift ;; -0|-1|-2|-q|-A) SHORT="${SHORT}${1#-}" shift ;; --) shift LOCATION="${LOCATION:+$LOCATION }$(printf '%s' "$*" | sed 's/^ //')" break ;; -*) echo "weather: unknown option '$1' (try --help)" >&2 exit 1 ;; *) # Positional: append to location (preserves multi-word locations). LOCATION="${LOCATION:+$LOCATION }$1" shift ;; esac done # --- Assemble the query string. --- # wttr.in accepts a leading run of single-letter options, then '&'-joined # long options (key=value). We build both parts and join them. QUERY="$SHORT" # Long options: format and lang. LONG="" if [ -n "$FORMAT" ]; then case "$FORMAT" in j1|j2) LONG="${LONG:+$LONG&}format=${FORMAT}" ;; [1-4]) LONG="${LONG:+$LONG&}format=${FORMAT}" ;; *) # Custom %-notation one-line format -> URL-encode it. ENC=$(printf '%s' "$FORMAT" | jq -sRr @uri) LONG="${LONG:+$LONG&}format=${ENC}" ;; esac fi if [ -n "$LANG_CODE" ]; then LONG="${LONG:+$LONG&}lang=$(printf '%s' "$LANG_CODE" | jq -sRr @uri)" fi # Combine short + long parts into a single query string. if [ -n "$QUERY" ] && [ -n "$LONG" ]; then QUERY="${QUERY}&${LONG}" elif [ -z "$QUERY" ] && [ -n "$LONG" ]; then QUERY="$LONG" fi # URL-encode the location (spaces -> %20, etc.). Empty means auto-detect. if [ -n "$LOCATION" ]; then LOC_ENC=$(printf '%s' "$LOCATION" | jq -sRr @uri) else LOC_ENC="" fi URL="${WTTRAPI}/${LOC_ENC}" if [ -n "$QUERY" ]; then URL="${URL}?${QUERY}" fi # --- Fetch. --- # wttr.in returns the graphical ANSI art whenever the request looks like it # comes from a console client (curl's default User-Agent), and HTML for # browsers. So we keep curl's default identity. The "-A" flag above is a # wttr.in URL option ("force ANSI") and is already part of the query string. RESP=$(curl -s -L --max-time "$TIMEOUT" "$URL") || { echo "weather: failed to reach $WTTRAPI" >&2 exit 1 } # --- Emit. --- if [ "$FORMAT" = "j1" ] || [ "$FORMAT" = "j2" ]; then if command -v jq >/dev/null 2>&1; then printf '%s\n' "$RESP" | jq . 2>/dev/null || printf '%s\n' "$RESP" else printf '%s\n' "$RESP" fi else printf '%s\n' "$RESP" fi exit 0