#!/usr/bin/env bash # check-redirects.sh: test a Skewcheck redirect map against the live site. # Usage: NEW_SITE=https://www.newsite.com ./check-redirects.sh redirect-map.csv > results.tsv # # Reads the columns named old_url, new_path and action (positions 2, 4 and 5 # on the redirect map template). One URL at a time: a GET for the first # status (15s cap), then a GET that follows up to 10 redirects (30s cap). set -u usage="usage: NEW_SITE=https://www.newsite.com $0 redirect-map.csv" map="${1:?$usage}" new_site="${NEW_SITE:?set NEW_SITE to the live site, e.g. https://www.newsite.com}" new_site="${new_site%/}" if [ ! -f "$map" ]; then printf 'error: cannot read %s\n' "$map" >&2 exit 1 fi # nth comma-separated field, 1-based. Quoted commas are not supported. field() { n=$1 rest=$2 i=1 while [ "$i" -lt "$n" ]; do case "$rest" in *,*) rest="${rest#*,}" ;; *) rest="" ;; esac i=$((i + 1)) done printf '%s' "${rest%%,*}" } trim() { s=$1 s="${s#"${s%%[![:space:]]*}"}" s="${s%"${s##*[![:space:]]}"}" printf '%s' "$s" } old_i=2 new_i=4 action_i=5 first_row=1 printf 'old_url\texpected\tfirst_status\thops\tfinal_status\tfinal_url\tresult\n' while IFS= read -r line || [ -n "${line:-}" ]; do line="${line%$'\r'}" [ -z "$line" ] && continue if [ "$first_row" -eq 1 ]; then first_row=0 case "$line" in *old_url*) i=1 rest=$line while [ -n "$rest" ]; do name=$(trim "${rest%%,*}") case "$name" in old_url) old_i=$i ;; new_path) new_i=$i ;; action) action_i=$i ;; esac case "$rest" in *,*) rest="${rest#*,}" ;; *) break ;; esac i=$((i + 1)) done continue ;; esac fi old=$(trim "$(field "$old_i" "$line")") new_path=$(trim "$(field "$new_i" "$line")") action=$(trim "$(field "$action_i" "$line")") [ -z "$old" ] && continue case "$old" in http://*|https://*) ;; *) # A leading = + - or @ is a spreadsheet formula. A quote keeps the cell as text. shown=$old case "$shown" in [-+=@]*) shown="'${shown}" ;; esac printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\n' \ "$shown" "(skipped: not an http(s) URL)" "" "" "" "" "CHECK" continue ;; esac case "$action" in redirect|keep|retire-404|retire-410) ;; *) printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\n' \ "$old" "(action: ${action})" "" "" "" "" "CHECK" continue ;; esac # || true: curl exits non-zero on a dead host. This file only sets -u, but # one failure must not stop the rest of the file if the caller has -e on. # -g: a URL with [ ] or { } must be one request, not a curl glob. first=$(curl -g -sS -o /dev/null --proto '=http,https' --proto-redir '=http,https' --max-time 15 -w '%{http_code}' -- "$old") || true hops="" final_status="" final_url="" meta=$(curl -g -sS -o /dev/null --proto '=http,https' --proto-redir '=http,https' -L --max-redirs 10 --max-time 30 \ -w '%{num_redirects}\t%{http_code}\t%{url_effective}' -- "$old") || true IFS=$'\t' read -r hops final_status final_url <