nu_command/system/
exec.rs1use std::borrow::Cow;
2
3use nu_engine::{command_prelude::*, env_to_strings};
4
5#[derive(Clone)]
6pub struct Exec;
7
8impl Command for Exec {
9 fn name(&self) -> &str {
10 "exec"
11 }
12
13 fn signature(&self) -> Signature {
14 Signature::build("exec")
15 .input_output_types(vec![(Type::Nothing, Type::Any)])
16 .rest(
17 "command",
18 SyntaxShape::OneOf(vec![SyntaxShape::GlobPattern, SyntaxShape::Any]),
19 "External command to run, with arguments.",
20 )
21 .allows_unknown_args()
22 .category(Category::System)
23 }
24
25 fn description(&self) -> &str {
26 "Execute a command, replacing or exiting the current process, depending on platform."
27 }
28
29 fn extra_description(&self) -> &str {
30 r#"On Unix-based systems, the current process is replaced with the command.
31On Windows based systems, Nushell will wait for the command to finish and then exit with the command's exit code."#
32 }
33
34 fn run(
35 &self,
36 engine_state: &EngineState,
37 stack: &mut Stack,
38 call: &Call,
39 _input: PipelineData,
40 ) -> Result<PipelineData, ShellError> {
41 let cwd = engine_state.cwd(Some(stack))?;
42 let rest = call.rest::<Value>(engine_state, stack, 0)?;
43 let name_args = rest.split_first();
44
45 let Some((name, call_args)) = name_args else {
46 return Err(ShellError::MissingParameter {
47 param_name: "no command given".into(),
48 span: call.head,
49 });
50 };
51
52 let name_str: Cow<str> = match &name {
53 Value::Glob { val, .. } => Cow::Borrowed(val),
54 Value::String { val, .. } => Cow::Borrowed(val),
55 _ => Cow::Owned(name.clone().coerce_into_string()?),
56 };
57
58 let executable = {
61 let paths = nu_engine::env::path_str(engine_state, stack, call.head)?;
62 let Some(executable) = crate::which(name_str.as_ref(), &paths, cwd.as_ref()) else {
63 return Err(crate::command_not_found(
64 &name_str,
65 call.head,
66 engine_state,
67 stack,
68 &cwd,
69 ));
70 };
71 executable
72 };
73
74 let mut command = std::process::Command::new(executable);
76
77 command.current_dir(cwd);
79
80 let envs = env_to_strings(engine_state, stack)?;
82 command.env_clear();
83 command.envs(envs);
84 if engine_state.is_interactive {
87 let shlvl = engine_state
88 .get_env_var("SHLVL")
89 .and_then(|shlvl_env| shlvl_env.coerce_str().ok()?.parse::<i64>().ok())
90 .unwrap_or(1)
91 .saturating_sub(1);
92 command.env("SHLVL", shlvl.to_string());
93 }
94
95 let args = crate::eval_external_arguments(engine_state, stack, call_args.to_vec())?;
97 command.args(args.into_iter().map(|s| s.item));
98
99 #[cfg(unix)]
102 {
103 use std::os::unix::process::CommandExt;
104
105 let err = command.exec();
106 Err(ShellError::ExternalCommand {
107 label: "Failed to exec into new process".into(),
108 help: err.to_string(),
109 span: call.head,
110 })
111 }
112 #[cfg(windows)]
113 {
114 let mut child = command.spawn().map_err(|err| ShellError::ExternalCommand {
115 label: "Failed to exec into new process".into(),
116 help: err.to_string(),
117 span: call.head,
118 })?;
119 let status = child.wait().map_err(|err| ShellError::ExternalCommand {
120 label: "Failed to wait for child process".into(),
121 help: err.to_string(),
122 span: call.head,
123 })?;
124 std::process::exit(status.code().expect("status.code() succeeds on Windows"))
125 }
126 }
127
128 fn examples(&self) -> Vec<Example> {
129 vec![
130 Example {
131 description: "Execute external 'ps aux' tool",
132 example: "exec ps aux",
133 result: None,
134 },
135 Example {
136 description: "Execute 'nautilus'",
137 example: "exec nautilus",
138 result: None,
139 },
140 ]
141 }
142}