ps: Implement bunch of field selection flags

This commit is contained in:
Tuomas Tynkkynen
2025-06-24 03:30:00 +03:00
parent 38c49c9cdf
commit 9518c429aa
4 changed files with 199 additions and 3 deletions
+59
View File
@@ -26,6 +26,65 @@ pub(crate) fn default_codes() -> Vec<String> {
["pid", "tname", "time", "ucmd"].map(Into::into).to_vec()
}
/// Returns the full format codes (for -f flag).
pub(crate) fn full_format_codes() -> Vec<String> {
[
"uid_hack", "pid", "ppid", "c", "stime", "tname", "time", "cmd",
]
.map(Into::into)
.to_vec()
}
/// Returns the extra full format codes (for -F flag).
pub(crate) fn extra_full_format_codes() -> Vec<String> {
[
"uid", "pid", "ppid", "c", "sz", "rss", "psr", "stime", "tname", "time", "ucmd",
]
.map(Into::into)
.to_vec()
}
/// Returns the job format codes (for -j flag).
pub(crate) fn job_format_codes() -> Vec<String> {
["pid", "pgid", "sid", "tname", "time", "ucmd"]
.map(Into::into)
.to_vec()
}
/// Returns the default codes with PSR column (for -P flag).
pub(crate) fn default_with_psr_codes() -> Vec<String> {
["pid", "psr", "tname", "time", "ucmd"]
.map(Into::into)
.to_vec()
}
/// Returns the signal format codes (for -s flag).
pub(crate) fn signal_format_codes() -> Vec<String> {
[
"uid", "pid", "pending", "blocked", "ignored", "caught", "stat", "tname", "time", "command",
]
.map(Into::into)
.to_vec()
}
/// Returns the user format codes (for -u flag).
pub(crate) fn user_format_codes() -> Vec<String> {
[
"user", "pid", "%cpu", "%mem", "vsz", "rss", "tname", "stat", "bsdstart", "time", "command",
]
.map(Into::into)
.to_vec()
}
/// Returns the virtual memory format codes (for -v flag).
pub(crate) fn vm_format_codes() -> Vec<String> {
[
"pid", "tname", "stat", "time", "maj_flt", "trs", "drs", "rss", "%mem", "command",
]
.map(Into::into)
.to_vec()
}
/// Collect mapping from argument
pub(crate) fn default_mapping() -> HashMap<String, String> {
let mut mapping = HashMap::new();
+1 -1
View File
@@ -31,7 +31,7 @@ pub(crate) fn collect_pickers(
"uid" | "euid" => pickers.push(helper(euid)),
"ruid" => pickers.push(helper(ruid)),
"suid" => pickers.push(helper(suid)),
"user" | "euser" => pickers.push(helper(euser)),
"uid_hack" | "user" | "euser" => pickers.push(helper(euser)),
"ruser" => pickers.push(helper(ruser)),
"suser" => pickers.push(helper(suser)),
"pgid" => pickers.push(helper(pgid)),
+63 -2
View File
@@ -11,7 +11,11 @@ mod sorting;
use clap::crate_version;
use clap::{Arg, ArgAction, ArgMatches, Command};
use mapping::{collect_code_mapping, default_codes, default_mapping};
use mapping::{
collect_code_mapping, default_codes, default_mapping, default_with_psr_codes,
extra_full_format_codes, full_format_codes, job_format_codes, signal_format_codes,
user_format_codes, vm_format_codes,
};
use parser::{parser, OptionalKeyValue};
use prettytable::{format::consts::FORMAT_CLEAN, Row, Table};
use std::{cell::RefCell, rc::Rc};
@@ -47,7 +51,21 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
};
// Collect codes with order
let codes = if arg_formats.is_empty() {
let codes = if matches.get_flag("f") {
full_format_codes()
} else if matches.get_flag("F") {
extra_full_format_codes()
} else if matches.get_flag("j") {
job_format_codes()
} else if matches.get_flag("P") {
default_with_psr_codes()
} else if matches.get_flag("s") {
signal_format_codes()
} else if matches.get_flag("u") {
user_format_codes()
} else if matches.get_flag("v") {
vm_format_codes()
} else if arg_formats.is_empty() {
default_codes()
} else {
arg_formats.iter().map(|it| it.key().to_owned()).collect()
@@ -164,6 +182,49 @@ pub fn uu_app() -> Command {
// .help("processes without controlling ttys")
// .allow_hyphen_values(true),
])
.arg(
Arg::new("f")
.short('f')
.action(ArgAction::SetTrue)
.help("full format listing"),
)
.arg(
Arg::new("F")
.short('F')
.action(ArgAction::SetTrue)
.help("extra full format listing"),
)
.arg(
Arg::new("j")
.short('j')
.action(ArgAction::SetTrue)
.help("job format"),
)
.arg(
Arg::new("P")
.short('P')
.action(ArgAction::SetTrue)
.help("add psr column"),
)
.arg(
Arg::new("s")
.short('s')
.action(ArgAction::SetTrue)
.help("signal format"),
)
// TODO: this can also be used with argument to filter by uid
.arg(
Arg::new("u")
.short('u')
.action(ArgAction::SetTrue)
.help("user format"),
)
.arg(
Arg::new("v")
.short('v')
.action(ArgAction::SetTrue)
.help("virtual memory format"),
)
.arg(
Arg::new("format")
.short('o')
+76
View File
@@ -21,6 +21,82 @@ fn test_invalid_arg() {
new_ucmd!().arg("--definitely-invalid").fails().code_is(1);
}
/// Helper function to check that ps output has the correct headers in the correct order
#[cfg(target_os = "linux")]
fn check_header(flag: &str, expected_headers: &[&str]) {
let result = new_ucmd!().arg(flag).succeeds();
let lines: Vec<&str> = result.stdout_str().lines().collect();
let headers: Vec<&str> = lines[0].split_whitespace().collect();
assert_eq!(headers, expected_headers);
}
#[test]
#[cfg(target_os = "linux")]
fn test_full_format_listing() {
check_header(
"-f",
&["UID", "PID", "PPID", "C", "STIME", "TTY", "TIME", "CMD"],
);
}
#[test]
#[cfg(target_os = "linux")]
fn test_extra_full_format() {
check_header(
"-F",
&[
"UID", "PID", "PPID", "C", "SZ", "RSS", "PSR", "STIME", "TTY", "TIME", "CMD",
],
);
}
#[test]
#[cfg(target_os = "linux")]
fn test_job_format() {
check_header("-j", &["PID", "PGID", "SID", "TTY", "TIME", "CMD"]);
}
#[test]
#[cfg(target_os = "linux")]
fn test_psr_format() {
check_header("-P", &["PID", "PSR", "TTY", "TIME", "CMD"]);
}
#[test]
#[cfg(target_os = "linux")]
fn test_signal_format() {
check_header(
"-s",
&[
"UID", "PID", "PENDING", "BLOCKED", "IGNORED", "CAUGHT", "STAT", "TTY", "TIME",
"COMMAND",
],
);
}
#[test]
#[cfg(target_os = "linux")]
fn test_user_format() {
check_header(
"-u",
&[
"USER", "PID", "%CPU", "%MEM", "VSZ", "RSS", "TTY", "STAT", "START", "TIME", "COMMAND",
],
);
}
#[test]
#[cfg(target_os = "linux")]
fn test_virtual_memory_format() {
check_header(
"-v",
&[
"PID", "TTY", "STAT", "TIME", "MAJFL", "TRS", "DRS", "RSS", "%MEM", "COMMAND",
],
);
}
#[test]
#[cfg(target_os = "linux")]
fn test_code_mapping() {