ps: Implement --pid flag

This commit is contained in:
Tuomas Tynkkynen
2025-11-02 00:47:27 +02:00
parent e8eb577386
commit f1b89bc575
3 changed files with 63 additions and 1 deletions
+11
View File
@@ -4,6 +4,7 @@
// file that was distributed with this source code.
use clap::ArgMatches;
use std::collections::HashSet;
use uu_pgrep::process::{walk_process, ProcessInformation, RunState, Teletype};
use uucore::error::UResult;
@@ -46,6 +47,9 @@ pub struct ProcessSelectionSettings {
/// - '-x' Lift "must have a tty" restriction.
pub dont_require_tty: bool,
/// Select specific process IDs (-p, --pid)
pub pids: Option<HashSet<usize>>,
/// - `-r` Restrict the selection to only running processes.
pub only_running: bool,
@@ -60,6 +64,9 @@ impl ProcessSelectionSettings {
select_non_session_leaders_with_tty: matches.get_flag("a"),
select_non_session_leaders: matches.get_flag("d"),
dont_require_tty: matches.get_flag("x"),
pids: matches
.get_many::<Vec<usize>>("pid")
.map(|xs| xs.flatten().copied().collect()),
only_running: matches.get_flag("r"),
negate_selection: matches.get_flag("deselect"),
}
@@ -75,6 +82,10 @@ impl ProcessSelectionSettings {
return Ok(false);
}
if let Some(ref pids) = self.pids {
return Ok(pids.contains(&process.pid));
}
if self.select_all {
return Ok(true);
}
+17 -1
View File
@@ -138,6 +138,15 @@ fn collect_format(
Ok(collect)
}
fn parse_numeric_list(s: &str) -> Result<Vec<usize>, String> {
s.split(|c: char| c.is_whitespace() || c == ',')
.map(|word| {
word.parse::<usize>()
.map_err(|_| format!("invalid number: '{}'", word))
})
.collect()
}
#[allow(clippy::cognitive_complexity)]
pub fn uu_app() -> Command {
Command::new(uucore::util_name())
@@ -262,6 +271,14 @@ pub fn uu_app() -> Command {
.action(ArgAction::SetTrue)
.help("do not print header at all"),
)
.arg(
Arg::new("pid")
.short('p')
.long("pid")
.action(ArgAction::Append)
.value_parser(parse_numeric_list)
.help("select by process ID"),
)
// .args([
// Arg::new("command").short('c').help("command name"),
// Arg::new("GID")
@@ -272,7 +289,6 @@ pub fn uu_app() -> Command {
// .short('g')
// .long("group")
// .help("session or effective group name"),
// Arg::new("PID").short('p').long("pid").help("process id"),
// Arg::new("pPID").long("ppid").help("parent process id"),
// Arg::new("qPID")
// .short('q')
+35
View File
@@ -202,3 +202,38 @@ fn test_deselect() {
.succeeds()
.stdout_matches(&Regex::new("\n *1 ").unwrap());
}
#[test]
#[cfg(target_os = "linux")]
fn test_pid_selection() {
let our_pid = std::process::id();
// Test that only pid 1 and pid of the test runner is present
let test = |pid_args: &[&str]| {
let match_regex = Regex::new(&format!("^ *1 *\n *{our_pid} *\n$")).unwrap();
let mut args = vec!["--no-headers", "-o", "pid"];
args.extend_from_slice(pid_args);
new_ucmd!()
.args(&args)
.succeeds()
.stdout_matches(&match_regex);
};
for flag in ["-p", "--pid"] {
test(&[flag, &format!("1 {our_pid}")]);
test(&[flag, &format!("1,{our_pid}")]);
test(&[flag, "1", flag, &our_pid.to_string()]);
}
// Test nonexistent PID (should show no output)
new_ucmd!()
.args(&["-p", "0", "--no-headers"])
.fails()
.code_is(1)
.stdout_is("");
// Test invalid PID
new_ucmd!()
.args(&["-p", "invalid"])
.fails()
.stderr_contains("invalid number");
}