diff --git a/codex-rs/core/src/shell.rs b/codex-rs/core/src/shell.rs index 46365123..98addffc 100644 --- a/codex-rs/core/src/shell.rs +++ b/codex-rs/core/src/shell.rs @@ -20,8 +20,13 @@ impl Shell { return None; } - let mut result = vec![zsh.shell_path.clone(), "-c".to_string()]; - if let Ok(joined) = shlex::try_join(command.iter().map(|s| s.as_str())) { + let mut result = vec![zsh.shell_path.clone()]; + result.push("-lc".to_string()); + + let joined = strip_bash_lc(&command) + .or_else(|| shlex::try_join(command.iter().map(|s| s.as_str())).ok()); + + if let Some(joined) = joined { result.push(format!("source {} && ({joined})", zsh.zshrc_path)); } else { return None; @@ -33,6 +38,19 @@ impl Shell { } } +fn strip_bash_lc(command: &Vec) -> Option { + match command.as_slice() { + // exactly three items + [first, second, third] + // first two must be "bash", "-lc" + if first == "bash" && second == "-lc" => + { + Some(third.clone()) + } + _ => None, + } +} + #[cfg(target_os = "macos")] pub async fn default_user_shell() -> Shell { use tokio::process::Command; @@ -119,15 +137,29 @@ mod tests { let cases = vec![ ( vec!["myecho"], - vec![shell_path, "-c", "source ZSHRC_PATH && (myecho)"], + vec![shell_path, "-lc", "source ZSHRC_PATH && (myecho)"], Some("It works!\n"), ), + ( + vec!["myecho"], + vec![shell_path, "-lc", "source ZSHRC_PATH && (myecho)"], + Some("It works!\n"), + ), + ( + vec!["bash", "-c", "echo 'single' \"double\""], + vec![ + shell_path, + "-lc", + "source ZSHRC_PATH && (bash -c \"echo 'single' \\\"double\\\"\")", + ], + Some("single double\n"), + ), ( vec!["bash", "-lc", "echo 'single' \"double\""], vec![ shell_path, - "-c", - "source ZSHRC_PATH && (bash -lc \"echo 'single' \\\"double\\\"\")", + "-lc", + "source ZSHRC_PATH && (echo 'single' \"double\")", ], Some("single double\n"), ),