diff --git a/src/cli.rs b/src/cli.rs index 219fdaa51..0cab0d554 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -271,6 +271,7 @@ pub async fn cli() -> Result<(), Box> { per_item_command(Calc), per_item_command(Mkdir), per_item_command(Move), + per_item_command(Kill), whole_stream_command(Version), whole_stream_command(Clear), whole_stream_command(What), diff --git a/src/commands.rs b/src/commands.rs index bd507f20f..e96218a98 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -119,6 +119,8 @@ pub(crate) use default::Default; pub(crate) use du::Du; pub(crate) use echo::Echo; pub(crate) use edit::Edit; +pub(crate) mod kill; +pub(crate) use kill::Kill; pub(crate) mod clear; pub(crate) use clear::Clear; pub(crate) use enter::Enter; diff --git a/src/commands/kill.rs b/src/commands/kill.rs new file mode 100644 index 000000000..c6aaed476 --- /dev/null +++ b/src/commands/kill.rs @@ -0,0 +1,104 @@ +use crate::commands::command::RunnablePerItemContext; +use crate::context::CommandRegistry; +use crate::prelude::*; +use nu_errors::ShellError; +use nu_protocol::{CallInfo, Signature, SyntaxShape, Value}; +use nu_source::Tagged; +use std::process::{Command, Stdio}; + +pub struct Kill; + +#[derive(Deserialize)] +pub struct KillArgs { + pub pid: Tagged, + pub rest: Vec>, + pub force: Tagged, + pub quiet: Tagged, +} + +impl PerItemCommand for Kill { + fn name(&self) -> &str { + "kill" + } + + fn signature(&self) -> Signature { + Signature::build("kill") + .required( + "pid", + SyntaxShape::Int, + "process id of process that is to be killed", + ) + .rest(SyntaxShape::Int, "rest of processes to kill") + .switch("force", "forcefully kill the process") + .switch("quiet", "won't print anything to the console") + } + + fn usage(&self) -> &str { + "Kill a process using the process id." + } + + fn run( + &self, + call_info: &CallInfo, + _registry: &CommandRegistry, + raw_args: &RawCommandArgs, + _input: Value, + ) -> Result { + call_info + .process(&raw_args.shell_manager, raw_args.ctrl_c.clone(), kill)? + .run() + } +} + +fn kill( + KillArgs { + pid, + rest, + force, + quiet, + }: KillArgs, + _context: &RunnablePerItemContext, +) -> Result { + let mut cmd = if cfg!(windows) { + let mut cmd = Command::new("taskkill"); + + if *force { + cmd.arg("/F"); + } + + cmd.arg("/PID"); + cmd.arg(pid.item().to_string()); + + // each pid must written as `/PID 0` otherwise + // taskkill will act as `killall` unix command + for id in &rest { + cmd.arg("/PID"); + cmd.arg(id.item().to_string()); + } + + cmd + } else { + let mut cmd = Command::new("kill"); + + if *force { + cmd.arg("-9"); + } + + cmd.arg(pid.item().to_string()); + + cmd.args(rest.iter().map(move |id| id.item().to_string())); + + cmd + }; + + // pipe everything to null + if *quiet { + cmd.stdin(Stdio::null()) + .stdout(Stdio::null()) + .stderr(Stdio::null()); + } + + cmd.status().expect("failed to execute shell command"); + + Ok(OutputStream::empty()) +}