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