reader: do not send queries if stdout is not a terminal

Interactive fish with output redirected ("fish >/dev/null")
is not a common use case but it is valid.

Perhaps surprisingly, "fish >some-file" *does* print terminal escape codes
(colors, cursor movement etc.) even if terminal output is not a TTY.
This is typically harmless, and potentially useful for debugging.

We also send blocking terminal queries but those are not harmless in this case.
Since no terminal will receive the queries, we hang; indefinitely as of today,
but we should give up after a timeout and print an error.  Either way that
seems needlessly surprising. Suppress queries in this case.

In future, we should probably do something similar if stdin is not a terminal;
though in that case we're even less likely to be interactive (only "-i"
I think).
This commit is contained in:
Johannes Altmanninger
2025-05-28 12:02:18 +02:00
parent c7a19a00ab
commit 3951a858dd
2 changed files with 28 additions and 1 deletions

View File

@@ -243,7 +243,11 @@ pub(crate) fn initial_query(
vars: Option<&dyn Environment>,
) {
blocking_query.get_or_init(|| {
let query = if is_dumb() || IN_MIDNIGHT_COMMANDER.load() || IN_DVTM.load() {
let query = if is_dumb()
|| IN_MIDNIGHT_COMMANDER.load()
|| IN_DVTM.load()
|| !isatty(STDOUT_FILENO)
{
None
} else {
// Query for kitty keyboard protocol support.
@@ -1525,6 +1529,9 @@ pub(crate) fn blocking_query(&self) -> RefMut<'_, Option<TerminalQuery>> {
}
pub fn request_cursor_position(&mut self, out: &mut Outputter, q: CursorPositionQuery) {
if !isatty(STDOUT_FILENO) {
return;
}
let mut query = self.blocking_query();
assert!(query.is_none());
*query = Some(TerminalQuery::CursorPositionReport(q));

View File

@@ -0,0 +1,20 @@
#RUN: %fish %s
#REQUIRES: command -v tmux
set -g isolated_tmux_fish_extra_args -C '
set -g fish (status fish-path)
'
isolated-tmux-start
# Implicit interactive but output is redirected.
isolated-tmux send-keys \
'$fish >output' Enter
tmux-sleep
isolated-tmux send-keys \
'status is-interactive && printf %s i n t e r a c t i v e \n' Enter \
C-d
tmux-sleep
# Extract the line where command output starts.
string match <output -r '.*\e\]133;C.*' |
string escape
# CHECK: {{.*}}interactive{{$}}