Logging bash scripts with verbose and xxtrace (set -vx) and configuring PS4
I am trying to tweak PS4 for better xxtrace output when I also have a verbose set for verbose mode. Some of the things I would like to do is wait, but I want to see any -x or -v output for these operations because they will print for every line.
I also wanted to get rid of the depth gauge / depth gauge (+) because I want the tracing result to be aligned.
My initial idea:
BACKSPACES=$'\b\b\b\b\b\b\b\b'
PS4='+`printf "%s[\t] %-16.16s:%03d (%-16.16s) $ " ${BACKSPACES:0:$BASH_SUBSHELL} $(basename $BASH_SOURCE) $LINENO ${FUNCNAME[0]:+${FUNCNAME[0]}}`'
The first problem I ran into was that $ BASH_SUBSHELL doesn't always match the + number that will be printed. Searching $ SHLVL didn't help either.
echo $X
+ 0 2 $ echo x <-- toplevel prints one + and $BASH_SUBSHELL is 0, as expected
x
( echo subshell )
+ 1 2 $ echo subshell <-- $BASH_SUBSHELL increments to 1 as expected, but why only one + instead of two ++?
source ./test2.sh
+ 0 2 $ source ./test2.sh
echo $X$X$X$X
++ 0 2 $ echo xxxx <-- ??? is $BASH_SUBSHELL relative to the current file or something but the + indicators are not???
xxxx
subshell
I think I got around this by forgetting about using $ BASH_SUBSHELL and making the first character in PS4 obscene instead, but I would still like to know why $ BASH_SUBSHELL is not what I expect.
To work around the issue with PS4-generated subshells, I looked at the PS1 equivalent PROMPT_COMMAND but couldn't find anything other than some pointers on how to implement it myself.
The best way I thought I found was to grab the DEBUG signal.
trap 'debugfun $BASH_SOURCE' DEBUG
Of course, the -vx options also apply to debugfun calls. My solution to suppress this output was to wrap the function steps in the + vx set and set the -vx.
debugfun() {
set +vx
VAR=`basename $1 .sh`
set -vx
}
Now ... I had to deal with a question about every print call:
debugfun $BASH_SOURCE <-- from -v
++ debugfun ./test.sh <-- from -x
++ set +vx <-- from -x
I'm not sure why -v doesn't print on the "set + vx" line. I thought the -T option could do it, but it doesn't.
Anyway, I thought the output would always be consistent, so all I had to do was erase those 3 lines from debugfun. My ugly solution was to add this line after "set + vx":
printf "\b\r\033[K\b\r\033[K\b\r\033[K" # clear previous 3 lines
It worked!
... Except when I connect multiple times.
I'll remove the printf line to show why:
echo $X$X | sed 's/x/y/' # pipe once... debugfun is called every time before each pipe component
debugfun $BASH_SOURCE
++ debugfun ./test.sh
++ set +vx
debugfun $BASH_SOURCE
++ debugfun ./test.sh
++ set +vx
+ echo xx
+ sed s/x/y/
echo $X$X$X | sed 's/x/y/' | sed 's/y/x/' # pipe twice
debugfun $BASH_SOURCE
++ debugfun ./test.sh
++ set +vx
debugfun $BASH_SOURCE
++ debugfun ./test.sh
++ set +vx
+ echo xxx
+ sed s/x/y/ # because this line is here, clearing 3 previous lines from the next "set +vx" would clear it
debugfun $BASH_SOURCE
++ debugfun ./test.sh
++ set +vx
+ sed s/y/x/
xxx
I finally came up with this solution, but I think it is really ugly and, although close, not quite what I want:
SPACES=" "
PS4='^@\[\e[31m\]+ [\t] \
${SPACES:0:$((${#BASH_SOURCE} > 24 ? 0 : 24 - ${#BASH_SOURCE}))}${BASH_SOURCE:$((${#BASH_SOURCE} < 24 ? 0 : -24))}:\
${SPACES:0:$((3-${#LINENO}))}$LINENO \
${FUNCNAME:-${SPACES:0:20}}${FUNCNAME:+${SPACES:0:$((20 - ${#FUNCNAME}))}} \
$ \[\e[0m\]'
The main thing I don't like is that I can't nest bash string manipulations to not only undo the truncation of $ BASH_SOURCE, but also get its basename without a subshell (i.e. concatenate with echo $ {BASH_SOURCE ## * /} )
I have no ideas and I will be grateful and clarified as well as advice on doing what I am trying to do.
GNU bash, version 4.1.2(1)-release (x86_64-redhat-linux-gnu)
source to share
I suggest ditching PS4 and making all trace output in DEBUG trap. Most, if not all of these -x fingerprints are available elsewhere. See BASH_SOURCE, BASH_LINENO, FUNCNAME, and BASH_COMMAND.
Like this:
function trace()
{
echo "TRACE" \
"${BASH_SOURCE[1]}:${BASH_LINENO[0]}:${FUNCNAME[1]}:" \
"$BASH_COMMAND"
}
set -o functrace
shopt -s extdebug
trap trace DEBUG
source to share