fixed empty arguments being filtered out during word splitting

This commit is contained in:
2026-02-16 19:09:10 -05:00
parent cefd55e7af
commit 6055f3434d
4 changed files with 16 additions and 12 deletions

View File

@@ -94,9 +94,7 @@ impl Expander {
_ => cur_word.push(ch), _ => cur_word.push(ch),
} }
} }
if !cur_word.is_empty() { words.push(cur_word);
words.push(cur_word);
}
words words
} }
} }
@@ -754,7 +752,7 @@ pub fn expand_proc_sub(raw: &str, is_input: bool) -> ShResult<String> {
let mut io_stack = IoStack::new(); let mut io_stack = IoStack::new();
io_stack.push_frame(io_frame); io_stack.push_frame(io_frame);
if let Err(e) = exec_input(raw.to_string(), Some(io_stack)) { if let Err(e) = exec_input(raw.to_string(), Some(io_stack), false) {
eprintln!("{e}"); eprintln!("{e}");
exit(1); exit(1);
} }
@@ -787,7 +785,7 @@ pub fn expand_cmd_sub(raw: &str) -> ShResult<String> {
match unsafe { fork()? } { match unsafe { fork()? } {
ForkResult::Child => { ForkResult::Child => {
io_stack.push_frame(cmd_sub_io_frame); io_stack.push_frame(cmd_sub_io_frame);
if let Err(e) = exec_input(raw.to_string(), Some(io_stack)) { if let Err(e) = exec_input(raw.to_string(), Some(io_stack), false) {
eprintln!("{e}"); eprintln!("{e}");
unsafe { libc::_exit(1) }; unsafe { libc::_exit(1) };
} }

View File

@@ -101,7 +101,7 @@ fn run_script<P: AsRef<Path>>(path: P, args: Vec<String>) -> ShResult<()> {
write_vars(|v| v.cur_scope_mut().bpush_arg(arg)) write_vars(|v| v.cur_scope_mut().bpush_arg(arg))
} }
exec_input(input, None) exec_input(input, None, false)
} }
fn fern_interactive() -> ShResult<()> { fn fern_interactive() -> ShResult<()> {
@@ -187,7 +187,7 @@ fn fern_interactive() -> ShResult<()> {
match readline.process_input() { match readline.process_input() {
Ok(ReadlineEvent::Line(input)) => { Ok(ReadlineEvent::Line(input)) => {
write_meta(|m| m.start_timer()); write_meta(|m| m.start_timer());
if let Err(e) = exec_input(input, None) { if let Err(e) = exec_input(input, None, true) {
match e.kind() { match e.kind() {
ShErrKind::CleanExit(code) => { ShErrKind::CleanExit(code) => {
QUIT_CODE.store(*code, Ordering::SeqCst); QUIT_CODE.store(*code, Ordering::SeqCst);

View File

@@ -116,7 +116,7 @@ impl ExecArgs {
} }
} }
pub fn exec_input(input: String, io_stack: Option<IoStack>) -> ShResult<()> { pub fn exec_input(input: String, io_stack: Option<IoStack>, interactive: bool) -> ShResult<()> {
let log_tab = read_logic(|l| l.clone()); let log_tab = read_logic(|l| l.clone());
let input = expand_aliases(input, HashSet::new(), &log_tab); let input = expand_aliases(input, HashSet::new(), &log_tab);
let mut parser = ParsedSrc::new(Arc::new(input)); let mut parser = ParsedSrc::new(Arc::new(input));
@@ -127,7 +127,7 @@ pub fn exec_input(input: String, io_stack: Option<IoStack>) -> ShResult<()> {
return Ok(()); return Ok(());
} }
let mut dispatcher = Dispatcher::new(parser.extract_nodes()); let mut dispatcher = Dispatcher::new(parser.extract_nodes(), interactive);
if let Some(mut stack) = io_stack { if let Some(mut stack) = io_stack {
dispatcher.io_stack.extend(stack.drain(..)); dispatcher.io_stack.extend(stack.drain(..));
} }
@@ -136,15 +136,17 @@ pub fn exec_input(input: String, io_stack: Option<IoStack>) -> ShResult<()> {
pub struct Dispatcher { pub struct Dispatcher {
nodes: VecDeque<Node>, nodes: VecDeque<Node>,
interactive: bool,
pub io_stack: IoStack, pub io_stack: IoStack,
pub job_stack: JobStack, pub job_stack: JobStack,
} }
impl Dispatcher { impl Dispatcher {
pub fn new(nodes: Vec<Node>) -> Self { pub fn new(nodes: Vec<Node>, interactive: bool) -> Self {
let nodes = VecDeque::from(nodes); let nodes = VecDeque::from(nodes);
Self { Self {
nodes, nodes,
interactive,
io_stack: IoStack::new(), io_stack: IoStack::new(),
job_stack: JobStack::new(), job_stack: JobStack::new(),
} }
@@ -265,7 +267,7 @@ impl Dispatcher {
let subsh_body = subsh.0.to_string(); let subsh_body = subsh.0.to_string();
let _guard = ScopeGuard::shared_scope(); let _guard = ScopeGuard::shared_scope();
exec_input(subsh_body, None)?; exec_input(subsh_body, None, self.interactive)?;
Ok(()) Ok(())
} }
@@ -607,6 +609,10 @@ impl Dispatcher {
self.io_stack.append_to_frame(cmd.redirs); self.io_stack.append_to_frame(cmd.redirs);
let exec_args = ExecArgs::new(argv)?; let exec_args = ExecArgs::new(argv)?;
if self.interactive {
log::info!("expanded argv: {:?}", exec_args.argv.iter().map(|s| s.to_str().unwrap()).collect::<Vec<_>>());
}
let io_frame = self.io_stack.pop_frame(); let io_frame = self.io_stack.pop_frame();
run_fork( run_fork(
io_frame, io_frame,

View File

@@ -802,6 +802,6 @@ pub fn source_file(path: PathBuf) -> ShResult<()> {
let mut buf = String::new(); let mut buf = String::new();
file.read_to_string(&mut buf)?; file.read_to_string(&mut buf)?;
exec_input(buf, None)?; exec_input(buf, None, false)?;
Ok(()) Ok(())
} }