mirror of
https://github.com/rcaloras/bash-preexec.git
synced 2026-11-03 14:39:43 -09:00
Fix initialization hook in the PROMPT_COMMAND array on Bash 5.1+
If PROMPT_COMMAND contains unrelated commands before installing bash-preexec, then these unrelated commands may trigger `__bp_preexec_invoke_exec`, which causes interactive mode marker to be unset. This causes preexec to be missed for the first interactive command, since bash-preexec assumes that we are still not interactive. Example of what can trigger such behavior: on Arch Linux in a login shell /etc/profile.d/80-systemd-osc-context.sh causes `__systemd_osc_context_precmdline` to be added to PROMPT_COMMAND, and `__systemd_osc_context_precmdline` trips `__bp_preexec_interactive_mode`. This patch avoids the issue by appending our initialization hook as the last element of PROMPT_COMMAND in Bash >= 5.1, instead of appending the hook to the first element of PROMPT_COMMAND.
This commit is contained in:
parent
fdd5ba732f
commit
5d8ba5dd9c
2 changed files with 83 additions and 14 deletions
|
|
@ -542,14 +542,18 @@ __bp_install_after_session_init() {
|
|||
__bp_require_not_readonly PS0 || return
|
||||
fi
|
||||
|
||||
local sanitized_prompt_command
|
||||
__bp_sanitize_string sanitized_prompt_command "${PROMPT_COMMAND:-}"
|
||||
if [[ -n "$sanitized_prompt_command" ]]; then
|
||||
# shellcheck disable=SC2178 # PROMPT_COMMAND is not an array in bash <= 5.0
|
||||
PROMPT_COMMAND=${sanitized_prompt_command}$'\n'
|
||||
if __bp_use_array_prompt_command; then
|
||||
PROMPT_COMMAND+=("${__bp_install_string}")
|
||||
else
|
||||
local sanitized_prompt_command
|
||||
__bp_sanitize_string sanitized_prompt_command "${PROMPT_COMMAND:-}"
|
||||
if [[ -n "$sanitized_prompt_command" ]]; then
|
||||
# shellcheck disable=SC2178 # PROMPT_COMMAND is not an array in bash <= 5.0
|
||||
PROMPT_COMMAND=${sanitized_prompt_command}$'\n'
|
||||
fi
|
||||
# shellcheck disable=SC2179 # PROMPT_COMMAND is not an array in bash <= 5.0
|
||||
PROMPT_COMMAND+=${__bp_install_string}
|
||||
fi
|
||||
# shellcheck disable=SC2179 # PROMPT_COMMAND is not an array in bash <= 5.0
|
||||
PROMPT_COMMAND+=${__bp_install_string}
|
||||
}
|
||||
|
||||
# Run our install so long as we're not delaying it.
|
||||
|
|
|
|||
|
|
@ -76,13 +76,28 @@ set_exit_code_and_run_precmd() {
|
|||
|
||||
# Assert that before running, the command contains the install string, and
|
||||
# afterwards it does not
|
||||
[[ "$PROMPT_COMMAND" == *"$__bp_install_string"* ]] || return 1
|
||||
[[ "$(join_PROMPT_COMMAND)" == *"$__bp_install_string"* ]] || return 1
|
||||
|
||||
eval_PROMPT_COMMAND
|
||||
|
||||
[[ "$PROMPT_COMMAND" != *"$__bp_install_string"* ]] || return 1
|
||||
[[ "$(join_PROMPT_COMMAND)" != *"$__bp_install_string"* ]] || return 1
|
||||
}
|
||||
|
||||
@test "__bp_install should remove trap logic and itself from modified PROMPT_COMMAND" {
|
||||
PROMPT_COMMAND=()
|
||||
__bp_install_after_session_init
|
||||
PROMPT_COMMAND="$PROMPT_COMMAND; true"
|
||||
|
||||
# Assert that before running, the command contains the install string, and
|
||||
# afterwards it does not
|
||||
[[ "$(join_PROMPT_COMMAND)" == *"$__bp_install_string"* ]] || return 1
|
||||
|
||||
eval_PROMPT_COMMAND
|
||||
|
||||
[[ "$(join_PROMPT_COMMAND)" != *"$__bp_install_string"* ]] || return 1
|
||||
}
|
||||
|
||||
|
||||
@test "__bp_install should preserve an existing DEBUG trap" {
|
||||
trap_invoked_count=0
|
||||
foo() { (( trap_invoked_count += 1 )); }
|
||||
|
|
@ -239,7 +254,9 @@ set_exit_code_and_run_precmd() {
|
|||
@test "Appending or prepending to PROMPT_COMMAND should work after bp_install_after_session_init" {
|
||||
__bp_install_after_session_init
|
||||
nl=$'\n'
|
||||
PROMPT_COMMAND="$PROMPT_COMMAND; true"
|
||||
# On Bash 5.1+ we append to the array properly,
|
||||
# so first element of PROMPT_COMMAND may still be empty.
|
||||
PROMPT_COMMAND="${PROMPT_COMMAND:+$PROMPT_COMMAND; }true"
|
||||
PROMPT_COMMAND="$PROMPT_COMMAND $nl true"
|
||||
PROMPT_COMMAND="$PROMPT_COMMAND; true"
|
||||
PROMPT_COMMAND="true; $PROMPT_COMMAND"
|
||||
|
|
@ -249,6 +266,24 @@ set_exit_code_and_run_precmd() {
|
|||
eval_PROMPT_COMMAND
|
||||
}
|
||||
|
||||
@test "Appending or prepending to PROMPT_COMMAND array should work after bp_install_after_session_init" {
|
||||
if __bp_use_array_prompt_command; then
|
||||
PROMPT_COMMAND=()
|
||||
__bp_install_after_session_init
|
||||
nl=$'\n'
|
||||
PROMPT_COMMAND=("${PROMPT_COMMAND[@]}" "true")
|
||||
PROMPT_COMMAND=("${PROMPT_COMMAND[@]}" "$nl true")
|
||||
PROMPT_COMMAND=("${PROMPT_COMMAND[@]}" "true")
|
||||
PROMPT_COMMAND=("true" "${PROMPT_COMMAND[@]}")
|
||||
PROMPT_COMMAND=("true" "${PROMPT_COMMAND[@]}")
|
||||
PROMPT_COMMAND=("true" "${PROMPT_COMMAND[@]}")
|
||||
PROMPT_COMMAND=("true $nl" "${PROMPT_COMMAND[@]}")
|
||||
eval_PROMPT_COMMAND
|
||||
else
|
||||
skip
|
||||
fi
|
||||
}
|
||||
|
||||
# Case where a user is appending or prepending to PROMPT_COMMAND.
|
||||
# This can happen after 'source bash-preexec.sh' e.g.
|
||||
# source bash-preexec.sh; PROMPT_COMMAND="$PROMPT_COMMAND; other_prompt_command_hook"
|
||||
|
|
@ -268,7 +303,11 @@ set_exit_code_and_run_precmd() {
|
|||
@test "Adding to PROMPT_COMMAND after with semicolon" {
|
||||
PROMPT_COMMAND="echo before"
|
||||
__bp_install_after_session_init
|
||||
PROMPT_COMMAND="$PROMPT_COMMAND; echo after"
|
||||
if __bp_use_array_prompt_command; then
|
||||
PROMPT_COMMAND[${#PROMPT_COMMAND[@]}-1]+='; echo after'
|
||||
else
|
||||
PROMPT_COMMAND+='; echo after'
|
||||
fi
|
||||
|
||||
eval_PROMPT_COMMAND
|
||||
|
||||
|
|
@ -285,12 +324,38 @@ set_exit_code_and_run_precmd() {
|
|||
|
||||
precmd() { echo "inside precmd"; }
|
||||
run eval_PROMPT_COMMAND
|
||||
[ "${#lines[@]}" == '5' ]
|
||||
[ "${lines[0]}" == "after2" ]
|
||||
[ "${lines[1]}" == "before" ]
|
||||
[ "${lines[2]}" == "before2" ]
|
||||
[ "${lines[3]}" == "inside precmd" ]
|
||||
[ "${lines[4]}" == "after" ]
|
||||
[ "${#lines[@]}" == '5' ]
|
||||
if __bp_use_array_prompt_command; then
|
||||
[ "${lines[3]}" == "after" ]
|
||||
[ "${lines[4]}" == "inside precmd" ]
|
||||
else
|
||||
[ "${lines[3]}" == "inside precmd" ]
|
||||
[ "${lines[4]}" == "after" ]
|
||||
fi
|
||||
}
|
||||
|
||||
@test "during install PROMPT_COMMAND and precmd functions should be executed each once (Bash 5.1+ PROMPT_COMMAND array)" {
|
||||
if __bp_use_array_prompt_command; then
|
||||
PROMPT_COMMAND=("echo before")
|
||||
PROMPT_COMMAND=("${PROMPT_COMMAND[@]}" "echo before2")
|
||||
__bp_install_after_session_init
|
||||
PROMPT_COMMAND=("${PROMPT_COMMAND[@]}" "echo after")
|
||||
PROMPT_COMMAND=("echo after2" "${PROMPT_COMMAND[@]}")
|
||||
|
||||
precmd() { echo "inside precmd"; }
|
||||
run eval_PROMPT_COMMAND
|
||||
[ "${#lines[@]}" == '5' ]
|
||||
[ "${lines[0]}" == "after2" ]
|
||||
[ "${lines[1]}" == "before" ]
|
||||
[ "${lines[2]}" == "before2" ]
|
||||
[ "${lines[3]}" == "inside precmd" ]
|
||||
[ "${lines[4]}" == "after" ]
|
||||
else
|
||||
skip
|
||||
fi
|
||||
}
|
||||
|
||||
@test "No functions defined for preexec should simply return" {
|
||||
|
|
|
|||
Loading…
Reference in a new issue