1: 2# SPDX-License-Identifier: BSD-2-Clause 3 4# NAME: 5# debug.sh - selectively debug scripts 6# 7# SYNOPSIS: 8# $_DEBUG_SH . debug.sh 9# DebugOn [-eo] "tag" ... 10# DebugOff [-eo] [rc="rc"] "tag" ... 11# Debugging 12# DebugEcho ... 13# DebugLog ... 14# DebugShell "tag" ... 15# DebugTrace ... 16# Debug "tag" ... 17# 18# $DEBUG_SKIP echo skipped when Debug "tag" is true. 19# $DEBUG_DO echo only done when Debug "tag" is true. 20# 21# DESCRIPTION: 22# debug.sh provides the following functions to facilitate 23# flexible run-time tracing of complicated shell scripts. 24# 25# DebugOn turns tracing on if any "tag" is found in "DEBUG_SH". 26# It turns tracing off if "!tag" is found in "DEBUG_SH". 27# It also sets "DEBUG_ON" to the "tag" that caused tracing to be 28# enabled, or "DEBUG_OFF" if we matched "!tag". 29# If '-e' option given returns 1 if no "tag" matched. 30# If the '-o' flag is given, tracing is turned off unless there 31# was a matched "tag", useful for functions too noisy to tace. 32# 33# DebugOff turns tracing on if any "tag" matches "DEBUG_OFF" or 34# off if any "tag" matches "DEBUG_ON". This allows nested 35# functions to not interfere with each other. 36# 37# DebugOff accepts but ignores the '-e' and '-o' options. 38# The optional "rc" value will be returned rather than the 39# default of 0. Thus if DebugOff is the last operation in a 40# function, "rc" will be the return code of that function. 41# 42# DebugEcho is just shorthand for: 43#.nf 44# $DEBUG_DO echo "$@" 45#.fi 46# 47# Debugging returns true if tracing is enabled. 48# It is useful for bounding complex debug actions, rather than 49# using lots of "DEBUG_DO" lines. 50# 51# DebugShell runs an interactive shell if any "tag" is found in 52# "DEBUG_INTERACTIVE", and there is a tty available. 53# The shell used is defined by "DEBUG_SHELL" or "SHELL" and 54# defaults to '/bin/sh'. 55# 56# Debug calls DebugOn and if that does not turn tracing on, it 57# calls DebugOff to turn it off. 58# 59# The variables "DEBUG_SKIP" and "DEBUG_DO" are set so as to 60# enable/disable code that should be skipped/run when debugging 61# is turned on. "DEBUGGING" is the same as "DEBUG_SKIP" for 62# backwards compatability. 63# 64# The use of $_DEBUG_SH is to prevent multiple inclusion, though 65# it does no harm in this case. 66# 67# BUGS: 68# Does not work with some versions of ksh. 69# If a function turns tracing on, ksh turns it off when the 70# function returns - useless. 71# PD ksh works ok ;-) 72# 73# AUTHOR: 74# Simon J. Gerraty <sjg@crufty.net> 75 76# RCSid: 77# $Id: debug.sh,v 1.35 2024/02/03 19:04:47 sjg Exp $ 78# 79# @(#) Copyright (c) 1994-2024 Simon J. Gerraty 80# 81# This file is provided in the hope that it will 82# be of use. There is absolutely NO WARRANTY. 83# Permission to copy, redistribute or otherwise 84# use this file is hereby granted provided that 85# the above copyright notice and this notice are 86# left intact. 87# 88# Please send copies of changes and bug-fixes to: 89# sjg@crufty.net 90# 91 92_DEBUG_SH=: 93 94Myname=${Myname:-`basename $0 .sh`} 95 96DEBUGGING= 97DEBUG_DO=: 98DEBUG_SKIP= 99export DEBUGGING DEBUG_DO DEBUG_SKIP 100 101_debugOn() { 102 DEBUG_OFF= 103 DEBUG_DO= 104 DEBUG_SKIP=: 105 DEBUG_X=-x 106 set -x 107 DEBUG_ON=$1 108} 109 110_debugOff() { 111 DEBUG_OFF=$1 112 set +x 113 DEBUG_ON=$2 114 DEBUG_DO=: 115 DEBUG_SKIP= 116 DEBUG_X= 117} 118 119DebugEcho() { 120 $DEBUG_DO echo "$@" 121} 122 123Debugging() { 124 test "$DEBUG_SKIP" 125} 126 127DebugLog() { 128 $DEBUG_SKIP return 0 129 echo `date '+@ %s [%Y-%m-%d %H:%M:%S %Z]'` "$@" 130} 131 132# something hard to miss when wading through huge -x output 133DebugTrace() { 134 $DEBUG_SKIP return 0 135 set +x 136 echo "@ ==================== [ $DEBUG_ON ] ====================" 137 DebugLog "$@" 138 echo "@ ==================== [ $DEBUG_ON ] ====================" 139 set -x 140} 141 142# Turn on debugging if appropriate 143DebugOn() { 144 _rc=0 # avoid problems with set -e 145 _off=: 146 while : 147 do 148 case "$1" in 149 -e) _rc=1; shift;; # caller ok with return 1 150 -o) _off=; shift;; # off unless we have a match 151 *) break;; 152 esac 153 done 154 case ",${DEBUG_SH:-$DEBUG}," in 155 ,,) return $_rc;; 156 *,[Dd]ebug,*) ;; 157 *) $DEBUG_DO set +x;; # reduce the noise 158 esac 159 _match= 160 # if debugging is off because of a !e 161 # don't add 'all' to the On list. 162 case "$_off$DEBUG_OFF" in 163 :) _e=all;; 164 *) _e=;; 165 esac 166 for _e in ${*:-$Myname} $_e 167 do 168 : $_e in ,${DEBUG_SH:-$DEBUG}, 169 case ",${DEBUG_SH:-$DEBUG}," in 170 *,!$_e,*|*,!$Myname:$_e,*) 171 # only turn it off if it was on 172 _rc=0 173 $DEBUG_DO _debugOff $_e $DEBUG_ON 174 break 175 ;; 176 *,$_e,*|*,$Myname:$_e,*) 177 # only turn it on if it was off 178 _rc=0 179 _match=$_e 180 $DEBUG_SKIP _debugOn $_e 181 break 182 ;; 183 esac 184 done 185 if test -z "$_off$_match"; then 186 # off unless explicit match, but 187 # only turn it off if it was on 188 $DEBUG_DO _debugOff $_e $DEBUG_ON 189 fi 190 DEBUGGING=$DEBUG_SKIP # backwards compatability 191 $DEBUG_DO set -x # back on if needed 192 $DEBUG_DO set -x # make sure we see it in trace 193 return $_rc 194} 195 196# Only turn debugging off if one of our args was the reason it 197# was turned on. 198# We normally return 0, but caller can pass rc=$? as first arg 199# so that we preserve the status of last statement. 200DebugOff() { 201 case ",${DEBUG_SH:-$DEBUG}," in 202 *,[Dd]ebug,*) ;; 203 *) $DEBUG_DO set +x;; # reduce the noise 204 esac 205 _rc=0 # always happy 206 while : 207 do 208 case "$1" in 209 -[eo]) shift;; # ignore it 210 rc=*) eval "_$1"; shift;; 211 *) break;; 212 esac 213 done 214 for _e in $* 215 do 216 : $_e==$DEBUG_OFF DEBUG_OFF 217 case "$DEBUG_OFF" in 218 "") break;; 219 $_e) _debugOn $DEBUG_ON; return $_rc;; 220 esac 221 done 222 for _e in $* 223 do 224 : $_e==$DEBUG_ON DEBUG_ON 225 case "$DEBUG_ON" in 226 "") break;; 227 $_e) _debugOff; return $_rc;; 228 esac 229 done 230 DEBUGGING=$DEBUG_SKIP # backwards compatability 231 $DEBUG_DO set -x # back on if needed 232 $DEBUG_DO set -x # make sure we see it in trace 233 return $_rc 234} 235 236_TTY=${_TTY:-`test -t 0 && tty`}; export _TTY 237 238# override this if you like 239_debugShell() { 240 { 241 echo DebugShell "$@" 242 echo "Type 'exit' to continue..." 243 } > $_TTY 244 ${DEBUG_SHELL:-${SHELL:-/bin/sh}} < $_TTY > $_TTY 2>&1 245} 246 247# Run an interactive shell if appropriate 248# Note: you can use $DEBUG_SKIP DebugShell ... to skip unless debugOn 249DebugShell() { 250 case "$_TTY%${DEBUG_INTERACTIVE}" in 251 *%|%*) return 0;; # no tty or no spec 252 esac 253 for _e in ${*:-$Myname} all 254 do 255 case ",${DEBUG_INTERACTIVE}," in 256 *,!$_e,*|*,!$Myname:$_e,*) 257 return 0 258 ;; 259 *,$_e,*|*,$Myname:$_e,*) 260 # Provide clues as to why/where 261 _debugShell "$_e: $@" 262 return $? 263 ;; 264 esac 265 done 266 return 0 267} 268 269# For backwards compatability 270Debug() { 271 case "${DEBUG_SH:-$DEBUG}" in 272 "") ;; 273 *) DEBUG_ON=${DEBUG_ON:-_Debug} 274 DebugOn -e $* || DebugOff $DEBUG_LAST 275 DEBUGGING=$DEBUG_SKIP 276 ;; 277 esac 278} 279