Skip to content

Commit f1b89bc

Browse files
committed
ps: Implement --pid flag
1 parent e8eb577 commit f1b89bc

File tree

3 files changed

+63
-1
lines changed

3 files changed

+63
-1
lines changed

src/uu/ps/src/process_selection.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
// file that was distributed with this source code.
55

66
use clap::ArgMatches;
7+
use std::collections::HashSet;
78
use uu_pgrep::process::{walk_process, ProcessInformation, RunState, Teletype};
89
use uucore::error::UResult;
910

@@ -46,6 +47,9 @@ pub struct ProcessSelectionSettings {
4647
/// - '-x' Lift "must have a tty" restriction.
4748
pub dont_require_tty: bool,
4849

50+
/// Select specific process IDs (-p, --pid)
51+
pub pids: Option<HashSet<usize>>,
52+
4953
/// - `-r` Restrict the selection to only running processes.
5054
pub only_running: bool,
5155

@@ -60,6 +64,9 @@ impl ProcessSelectionSettings {
6064
select_non_session_leaders_with_tty: matches.get_flag("a"),
6165
select_non_session_leaders: matches.get_flag("d"),
6266
dont_require_tty: matches.get_flag("x"),
67+
pids: matches
68+
.get_many::<Vec<usize>>("pid")
69+
.map(|xs| xs.flatten().copied().collect()),
6370
only_running: matches.get_flag("r"),
6471
negate_selection: matches.get_flag("deselect"),
6572
}
@@ -75,6 +82,10 @@ impl ProcessSelectionSettings {
7582
return Ok(false);
7683
}
7784

85+
if let Some(ref pids) = self.pids {
86+
return Ok(pids.contains(&process.pid));
87+
}
88+
7889
if self.select_all {
7990
return Ok(true);
8091
}

src/uu/ps/src/ps.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,15 @@ fn collect_format(
138138
Ok(collect)
139139
}
140140

141+
fn parse_numeric_list(s: &str) -> Result<Vec<usize>, String> {
142+
s.split(|c: char| c.is_whitespace() || c == ',')
143+
.map(|word| {
144+
word.parse::<usize>()
145+
.map_err(|_| format!("invalid number: '{}'", word))
146+
})
147+
.collect()
148+
}
149+
141150
#[allow(clippy::cognitive_complexity)]
142151
pub fn uu_app() -> Command {
143152
Command::new(uucore::util_name())
@@ -262,6 +271,14 @@ pub fn uu_app() -> Command {
262271
.action(ArgAction::SetTrue)
263272
.help("do not print header at all"),
264273
)
274+
.arg(
275+
Arg::new("pid")
276+
.short('p')
277+
.long("pid")
278+
.action(ArgAction::Append)
279+
.value_parser(parse_numeric_list)
280+
.help("select by process ID"),
281+
)
265282
// .args([
266283
// Arg::new("command").short('c').help("command name"),
267284
// Arg::new("GID")
@@ -272,7 +289,6 @@ pub fn uu_app() -> Command {
272289
// .short('g')
273290
// .long("group")
274291
// .help("session or effective group name"),
275-
// Arg::new("PID").short('p').long("pid").help("process id"),
276292
// Arg::new("pPID").long("ppid").help("parent process id"),
277293
// Arg::new("qPID")
278294
// .short('q')

tests/by-util/test_ps.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,3 +202,38 @@ fn test_deselect() {
202202
.succeeds()
203203
.stdout_matches(&Regex::new("\n *1 ").unwrap());
204204
}
205+
206+
#[test]
207+
#[cfg(target_os = "linux")]
208+
fn test_pid_selection() {
209+
let our_pid = std::process::id();
210+
// Test that only pid 1 and pid of the test runner is present
211+
let test = |pid_args: &[&str]| {
212+
let match_regex = Regex::new(&format!("^ *1 *\n *{our_pid} *\n$")).unwrap();
213+
let mut args = vec!["--no-headers", "-o", "pid"];
214+
args.extend_from_slice(pid_args);
215+
new_ucmd!()
216+
.args(&args)
217+
.succeeds()
218+
.stdout_matches(&match_regex);
219+
};
220+
221+
for flag in ["-p", "--pid"] {
222+
test(&[flag, &format!("1 {our_pid}")]);
223+
test(&[flag, &format!("1,{our_pid}")]);
224+
test(&[flag, "1", flag, &our_pid.to_string()]);
225+
}
226+
227+
// Test nonexistent PID (should show no output)
228+
new_ucmd!()
229+
.args(&["-p", "0", "--no-headers"])
230+
.fails()
231+
.code_is(1)
232+
.stdout_is("");
233+
234+
// Test invalid PID
235+
new_ucmd!()
236+
.args(&["-p", "invalid"])
237+
.fails()
238+
.stderr_contains("invalid number");
239+
}

0 commit comments

Comments
 (0)