From 2b1f07a1143e38edb77e0c8f54d9b24c723d1fcd Mon Sep 17 00:00:00 2001 From: andrewflbarnes Date: Tue, 2 Dec 2025 19:45:16 +0000 Subject: [PATCH] fix: linux shell inference when spaces or trailing slash in command --- .changeset/fine-frogs-tease.md | 5 +++++ src/shell/infer/unix.rs | 33 +++++++++++++++++++++------------ src/shell/infer/windows.rs | 7 ++++--- 3 files changed, 30 insertions(+), 15 deletions(-) create mode 100644 .changeset/fine-frogs-tease.md diff --git a/.changeset/fine-frogs-tease.md b/.changeset/fine-frogs-tease.md new file mode 100644 index 000000000..c71d4d2eb --- /dev/null +++ b/.changeset/fine-frogs-tease.md @@ -0,0 +1,5 @@ +--- +"fnm": patch +--- + +fix: correct shell inference when path has spaces or trailing slash diff --git a/src/shell/infer/unix.rs b/src/shell/infer/unix.rs index 76a677547..9db3ed0e7 100644 --- a/src/shell/infer/unix.rs +++ b/src/shell/infer/unix.rs @@ -8,6 +8,7 @@ use thiserror::Error; #[derive(Debug)] struct ProcessInfo { parent_pid: Option, + parent_pid_str: String, command: String, } @@ -28,13 +29,24 @@ pub fn infer_shell() -> Option> { err }) .ok()?; - let binary = process_info - .command - .trim_start_matches('-') - .split('/') - .next_back()?; + + debug!( + "pid {current_pid} parent process {} : {}", + process_info.parent_pid_str, process_info.command + ); + + let mut parts = process_info.command.trim_start_matches('-').split('/'); + + let mut binary = ""; + while let Some(b) = parts.next_back() { + if !b.is_empty() { + binary = b; + break; + } + } if let Some(shell) = super::shell_from_string(binary) { + debug!("Found supported shell: {:?}", shell); return Some(shell); } @@ -69,18 +81,15 @@ fn get_process_info(pid: u32) -> Result { .next() .ok_or_else(|| Error::from(ErrorKind::NotFound))??; - let mut parts = line.split_whitespace(); - let ppid = parts.next().ok_or_else(|| ProcessInfoError::Parse { - expectation: "Can't read the ppid from ps, should be the first item in the table", - got: line.to_string(), - })?; - let command = parts.next().ok_or_else(|| ProcessInfoError::Parse { - expectation: "Can't read the command from ps, should be the second item in the table", + let (ppid, command_raw) = line.trim().split_once(char::is_whitespace).ok_or_else(|| ProcessInfoError::Parse { + expectation: "Can't split the ppid and program from ps, should be first and second items in the table", got: line.to_string(), })?; + let command = command_raw.trim_start(); Ok(ProcessInfo { parent_pid: ppid.parse().ok(), + parent_pid_str: ppid.into(), command: command.into(), }) } diff --git a/src/shell/infer/windows.rs b/src/shell/infer/windows.rs index 0abda183b..3bd310645 100644 --- a/src/shell/infer/windows.rs +++ b/src/shell/infer/windows.rs @@ -14,9 +14,9 @@ pub fn infer_shell() -> Option> { while let Some(pid) = current_pid { if let Some(process) = system.process(pid) { current_pid = process.parent(); - debug!("pid {pid} parent process is {current_pid:?}"); - let process_name = process - .exe() + let exe = process.exe(); + debug!("pid {pid} parent process is {current_pid:?}: {exe:?}"); + let process_name = exe .and_then(|x| { tap_none(x.file_stem(), || { warn!("failed to get file stem from {:?}", x); @@ -33,6 +33,7 @@ pub fn infer_shell() -> Option> { .map(|x| &x[..]) .and_then(super::shell_from_string) { + debug!("Found supported shell: {:?}", shell); return Some(shell); } } else {