Skip to content

Commit 40a1db9

Browse files
committed
add a useful bash debugger script.
1 parent 2773e5e commit 40a1db9

File tree

3 files changed

+296
-0
lines changed

3 files changed

+296
-0
lines changed

bashrc/bashdebug/bashdb.fns

+237
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
2+
# After each line of the test script is executed the shell traps to
3+
# this function.
4+
5+
function _steptrap
6+
{
7+
_curline=$1 # the number of the line that just ran
8+
9+
(( $_trace )) && _msg "$PS4 line $_curline: ${_lines[$_curline]}"
10+
11+
if (( $_steps >= 0 )); then
12+
let _steps="$_steps - 1"
13+
fi
14+
15+
# first check to see if a line number breakpoint was reached,
16+
# if it was then enter the debugger
17+
if _at_linenumbp ; then
18+
_msg "Reached breakpoint at line $_curline"
19+
_cmdloop
20+
21+
# it wasn't, so check whether a break condition exists and is true
22+
# if it is then enter the debugger
23+
elif [ -n "$_brcond" ] && eval $_brcond; then
24+
_msg "Break condition $_brcond true at line $_curline"
25+
_cmdloop
26+
27+
# it wasn't, so check if we are in step mode and the number of steps is up
28+
# if it is then enter the debugger
29+
elif (( $_steps == 0 )); then
30+
_msg "Stopped at line $_curline"
31+
_cmdloop
32+
fi
33+
}
34+
35+
36+
# The Debugger Command Loop
37+
38+
function _cmdloop {
39+
local cmd args
40+
41+
while read -e -p "bashdb> " cmd args; do
42+
case $cmd in
43+
\? | h ) _menu ;; # print command menu
44+
bc ) _setbc $args ;; # set a break condition
45+
bp ) _setbp $args ;; # set a breakpoint at the given line
46+
cb ) _clearbp $args ;; # clear one or all breakpoints
47+
ds ) _displayscript ;; # list the script and show the breakpoints
48+
g ) return ;; # "go": start/resume execution of the script
49+
q ) exit ;; # quit
50+
s ) let _steps=${args:-1} # single step N times (default = 1)
51+
return ;;
52+
x ) _xtrace ;; # toggle execution trace
53+
\!* ) eval ${cmd#!} $args ;; # pass to the shell
54+
* ) _msg "Invalid command: '$cmd'" ;;
55+
esac
56+
done
57+
}
58+
59+
60+
# See if this line number has a breakpoint
61+
function _at_linenumbp
62+
{
63+
local i=0
64+
65+
# Loop through the breakpoints array and check to see if any of
66+
# them match the current line number. If they do return true (0)
67+
# otherwise return false.
68+
69+
if [ "$_linebp" ]; then
70+
while (( $i < ${#_linebp[@]} )); do
71+
if (( ${_linebp[$i]} == $_curline )); then
72+
return 0
73+
fi
74+
let i=$i+1
75+
done
76+
fi
77+
return 1
78+
}
79+
80+
81+
# Set a breakpoint at the given line number or list breakpoints
82+
function _setbp
83+
{
84+
local i
85+
86+
# If there are no arguments call the breakpoint list function.
87+
# Otherwise check to see if the argument was a positive number.
88+
# If it wasn't then print an error message. If it was then check
89+
# to see if the line number contains text. If it doesn't then
90+
# print an error message. If it does then echo the current
91+
# breakpoints and the new addition and pipe them to "sort" and
92+
# assign the result back to the list of breakpoints. This results
93+
# in keeping the breakpoints in numerical sorted order.
94+
95+
# Note that we can remove duplicate breakpoints here by using
96+
# the -u option to sort which uniquifies the list.
97+
98+
if [ -z $1 ]; then
99+
_listbp
100+
elif [ $(echo $1 | grep '^[0-9]*') ]; then
101+
if [ -n "${_lines[$1]}" ]; then
102+
_linebp=($(echo $( (for i in ${_linebp[*]} $1; do
103+
echo $i; done) | sort -n) ))
104+
_msg "Breakpoint set at line $1"
105+
else
106+
_msg "Breakpoints can only be set on non-blank lines"
107+
fi
108+
else
109+
_msg "Please specify a numeric line number"
110+
fi
111+
}
112+
113+
114+
# List breakpoints and break conditions
115+
function _listbp
116+
{
117+
if [ -n "$_linebp" ]; then
118+
_msg "Breakpoints at lines: ${_linebp[*]}"
119+
else
120+
_msg "No breakpoints have been set"
121+
fi
122+
123+
_msg "Break on condition:"
124+
_msg "$_brcond"
125+
}
126+
127+
128+
# Clear individual or all breakpoints
129+
function _clearbp
130+
{
131+
local i
132+
133+
# If there are no arguments then delete all the breakpoints.
134+
# Otherwise check to see if the argument was a positive number.
135+
# If it wasn't then print an error message. If it was then
136+
# echo all of the current breakpoints except the passed one.
137+
# Then destroy the old array and assign the elements of
138+
# the local array so we effectively recreate it, minus the passed
139+
# breakpoint.
140+
141+
if [ -z $1 ]; then
142+
unset _linebp[*]
143+
_msg "All breakpoints have been cleared"
144+
elif [ $(echo $1 | grep '^[0-9]*') ]; then
145+
_linebp=($(echo $(for i in ${_linebp[*]}; do
146+
if (( $1 != $i )); then echo $i; fi; done) ))
147+
_msg "Breakpoint cleared at line $1"
148+
else
149+
_msg "Please specify a numeric line number"
150+
fi
151+
}
152+
153+
154+
# Set or clear a break condition
155+
function _setbc
156+
{
157+
if [ -n "$*" ]; then
158+
_brcond=$args
159+
_msg "Break when true: $_brcond"
160+
else
161+
_brcond=
162+
_msg "Break condition cleared"
163+
fi
164+
}
165+
166+
167+
# Print out the shell script and mark the location of breakpoints
168+
# and the current line
169+
170+
function _displayscript
171+
{
172+
local i=1 j=0 bp cl
173+
174+
( while (( $i < ${#_lines[@]} )); do
175+
if [ ${_linebp[$j]} ] && (( ${_linebp[$j]} == $i )); then
176+
bp='*'
177+
let j=$j+1
178+
else
179+
bp=' '
180+
fi
181+
if (( $_curline == $i )); then
182+
cl=">"
183+
else
184+
cl=" "
185+
fi
186+
echo "$i:$bp $cl ${_lines[$i]}"
187+
let i=$i+1
188+
done
189+
) | more
190+
}
191+
192+
193+
# Toggle execution trace on/off
194+
function _xtrace
195+
{
196+
let _trace="! $_trace"
197+
_msg "Execution trace \c"
198+
if (( $_trace )); then
199+
_msg "on"
200+
else
201+
_msg "off"
202+
fi
203+
}
204+
205+
206+
# Print the passed arguments to Standard Error
207+
function _msg
208+
{
209+
echo -e "$@" >&2
210+
}
211+
212+
213+
# Print command menu
214+
function _menu {
215+
_msg 'bashdb commands:
216+
bp N set breakpoint at line N
217+
bp list breakpoints and break condition
218+
bc string set break condition to string
219+
bc clear break condition
220+
cb N clear breakpoint at line N
221+
cb clear all breakpoints
222+
ds displays the test script and breakpoints
223+
g start/resume execution
224+
s [N] execute N statements (default 1)
225+
x toggle execution trace on/off
226+
h, ? print this menu
227+
! string passes string to a shell
228+
q quit'
229+
}
230+
231+
232+
233+
# Erase the temporary file before exiting
234+
function _cleanup
235+
{
236+
rm $_debugfile 2>/dev/null
237+
}

bashrc/bashdebug/bashdb.pre

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
2+
# bashdb preamble
3+
# This file gets prepended to the shell script being debugged.
4+
# Arguments:
5+
# $1 = the name of the original guinea pig script
6+
# $2 = the directory where temporary files are stored
7+
# $3 = the directory where bashdb.pre and bashdb.fns are stored
8+
9+
_debugfile=$0
10+
_guineapig=$1
11+
_tmpdir=$2
12+
_libdir=$3
13+
14+
shift 3
15+
16+
source $_libdir/bashdb.fns
17+
_linebp=()
18+
let _trace=0
19+
let _i=1
20+
21+
while read; do
22+
_lines[$_i]=$REPLY
23+
let _i=$_i+1
24+
done < $_guineapig
25+
26+
trap _cleanup EXIT
27+
let _steps=1
28+
LINENO=-2
29+
trap '_steptrap $LINENO' DEBUG
30+
:

bashrc/bashdebug/bashdb.sh

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
2+
# bashdb - a bash debugger
3+
# Driver Script: concatenates the preamble and the target script
4+
# and then executes the new script.
5+
6+
echo 'Bash Debugger version 1.0'
7+
8+
_dbname=${0##*/}
9+
10+
if (( $# < 1 )) ; then
11+
echo "$_dbname: Usage: $_dbname filename" >&2
12+
exit 1
13+
fi
14+
15+
_guineapig=$1
16+
17+
if [ ! -r $1 ]; then
18+
echo "$_dbname: Cannot read file '$_guineapig'." >&2
19+
exit 1
20+
fi
21+
22+
shift
23+
24+
_tmpdir=..
25+
_libdir=.
26+
_debugfile=$_tmpdir/bashdb.$$ # temporary file for script being debugged
27+
cat $_libdir/bashdb.pre $_guineapig > $_debugfile
28+
exec bash $_debugfile $_guineapig $_tmpdir $_libdir "$@"
29+

0 commit comments

Comments
 (0)