@@ -163,6 +163,39 @@ __bp_sanitize_string() {
163163 fi
164164}
165165
166+
167+ # Bash >= 5.1 supports the array version of PROMPT_COMMAND.
168+ __bp_use_array_prompt_command () {
169+ (( BASH_VERSINFO[0 ] > 5 || (BASH_VERSINFO[0 ] == 5 && BASH_VERSINFO[1 ] >= 1 ) ))
170+ }
171+
172+
173+ # Remove $1 and sanitize each elements of PROMPT_COMMAND. We want to keep
174+ # PROMPT_COMMAND scalar in bash < 5.1 because some configuration tests the
175+ # support for the array PROMPT_COMMAND by checking the array attribute of
176+ # PROMPT_COMMAND.
177+ __bp_remove_command_from_prompt_command () {
178+ local removed_command=" ${1-} "
179+ if __bp_use_array_prompt_command; then
180+ local i sanitized_prompt_command
181+ for i in " ${! PROMPT_COMMAND[@]} " ; do
182+ sanitized_prompt_command=" ${PROMPT_COMMAND[i]:- } "
183+ sanitized_prompt_command=" ${sanitized_prompt_command// " $removed_command " /: } "
184+ __bp_sanitize_string sanitized_prompt_command " $sanitized_prompt_command "
185+ if [[ -n " $sanitized_prompt_command " ]]; then
186+ PROMPT_COMMAND[i]=" $sanitized_prompt_command "
187+ else
188+ unset -v ' PROMPT_COMMAND[i]'
189+ fi
190+ done
191+ else
192+ local sanitized_prompt_command=" ${PROMPT_COMMAND:- } "
193+ sanitized_prompt_command=" ${sanitized_prompt_command// " $removed_command " /: } " # no-op
194+ __bp_sanitize_string PROMPT_COMMAND " $sanitized_prompt_command "
195+ fi
196+ }
197+
198+
166199# This function is installed as part of the PROMPT_COMMAND;
167200# It sets a variable to indicate that the prompt was just displayed,
168201# to allow the DEBUG trap to know that the next command is likely interactive.
@@ -187,6 +220,11 @@ __bp_precmd_invoke_cmd() {
187220 if (( __bp_inside_precmd > 0 )) ; then
188221 return
189222 fi
223+
224+ # Check and adjust PROMPT_COMMAND to make sure that PROMPT_COMMAND has the
225+ # form "__bp_precmd_invoke_cmd; ...; __bp_interactive_mode"
226+ __bp_install_prompt_command
227+
190228 local __bp_inside_precmd=1
191229
192230 # Invoke every function defined in our function array.
@@ -354,23 +392,10 @@ __bp_install() {
354392 shopt -s extdebug > /dev/null 2>&1
355393 fi ;
356394
357- local existing_prompt_command
358395 # Remove setting our trap install string and sanitize the existing prompt command string
359- existing_prompt_command=" ${PROMPT_COMMAND:- } "
360- # Edge case of appending to PROMPT_COMMAND
361- existing_prompt_command=" ${existing_prompt_command// $__bp_install_string /: } " # no-op
362- __bp_sanitize_string existing_prompt_command " $existing_prompt_command "
396+ __bp_remove_command_from_prompt_command " $__bp_install_string "
363397
364- # Install our hooks in PROMPT_COMMAND to allow our trap to know when we've
365- # actually entered something.
366- PROMPT_COMMAND=' __bp_precmd_invoke_cmd'
367- PROMPT_COMMAND+=${existing_prompt_command: +$' \n ' $existing_prompt_command }
368- if (( BASH_VERSINFO[0 ] > 5 || (BASH_VERSINFO[0 ] == 5 && BASH_VERSINFO[1 ] >= 1 ) )) ; then
369- PROMPT_COMMAND+=(' __bp_interactive_mode' )
370- else
371- # shellcheck disable=SC2179 # PROMPT_COMMAND is not an array in bash <= 5.0
372- PROMPT_COMMAND+=$' \n __bp_interactive_mode'
373- fi
398+ __bp_install_prompt_command
374399
375400 # Add two functions to our arrays for convenience
376401 # of definition.
@@ -382,6 +407,38 @@ __bp_install() {
382407 __bp_interactive_mode
383408}
384409
410+
411+ # Encloses PROMPT_COMMAND hooks within __bp_precmd_invoke_cmd and
412+ # __bp_interactive_mode.
413+ __bp_install_prompt_command () {
414+ local prompt_command=" ${PROMPT_COMMAND:- } "
415+ if __bp_use_array_prompt_command; then
416+ local IFS=$' \n '
417+ prompt_command=" ${PROMPT_COMMAND[*]:- } "
418+ IFS=$' \t\n '
419+ fi
420+
421+ # Exit if we already have a properly set-up hooks in PROMPT_COMMAND
422+ if [[ " $prompt_command " == __bp_precmd_invoke_cmd$' \n ' * $' \n ' __bp_interactive_mode ]]; then
423+ return 0
424+ fi
425+
426+ __bp_remove_command_from_prompt_command __bp_precmd_invoke_cmd
427+ __bp_remove_command_from_prompt_command __bp_interactive_mode
428+
429+ # Install our hooks in PROMPT_COMMAND to allow our trap to know when we've
430+ # actually entered something.
431+ # shellcheck disable=SC2178 # PROMPT_COMMAND is not an array in bash <= 5.0
432+ PROMPT_COMMAND=' __bp_precmd_invoke_cmd' ${PROMPT_COMMAND: +$' \n ' $PROMPT_COMMAND }
433+ if __bp_use_array_prompt_command; then
434+ PROMPT_COMMAND+=(' __bp_interactive_mode' )
435+ else
436+ # shellcheck disable=SC2179 # PROMPT_COMMAND is not an array in bash <= 5.0
437+ PROMPT_COMMAND+=$' \n __bp_interactive_mode'
438+ fi
439+ }
440+
441+
385442# Sets an installation string as part of our PROMPT_COMMAND to install
386443# after our session has started. This allows bash-preexec to be included
387444# at any point in our bash profile.
0 commit comments