1 use std::{ 2 collections::HashMap, 3 io::ErrorKind, 4 os::fd::{AsRawFd, FromRawFd}, 5 process::{Child, ChildStdout, Stdio}, 6 sync::{Arc, Mutex}, 7 }; 8 9 use regex::Regex; 10 11 #[derive(Debug)] 12 pub enum Token { 13 Word(String), // 普通的命令或选项 14 Symbol(String), // 特殊符号 15 } 16 17 #[derive(Debug, Clone)] 18 pub enum CommandType { 19 Simple, // 简单命令 20 Redirect { 21 target: RedirectTarget, 22 mode: RedirectMode, 23 }, // 重定向命令 24 Pipe, // 管道命令 25 } 26 27 #[derive(Debug, Clone, PartialEq, Eq, Copy)] 28 pub enum ConnectType { 29 Simple, // 普通连接 30 And, // 与连接 31 Or, // 或连接 32 } 33 34 #[derive(Debug, Clone)] 35 pub struct Command { 36 name: String, 37 args: Vec<String>, 38 cmd_type: CommandType, 39 conn_type: ConnectType, 40 } 41 42 impl Command { 43 pub fn new( 44 name: &String, 45 args: &[String], 46 cmd_type: CommandType, 47 conn_type: ConnectType, 48 ) -> Command { 49 Self { 50 name: name.clone(), 51 args: args.to_vec(), 52 cmd_type, 53 conn_type, 54 } 55 } 56 57 pub fn execute(&self) {} 58 } 59 60 #[derive(Debug, Clone)] 61 pub enum RedirectTarget { 62 File(String), 63 FileDiscriptor(i32), 64 } 65 66 impl RedirectTarget { 67 pub fn from_string(str: &String) -> Option<RedirectTarget> { 68 if str.starts_with("&") { 69 if let Ok(fd) = str.split_at(1).1.parse::<i32>() { 70 Some(RedirectTarget::FileDiscriptor(fd)) 71 } else { 72 None 73 } 74 } else { 75 Some(RedirectTarget::File(str.clone())) 76 } 77 } 78 } 79 80 #[derive(Debug, PartialEq, Eq, Clone, Copy)] 81 pub enum RedirectMode { 82 Overwrite, 83 Append, 84 } 85 86 impl RedirectMode { 87 pub fn from_string(str: &String) -> Option<RedirectMode> { 88 match str.as_str() { 89 ">" => Some(RedirectMode::Overwrite), 90 ">>" => Some(RedirectMode::Append), 91 _ => None, 92 } 93 } 94 } 95 96 #[derive(Debug, Clone)] 97 pub enum ParseError { 98 UnexpectedInput(String), 99 UnsupportedToken(String), 100 UnexpectedToken(String), 101 } 102 103 impl ParseError { 104 pub fn handle(&self) { 105 match self { 106 ParseError::UnexpectedInput(str) => eprintln!("Unexpected input: \"{str}\""), 107 ParseError::UnsupportedToken(str) => eprintln!("Unsupported token: \"{str}\""), 108 ParseError::UnexpectedToken(str) => eprintln!("Unexpected token: \"{str}\""), 109 } 110 } 111 } 112 113 pub struct Parser; 114 115 impl Parser { 116 fn parse_env(str: &str) -> String { 117 std::env::var(str).unwrap_or(String::new()) 118 } 119 120 fn lexer(input: &str) -> Result<Vec<Token>, ParseError> { 121 let mut tokens = Vec::new(); 122 123 // 匹配环境变量的正则表达式 124 let env_token = Regex::new(r#"\$\{(\w[\w\d_]*)\}"#).unwrap(); 125 126 // 使用具体的符号组合来匹配 127 let regex_token = 128 Regex::new(r#"([^'";|&$\s]+)|(["'].*?["'])|(&&|\|\||<<|>>|[<>|&;])"#).unwrap(); 129 130 // 预先替换"${}"包围的环境变量 131 let remaining_input = env_token 132 .replace_all(input, |captures: ®ex::Captures| { 133 Self::parse_env(&captures[1]) 134 }) 135 .into_owned(); 136 137 let mut remaining_input = remaining_input.trim(); 138 139 while !remaining_input.is_empty() { 140 if let Some(mat) = regex_token.find(remaining_input) { 141 let token_str = mat.as_str(); 142 if token_str.starts_with('"') || token_str.starts_with('\'') { 143 tokens.push(Token::Word(token_str[1..token_str.len() - 1].to_string())); 144 } else if token_str.starts_with('$') { 145 tokens.push(Token::Word(Self::parse_env(&token_str[1..]))); 146 } else if token_str == ">>" 147 || token_str == ">" 148 || token_str == "<<" 149 || token_str == "<" 150 || token_str == "|" 151 || token_str == "&" 152 || token_str == ";" 153 || token_str == "&&" 154 || token_str == "||" 155 { 156 if token_str == "<" || token_str == "<<" { 157 return Err(ParseError::UnsupportedToken(token_str.to_string())); 158 } 159 tokens.push(Token::Symbol(token_str.to_string())); 160 } else { 161 tokens.push(Token::Word(token_str.to_string())); 162 } 163 164 remaining_input = &remaining_input[mat.end()..].trim(); 165 } else { 166 return Err(ParseError::UnexpectedInput(remaining_input.to_string())); 167 } 168 } 169 Ok(tokens) 170 } 171 172 fn parser(tokens: Vec<Token>) -> Result<Vec<Pipeline>, ParseError> { 173 let mut commands = Vec::new(); 174 let mut current_command: Vec<String> = Vec::new(); 175 let mut pipelines = Vec::new(); 176 let mut redirect_object: (Option<RedirectMode>, Option<RedirectTarget>) = (None, None); 177 178 for token in tokens { 179 match token { 180 Token::Word(ref word) => { 181 if let (Some(_), None) = redirect_object { 182 redirect_object.1 = RedirectTarget::from_string(word); 183 } else { 184 current_command.push(word.to_string()); 185 } 186 } 187 188 Token::Symbol(symbol) => { 189 match symbol.as_str() { 190 ">" | ">>" => { 191 // 重定向符号不能重复出现 192 if redirect_object.0.is_some() { 193 return Err(ParseError::UnexpectedToken(symbol)); 194 } else { 195 redirect_object.0 = RedirectMode::from_string(&symbol); 196 } 197 } 198 "|" | "&" | "||" | "&&" | ";" => { 199 if let Some((name, args)) = current_command.split_first() { 200 let mut cmd_type = 201 if let (Some(mode), Some(ref target)) = redirect_object { 202 CommandType::Redirect { 203 target: target.clone(), 204 mode, 205 } 206 } else { 207 CommandType::Simple 208 }; 209 210 let conn_type = match symbol.as_str() { 211 "|" => { 212 // 重定向优先级高于管道 213 if let CommandType::Simple = cmd_type { 214 cmd_type = CommandType::Pipe; 215 } 216 ConnectType::Simple 217 } 218 "&" | ";" => ConnectType::Simple, 219 "||" => ConnectType::Or, 220 "&&" => ConnectType::And, 221 _ => todo!(), 222 }; 223 224 commands.push(Command::new(name, args, cmd_type, conn_type)); 225 current_command.clear(); 226 227 if symbol == "&" { 228 pipelines.push(Pipeline::new(&commands, true)); 229 commands.clear(); 230 } 231 } else { 232 // 这些符号之前必须有word作为命令被分隔,否则这些符号是没有意义的 233 return Err(ParseError::UnexpectedToken(symbol)); 234 } 235 } 236 _ => todo!(), 237 } 238 } 239 } 240 } 241 242 // 处理最后一个命令 243 if let Some((name, args)) = current_command.split_first() { 244 commands.push(Command::new( 245 name, 246 args, 247 if let (Some(mode), Some(ref target)) = redirect_object { 248 CommandType::Redirect { 249 target: target.clone(), 250 mode, 251 } 252 } else { 253 CommandType::Simple 254 }, 255 ConnectType::Simple, 256 )); 257 } 258 259 if !commands.is_empty() { 260 pipelines.push(Pipeline::new(&commands, false)); 261 } 262 263 Ok(pipelines) 264 } 265 266 pub fn parse(input: &str) -> Result<Vec<Pipeline>, ParseError> { 267 // 解析输入并生成token列表 268 let tokens = Self::lexer(input)?; 269 // println!("tokens: {tokens:?}"); 270 271 // 解析 tokens 生成命令流水线 272 Self::parser(tokens) 273 } 274 } 275 276 #[allow(dead_code)] 277 #[derive(Debug)] 278 pub struct ExecuteError { 279 name: String, 280 err_type: ExecuteErrorType, 281 } 282 283 impl ExecuteError { 284 pub fn handle(&self, prompt: Option<String>) { 285 if let Some(prompt) = prompt { 286 eprint!("{}: ", prompt); 287 } 288 eprint!("{}: ", self.name); 289 match &self.err_type { 290 ExecuteErrorType::CommandNotFound => eprintln!("Command not found"), 291 ExecuteErrorType::FileNotFound(file) => eprintln!("Not a file or directory: {}", file), 292 ExecuteErrorType::NotDir(ref path) => eprintln!("Not a Directory: {path}"), 293 ExecuteErrorType::NotFile(ref path) => eprintln!("Not a File: {path}"), 294 ExecuteErrorType::PermissionDenied(ref file) => eprintln!("File open denied: {file}"), 295 ExecuteErrorType::ExecuteFailed => eprintln!("Command execute failed"), 296 ExecuteErrorType::ExitWithCode(exit_code) => { 297 eprintln!("Command exit with code: {}", exit_code) 298 } 299 ExecuteErrorType::ProcessTerminated => eprintln!("Process terminated"), 300 ExecuteErrorType::FileOpenFailed(file) => { 301 eprintln!("File open failed: {}", file.clone()) 302 } 303 ExecuteErrorType::TooManyArguments => eprintln!("Too many arguments"), 304 ExecuteErrorType::TooFewArguments => eprintln!("Too few arguments"), 305 ExecuteErrorType::InvalidArgument(arg) => eprintln!("Invalid argument: {}", arg), 306 } 307 } 308 } 309 310 #[allow(dead_code)] 311 #[derive(Debug, Clone)] 312 pub enum ExecuteErrorType { 313 CommandNotFound, 314 FileNotFound(String), 315 NotDir(String), 316 NotFile(String), 317 PermissionDenied(String), 318 ExecuteFailed, 319 ProcessTerminated, 320 ExitWithCode(i32), 321 FileOpenFailed(String), 322 TooManyArguments, 323 TooFewArguments, 324 InvalidArgument(String), 325 } 326 327 pub enum RedirectStdout { 328 Stdout(Option<ChildStdout>), 329 RawPipe(i32), 330 } 331 332 impl RedirectStdout { 333 pub fn as_raw_fd(&mut self) -> i32 { 334 match self { 335 RedirectStdout::Stdout(child_stdout) => child_stdout.take().unwrap().as_raw_fd(), 336 RedirectStdout::RawPipe(fd) => *fd, 337 } 338 } 339 340 pub fn as_std(&mut self) -> Stdio { 341 match self { 342 RedirectStdout::Stdout(child_stdout) => Stdio::from(child_stdout.take().unwrap()), 343 RedirectStdout::RawPipe(fd) => unsafe { Stdio::from_raw_fd(*fd) }, 344 } 345 } 346 } 347 348 impl From<i32> for RedirectStdout { 349 fn from(value: i32) -> Self { 350 RedirectStdout::RawPipe(value) 351 } 352 } 353 354 impl From<Option<ChildStdout>> for RedirectStdout { 355 fn from(mut value: Option<ChildStdout>) -> Self { 356 RedirectStdout::Stdout(value.take()) 357 } 358 } 359 360 #[derive(Debug)] 361 pub struct Pipeline { 362 commands: Vec<Command>, // 存储一系列命令 363 backend: bool, 364 } 365 366 type CommandMap = HashMap<String, fn(&Vec<String>) -> Result<(), ExecuteErrorType>>; 367 368 impl Pipeline { 369 pub fn new(commands: &Vec<Command>, backend: bool) -> Pipeline { 370 Self { 371 commands: commands.to_vec(), 372 backend, 373 } 374 } 375 376 pub fn execute(&self, internal_commands: Option<Arc<Mutex<CommandMap>>>) -> Vec<Child> { 377 // 前一个命令是否为管道输出 378 let mut stdout: Option<RedirectStdout> = None; 379 // 提前推断下条命令的布尔值,为None代表下条命令需要运行 380 let mut result_next: Option<bool> = None; 381 let mut children: Vec<Child> = Vec::new(); 382 let mut err: Option<ExecuteErrorType> = None; 383 384 for cmd in self.commands.iter() { 385 if let Some(result) = result_next { 386 // 如果前面已经推导出本条命令的布尔值,则本条命令不需要执行,并继续推断下条命令 387 if (result && cmd.conn_type == ConnectType::And) 388 || (!result && cmd.conn_type == ConnectType::Or) 389 { 390 // 如果true遇到||或false遇到&&,则下条命令的布尔值相同 391 // 如果true遇到&&或false遇到||,继承中断,设为None以执行后续命令 392 result_next = None; 393 } 394 continue; 395 } 396 397 let mut internal = false; 398 if let Some(ref map) = internal_commands { 399 let map = map.lock().unwrap(); 400 if let Some(f) = map.get(&cmd.name) { 401 // 找到内部命令,优先执行,设置标记 402 internal = true; 403 404 // child_fd 405 let child_fd = if self.backend { 406 unsafe { libc::fork() } 407 } else { 408 0 409 }; 410 411 // 为子进程或前台运行 412 if child_fd == 0 { 413 let mut old_stdin: Option<i32> = None; 414 let mut old_stdout: Option<i32> = None; 415 416 // 如果上条命令为管道,将标准输入重定向 417 if let Some(mut redirect_stdout) = stdout { 418 unsafe { 419 old_stdin = Some(libc::dup(libc::STDIN_FILENO)); 420 libc::dup2(redirect_stdout.as_raw_fd(), libc::STDIN_FILENO); 421 stdout = None; 422 } 423 } 424 425 // 根据命令类型重定向标准输出 426 match cmd.cmd_type { 427 CommandType::Simple => {} 428 CommandType::Pipe => unsafe { 429 let mut pipe: [i32; 2] = [0; 2]; 430 libc::pipe2(pipe.as_mut_ptr(), libc::O_CLOEXEC); 431 stdout = Some(RedirectStdout::from(pipe[0])); 432 433 old_stdout = Some(libc::dup(libc::STDOUT_FILENO)); 434 435 libc::dup2(pipe[1], libc::STDOUT_FILENO); 436 }, 437 CommandType::Redirect { 438 ref target, 439 ref mode, 440 } => unsafe { 441 let mut pipe: [i32; 2] = [0; 2]; 442 libc::pipe2(pipe.as_mut_ptr(), libc::O_CLOEXEC); 443 stdout = Some(RedirectStdout::from(pipe[0])); 444 445 old_stdout = Some(libc::dup(libc::STDOUT_FILENO)); 446 447 let append = match mode { 448 RedirectMode::Overwrite => false, 449 RedirectMode::Append => true, 450 }; 451 452 match target { 453 RedirectTarget::File(file) => { 454 match std::fs::OpenOptions::new() 455 .write(true) 456 .append(append) 457 .create(true) 458 .open(file) 459 { 460 Ok(file) => { 461 libc::dup2(file.as_raw_fd(), libc::STDIN_FILENO); 462 } 463 464 Err(_) => { 465 err = Some(ExecuteErrorType::FileOpenFailed( 466 file.clone(), 467 )); 468 } 469 }; 470 } 471 RedirectTarget::FileDiscriptor(fd) => { 472 libc::dup2(*fd, libc::STDIN_FILENO); 473 } 474 } 475 }, 476 } 477 478 // 如果之前没有出错,执行命令 479 if err.is_none() { 480 match f(&cmd.args) { 481 Ok(_) => err = None, 482 Err(err_type) => err = Some(err_type), 483 } 484 } 485 486 // 还原标准输出 487 unsafe { 488 if let Some(old_stdin) = old_stdin { 489 libc::dup2(old_stdin, libc::STDIN_FILENO); 490 } 491 492 if let Some(old_stdout) = old_stdout { 493 libc::dup2(old_stdout, libc::STDOUT_FILENO); 494 } 495 } 496 } else if child_fd < 0 { 497 err = Some(ExecuteErrorType::ExecuteFailed) 498 } 499 500 // 后台命令且当前进程为父进程 501 if self.backend && !child_fd == 0 { 502 err = match unsafe { libc::waitpid(child_fd, std::ptr::null_mut(), 0) } { 503 -1 => Some(ExecuteErrorType::ExecuteFailed), 504 _ => None, 505 } 506 } 507 } 508 }; 509 510 // 没找到执行内部命令的标记,尝试作为外部命令执行 511 if !internal { 512 let path = if cmd.name.contains('/') { 513 // 为路径,获取规范的绝对路径 514 if let Ok(path) = std::fs::canonicalize(&cmd.name) { 515 if path.is_file() { 516 Ok(path) 517 } else { 518 // 路径不为文件,返回错误 519 Err(ExecuteErrorType::NotFile(cmd.name.clone())) 520 } 521 } else { 522 Err(ExecuteErrorType::CommandNotFound) 523 } 524 } else { 525 // 不为路径,从环境变量中查找命令 526 which::which(&cmd.name).map_err(|_| ExecuteErrorType::CommandNotFound) 527 }; 528 529 // println!("path: {:?}", path); 530 531 match path { 532 Err(e) => err = Some(e), 533 Ok(real_path) => { 534 let mut child_command = std::process::Command::new(real_path); 535 child_command.args(cmd.args.clone()); 536 child_command.current_dir(std::env::current_dir().unwrap()); 537 if stdout.is_some() { 538 child_command.stdin(stdout.take().unwrap().as_std()); 539 } 540 541 match &cmd.cmd_type { 542 CommandType::Simple => {} 543 CommandType::Redirect { target, mode } => { 544 let append = match mode { 545 RedirectMode::Overwrite => false, 546 RedirectMode::Append => true, 547 }; 548 match target { 549 RedirectTarget::File(file) => { 550 match std::fs::OpenOptions::new() 551 .write(true) 552 .append(append) 553 .create(true) 554 .open(file) 555 { 556 Ok(file) => { 557 child_command.stdout(file); 558 } 559 Err(_) => { 560 err = Some(ExecuteErrorType::FileOpenFailed( 561 file.clone(), 562 )); 563 } 564 }; 565 } 566 RedirectTarget::FileDiscriptor(fd) => { 567 child_command.stdout(unsafe { Stdio::from_raw_fd(*fd) }); 568 } 569 } 570 } 571 CommandType::Pipe => { 572 // 标准输出重定向到管道 573 child_command.stdout(Stdio::piped()); 574 } 575 } 576 577 if err.is_none() { 578 match child_command.spawn() { 579 Ok(mut child) => { 580 // 如果为管道命令,记录下来 581 if let CommandType::Pipe = cmd.cmd_type { 582 stdout = Some(RedirectStdout::Stdout(child.stdout.take())); 583 } 584 585 // println!("exec command: {child_command:#?}"); 586 587 match child.wait() { 588 Ok(exit_status) => match exit_status.code() { 589 Some(exit_code) => { 590 if exit_code != 0 { 591 err = Some(ExecuteErrorType::ExitWithCode( 592 exit_code, 593 )); 594 } 595 } 596 None => err = Some(ExecuteErrorType::ProcessTerminated), 597 }, 598 Err(_) => err = Some(ExecuteErrorType::ExecuteFailed), 599 }; 600 601 children.push(child); 602 } 603 604 Err(e) => match e.kind() { 605 ErrorKind::PermissionDenied => { 606 err = Some(ExecuteErrorType::PermissionDenied( 607 cmd.name.clone(), 608 )) 609 } 610 _ => eprintln!("Error occurred: {}", e.kind()), 611 }, 612 } 613 } 614 } 615 } 616 } 617 618 // 预计算下条命令的结果 619 result_next = match err { 620 Some(ref e) => { 621 ExecuteError { 622 name: cmd.name.clone(), 623 err_type: e.clone(), 624 } 625 .handle(if internal { 626 Some("internal command".to_string()) 627 } else { 628 None 629 }); 630 if cmd.conn_type == ConnectType::And { 631 Some(false) 632 } else { 633 None 634 } 635 } 636 None => { 637 if cmd.conn_type == ConnectType::Or { 638 Some(true) 639 } else { 640 None 641 } 642 } 643 } 644 } 645 646 children 647 } 648 649 pub fn backend(&self) -> bool { 650 self.backend 651 } 652 } 653