nu_command/platform/
kill.rs1use nu_engine::command_prelude::*;
2use nu_protocol::shell_error::generic::GenericError;
3use nu_system::build_kill_command;
4use std::process::Stdio;
5
6#[derive(Clone)]
7pub struct Kill;
8
9impl Command for Kill {
10 fn name(&self) -> &str {
11 "kill"
12 }
13
14 fn description(&self) -> &str {
15 "Kill a process using its process ID."
16 }
17
18 fn signature(&self) -> Signature {
19 let signature = Signature::build("kill")
20 .input_output_types(vec![(Type::Nothing, Type::Any)])
21 .allow_variants_without_examples(true)
22 .rest(
23 "pid",
24 SyntaxShape::Int,
25 "Process ids of processes that are to be killed.",
26 )
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", "taskkill"]
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 pids: Vec<i64> = call.rest(engine_state, stack, 0)?;
55 let force: bool = call.has_flag(engine_state, stack, "force")?;
56 let signal: Option<Spanned<i64>> = call.get_flag(engine_state, stack, "signal")?;
57 let quiet: bool = call.has_flag(engine_state, stack, "quiet")?;
58
59 if pids.is_empty() {
60 return Err(ShellError::MissingParameter {
61 param_name: "pid".to_string(),
62 span: call.arguments_span(),
63 });
64 }
65
66 if cfg!(unix)
67 && let (
68 true,
69 Some(Spanned {
70 item: _,
71 span: signal_span,
72 }),
73 ) = (force, signal)
74 {
75 return Err(ShellError::IncompatibleParameters {
76 left_message: "force".to_string(),
77 left_span: call
78 .get_flag_span(stack, "force")
79 .expect("Had flag force, but didn't have span for flag"),
80 right_message: "signal".to_string(),
81 right_span: Span::merge(
82 call.get_flag_span(stack, "signal")
83 .expect("Had flag signal, but didn't have span for flag"),
84 signal_span,
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| {
103 ShellError::Generic(GenericError::new(
104 "failed to execute shell command",
105 e.to_string(),
106 call.head,
107 ))
108 })?;
109
110 if !quiet && !output.status.success() {
111 return Err(ShellError::Generic(GenericError::new(
112 "process didn't terminate successfully",
113 String::from_utf8(output.stderr).unwrap_or_default(),
114 call.head,
115 )));
116 }
117
118 let mut output = String::from_utf8(output.stdout).map_err(|e| {
119 ShellError::Generic(GenericError::new(
120 "failed to convert output to string",
121 e.to_string(),
122 call.head,
123 ))
124 })?;
125
126 output.truncate(output.trim_end().len());
127
128 if output.is_empty() {
129 Ok(Value::nothing(call.head).into_pipeline_data())
130 } else {
131 Ok(Value::string(output, call.head).into_pipeline_data())
132 }
133 }
134
135 fn examples(&self) -> Vec<Example<'_>> {
136 vec![
137 Example {
138 description: "Kill the pid using the most memory.",
139 example: "ps | sort-by mem | last | kill $in.pid",
140 result: None,
141 },
142 Example {
143 description: "Force kill a given pid.",
144 example: "kill --force 12345",
145 result: None,
146 },
147 #[cfg(not(target_os = "windows"))]
148 Example {
149 description: "Send INT signal.",
150 example: "kill -s 2 12345",
151 result: None,
152 },
153 ]
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 use super::Kill;
160
161 #[test]
162 fn examples_work_as_expected() -> nu_test_support::Result {
163 nu_test_support::test().examples(Kill)
164 }
165}