diff --git a/src/reader.rs b/src/reader.rs index 4f4dc59b2..6cacf8ca3 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -4204,6 +4204,11 @@ impl<'a> Reader<'a> { // hack to work. reader_write_title(L!(""), zelf.parser, false); + // Reap jobs but do NOT trigger a repaint. + // This is to prevent infinite loops in case a job from the prompt triggers a repaint. + // See #9796. + job_reap(zelf.parser, true); + // Some prompt may have requested an exit (#8033). let exit_current_script = zelf.parser.libdata().exit_current_script; zelf.exit_loop_requested |= exit_current_script; diff --git a/tests/pexpects/prompt_redraw_loop.py b/tests/pexpects/prompt_redraw_loop.py new file mode 100644 index 000000000..45aad38ea --- /dev/null +++ b/tests/pexpects/prompt_redraw_loop.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 +from pexpect_helper import SpawnedProc +import re + +sp = SpawnedProc() +send, sendline, sleep, expect_prompt, expect_re, expect_str = ( + sp.send, + sp.sendline, + sp.sleep, + sp.expect_prompt, + sp.expect_re, + sp.expect_str, +) +expect_prompt() + +# Regression test for 9796 +# If a process in the prompt exits with a status that would cause a message to be printed, +# don't trigger another prompt execution which would result in an infinite loop. +sendline("sh -c 'kill -ABRT $$'") +expect_prompt(re.escape("fish: Job 1, 'sh -c 'kill -ABRT $$'' terminated by signal SIGABRT (Abort)")) + +# Copy current prompt so we can reuse it. +sendline("functions --copy fish_prompt fish_prompt_orig") +expect_prompt() + +sendline("function fish_prompt; sh -c 'kill -ABRT $$'; fish_prompt_orig; end") +expect_prompt(re.escape("fish: Job 1, 'sh -c 'kill -ABRT $$'' terminated by signal SIGABRT (Abort)")) + +sendline("echo still alive!") +expect_prompt("still alive!")