@@ -71,7 +71,11 @@ __bp_inside_precmd=0
7171__bp_inside_preexec=0
7272
7373# Initial PROMPT_COMMAND string that is removed from PROMPT_COMMAND post __bp_install
74- __bp_install_string=$' __bp_trap_string="$(trap -p DEBUG)"\n trap - DEBUG\n __bp_install'
74+ bash_preexec_install_string=$' __bp_trap_string="$(trap -p DEBUG)"\n trap - DEBUG\n __bp_install'
75+
76+ # The command string that is registered to the DEBUG trap.
77+ # shellcheck disable=SC2016
78+ bash_preexec_trapdebug_string=' __bp_preexec_invoke_exec "$_"'
7579
7680# Fails if any of the given variables are readonly
7781# Reference https://stackoverflow.com/a/4441178
@@ -157,7 +161,7 @@ __bp_precmd_invoke_cmd() {
157161 return
158162 fi
159163 local __bp_inside_precmd=1
160- __bp_invoke_precmd_functions " $__bp_last_ret_value " " $__bp_last_argument_prev_command "
164+ bash_preexec_invoke_precmd_functions " $__bp_last_ret_value " " $__bp_last_argument_prev_command "
161165
162166 __bp_set_ret_value " $__bp_last_ret_value " " $__bp_last_argument_prev_command "
163167}
@@ -167,7 +171,7 @@ __bp_precmd_invoke_cmd() {
167171# $_, respectively, which will be set for each precmd function. This function
168172# returns the last non-zero exit status of the hook functions. If there is no
169173# error, this function returns 0.
170- __bp_invoke_precmd_functions () {
174+ bash_preexec_invoke_precmd_functions () {
171175 local lastexit=$1 lastarg=$2
172176 # Invoke every function defined in our function array.
173177 local precmd_function
@@ -275,7 +279,7 @@ __bp_preexec_invoke_exec() {
275279 return
276280 fi
277281
278- __bp_invoke_preexec_functions " ${__bp_last_ret_value:- } " " $__bp_last_argument_prev_command " " $this_command "
282+ bash_preexec_invoke_preexec_functions " ${__bp_last_ret_value:- } " " $__bp_last_argument_prev_command " " $this_command "
279283 local preexec_ret_value=$?
280284
281285 # Restore the last argument of the last executed command, and set the return
@@ -294,7 +298,7 @@ __bp_preexec_invoke_exec() {
294298# (corresponding to BASH_COMMAND in the DEBUG trap). This function returns the
295299# last non-zero exit status from the preexec functions. If there is no error,
296300# this function returns `0`.
297- __bp_invoke_preexec_functions () {
301+ bash_preexec_invoke_preexec_functions () {
298302 local lastexit=$1 lastarg=$2 this_command=$3
299303 local preexec_function
300304 local preexec_function_ret_value
@@ -322,7 +326,8 @@ __bp_install() {
322326 return 1
323327 fi
324328
325- trap ' __bp_preexec_invoke_exec "$_"' DEBUG
329+ # shellcheck disable=SC2064
330+ trap " $bash_preexec_trapdebug_string " DEBUG
326331
327332 # Preserve any prior DEBUG trap as a preexec function
328333 eval " local trap_argv=(${__bp_trap_string:- } )"
@@ -353,7 +358,7 @@ __bp_install() {
353358 # Remove setting our trap install string and sanitize the existing prompt command string
354359 existing_prompt_command=" ${PROMPT_COMMAND:- } "
355360 # Edge case of appending to PROMPT_COMMAND
356- existing_prompt_command=" ${existing_prompt_command// $__bp_install_string /: } " # no-op
361+ existing_prompt_command=" ${existing_prompt_command// $bash_preexec_install_string /: } " # no-op
357362 existing_prompt_command=" ${existing_prompt_command// $' \n ' : $' \n ' / $' \n ' } " # remove known-token only
358363 existing_prompt_command=" ${existing_prompt_command// $' \n ' : ;/ $' \n ' } " # remove known-token only
359364 __bp_sanitize_string existing_prompt_command " $existing_prompt_command "
@@ -372,10 +377,13 @@ __bp_install() {
372377 PROMPT_COMMAND+=$' \n __bp_interactive_mode'
373378 fi
374379
375- # Add two functions to our arrays for convenience
376- # of definition.
377- precmd_functions+=(precmd)
378- preexec_functions+=(preexec)
380+ # Add two functions to our arrays for convenience of definition only when
381+ # the functions have not yet added.
382+ if [[ ! ${__bp_installed_convenience_functions-} ]]; then
383+ __bp_installed_convenience_functions=1
384+ precmd_functions+=(precmd)
385+ preexec_functions+=(preexec)
386+ fi
379387
380388 # Invoke our two functions manually that were added to $PROMPT_COMMAND
381389 __bp_precmd_invoke_cmd
@@ -397,8 +405,46 @@ __bp_install_after_session_init() {
397405 PROMPT_COMMAND=${sanitized_prompt_command} $' \n '
398406 fi
399407 # shellcheck disable=SC2179 # PROMPT_COMMAND is not an array in bash <= 5.0
400- PROMPT_COMMAND+=${__bp_install_string}
408+ PROMPT_COMMAND+=${bash_preexec_install_string}
409+ }
410+
411+ # Remove hooks installed in the DEBUG trap and PROMPT_COMMAND.
412+ bash_preexec_uninstall () {
413+ # Remove __bp_install hook from PROMPT_COMMAND
414+ # shellcheck disable=SC2178 # PROMPT_COMMAND is not an array in bash <= 5.0
415+ if [[ ${PROMPT_COMMAND-} == * " $bash_preexec_install_string " * ]]; then
416+ PROMPT_COMMAND=" ${PROMPT_COMMAND// ${bash_preexec_install_string} [;$'\n']} " # Edge case of appending to PROMPT_COMMAND
417+ PROMPT_COMMAND=" ${PROMPT_COMMAND// $bash_preexec_install_string } "
418+ fi
419+
420+ # Remove precmd hook from PROMPT_COMMAND
421+ local i prompt_command
422+ for i in " ${! PROMPT_COMMAND[@]} " ; do
423+ prompt_command=${PROMPT_COMMAND[i]}
424+ case $prompt_command in
425+ __bp_precmd_invoke_cmd | __bp_interactive_mode)
426+ prompt_command= ;;
427+ * )
428+ prompt_command=${prompt_command/# $' __bp_precmd_invoke_cmd\n ' / $' \n ' }
429+ prompt_command=${prompt_command% $' \n __bp_interactive_mode' }
430+ prompt_command=${prompt_command# $' \n ' }
431+ esac
432+ PROMPT_COMMAND[i]=$prompt_command
433+ done
434+
435+ # Remove preexec hook in the DEBUG trap
436+ local q=" '" Q=" '\''"
437+ if [[ $( trap -p DEBUG) == " trap -- '${bash_preexec_trapdebug_string// $q / $Q } ' DEBUG" ]]; then
438+ if [[ ${__bp_trap_string-} ]]; then
439+ eval -- " $__bp_trap_string "
440+ else
441+ trap - DEBUG
442+ fi
443+ fi
401444}
445+ # Note: We need to add "trace" attribute to the function so that "trap - DEBUG"
446+ # inside the function takes an effect.
447+ declare -ft bash_preexec_uninstall
402448
403449# Run our install so long as we're not delaying it.
404450if [[ -z " ${__bp_delay_install:- } " ]]; then
0 commit comments