nu_command/platform/
kill.rs1use nu_engine::command_prelude::*;
2use nu_system::build_kill_command;
3use std::process::Stdio;
4
5#[derive(Clone)]
6pub struct Kill;
7
8impl Command for Kill {
9 fn name(&self) -> &str {
10 "kill"
11 }
12
13 fn description(&self) -> &str {
14 "Kill a process using the process id."
15 }
16
17 fn signature(&self) -> Signature {
18 let signature = Signature::build("kill")
19 .input_output_types(vec![(Type::Nothing, Type::Any)])
20 .allow_variants_without_examples(true)
21 .rest(
22 "pid",
23 SyntaxShape::Int,
24 "Process ids of processes that are to be killed.",
25 )
26 .switch("force", "forcefully kill the process", Some('f'))
27 .switch("quiet", "won't print anything to the console", Some('q'))
28 .category(Category::Platform);
29
30 if cfg!(windows) {
31 return signature;
32 }
33
34 signature.named(
35 "signal",
36 SyntaxShape::Int,
37 "signal decimal number to be sent instead of the default 15 (unsupported on Windows)",
38 Some('s'),
39 )
40 }
41
42 fn search_terms(&self) -> Vec<&str> {
43 vec!["stop", "end", "close"]
44 }
45
46 fn run(
47 &self,
48 engine_state: &EngineState,
49 stack: &mut Stack,
50 call: &Call,
51 _input: PipelineData,
52 ) -> Result<PipelineData, ShellError> {
53 let pids: Vec<i64> = call.rest(engine_state, stack, 0)?;
54 let force: bool = call.has_flag(engine_state, stack, "force")?;
55 let signal: Option<Spanned<i64>> = call.get_flag(engine_state, stack, "signal")?;
56 let quiet: bool = call.has_flag(engine_state, stack, "quiet")?;
57
58 if pids.is_empty() {
59 return Err(ShellError::MissingParameter {
60 param_name: "pid".to_string(),
61 span: call.arguments_span(),
62 });
63 }
64
65 if cfg!(unix) {
66 if let (
67 true,
68 Some(Spanned {
69 item: _,
70 span: signal_span,
71 }),
72 ) = (force, signal)
73 {
74 return Err(ShellError::IncompatibleParameters {
75 left_message: "force".to_string(),
76 left_span: call
77 .get_flag_span(stack, "force")
78 .expect("Had flag force, but didn't have span for flag"),
79 right_message: "signal".to_string(),
80 right_span: Span::merge(
81 call.get_flag_span(stack, "signal")
82 .expect("Had flag signal, but didn't have span for flag"),
83 signal_span,
84 ),
85 });
86 }
87 };
88
89 let mut cmd = build_kill_command(
90 force,
91 pids.iter().copied(),
92 signal.map(|spanned| spanned.item as u32),
93 );
94
95 if quiet {
97 cmd.stdin(Stdio::null())
98 .stdout(Stdio::null())
99 .stderr(Stdio::null());
100 }
101
102 let output = cmd.output().map_err(|e| ShellError::GenericError {
103 error: "failed to execute shell command".into(),
104 msg: e.to_string(),
105 span: Some(call.head),
106 help: None,
107 inner: vec![],
108 })?;
109
110 if !quiet && !output.status.success() {
111 return Err(ShellError::GenericError {
112 error: "process didn't terminate successfully".into(),
113 msg: String::from_utf8(output.stderr).unwrap_or_default(),
114 span: Some(call.head),
115 help: None,
116 inner: vec![],
117 });
118 }
119
120 let mut output =
121 String::from_utf8(output.stdout).map_err(|e| ShellError::GenericError {
122 error: "failed to convert output to string".into(),
123 msg: e.to_string(),
124 span: Some(call.head),
125 help: None,
126 inner: vec![],
127 })?;
128
129 output.truncate(output.trim_end().len());
130
131 if output.is_empty() {
132 Ok(Value::nothing(call.head).into_pipeline_data())
133 } else {
134 Ok(Value::string(output, call.head).into_pipeline_data())
135 }
136 }
137
138 fn examples(&self) -> Vec<Example> {
139 vec![
140 Example {
141 description: "Kill the pid using the most memory",
142 example: "ps | sort-by mem | last | kill $in.pid",
143 result: None,
144 },
145 Example {
146 description: "Force kill a given pid",
147 example: "kill --force 12345",
148 result: None,
149 },
150 #[cfg(not(target_os = "windows"))]
151 Example {
152 description: "Send INT signal",
153 example: "kill -s 2 12345",
154 result: None,
155 },
156 ]
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 use super::Kill;
163
164 #[test]
165 fn examples_work_as_expected() {
166 use crate::test_examples;
167 test_examples(Kill {})
168 }
169}