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 "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}