Skip to content

Commit aea0541

Browse files
authored
Merge pull request #249 from rustcoreutils/ps
PS: flexible output field selection
2 parents cb88f8b + 1cb6145 commit aea0541

File tree

3 files changed

+94
-9
lines changed

3 files changed

+94
-9
lines changed

fs/df.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,11 @@ fn read_mount_info() -> io::Result<MountList> {
161161
let mut mount: libc::statfs = std::mem::zeroed();
162162
let rc = libc::statfs(dirname.as_ptr(), &mut mount);
163163
if rc < 0 {
164-
eprintln!("{}: {}", dirname.to_str().unwrap(), io::Error::last_os_error());
164+
eprintln!(
165+
"{}: {}",
166+
dirname.to_str().unwrap(),
167+
io::Error::last_os_error()
168+
);
165169
continue;
166170
}
167171

process/tests/fuser/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ fn fuser_test(
3939
///
4040
/// **Assertions:**
4141
/// - Verifies that the PID of the process is included in the output of `fuser`.
42+
#[cfg(target_os = "linux")]
4243
#[tokio::test]
4344
async fn test_fuser_basic() {
4445
let process = Command::new("sleep")

sys/ps.rs

Lines changed: 88 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ mod psmacos;
1414
mod pslinux;
1515

1616
use clap::Parser;
17+
use std::collections::HashMap;
1718

1819
#[cfg(target_os = "macos")]
1920
mod platform {
@@ -44,6 +45,57 @@ struct Args {
4445
/// Exclude session leaders
4546
#[arg(short = 'd', long)]
4647
exclude_session_leaders: bool,
48+
49+
/// Full output format (-f)
50+
#[arg(short = 'f', long)]
51+
full_format: bool,
52+
53+
/// Long output format (-l)
54+
#[arg(short = 'l', long)]
55+
long_format: bool,
56+
57+
/// Custom output format (-o)
58+
#[arg(short = 'o', long, value_parser = clap::builder::NonEmptyStringValueParser::new())]
59+
output_format: Option<String>,
60+
}
61+
62+
// Parse the -o format option into a list of fields
63+
fn parse_output_format<'a>(
64+
format: &'a str,
65+
posix_fields: &'a HashMap<&'a str, (&'a str, &'a str)>,
66+
) -> Vec<&'a str> {
67+
format
68+
.split(|c| c == ' ' || c == ',')
69+
.map(|s| {
70+
let field = s.split('=').next().unwrap_or("").trim();
71+
if posix_fields.contains_key(field) {
72+
field
73+
} else {
74+
panic!("Invalid field specified in -o option: {}", field);
75+
}
76+
})
77+
.collect()
78+
}
79+
80+
// Lookup table for POSIX-compliant output fields
81+
fn posix_field_map() -> HashMap<&'static str, (&'static str, &'static str)> {
82+
HashMap::from([
83+
("ruser", ("uid", "RUSER")),
84+
("user", ("uid", "USER")),
85+
("rgroup", ("gid", "RGROUP")),
86+
("group", ("gid", "GROUP")),
87+
("pid", ("pid", "PID")),
88+
("ppid", ("ppid", "PPID")),
89+
("pgid", ("pgid", "PGID")),
90+
("pcpu", ("pcpu", "%CPU")),
91+
("vsz", ("vsz", "VSZ")),
92+
("nice", ("nice", "NI")),
93+
("etime", ("etime", "ELAPSED")),
94+
("time", ("time", "TIME")),
95+
("tty", ("tty", "TTY")),
96+
("comm", ("comm", "COMMAND")),
97+
("args", ("args", "COMMAND")),
98+
])
4799
}
48100

49101
fn main() {
@@ -80,14 +132,42 @@ fn main() {
80132
processes
81133
};
82134

83-
println!(
84-
"{:<5} {:<5} {:<5} {:<5} {}",
85-
"PID", "PPID", "UID", "GID", "COMMAND"
86-
);
135+
// Define a lookup table for POSIX-compliant fields
136+
let posix_fields = posix_field_map();
137+
138+
// Build output based on -o, -f, -l, or default
139+
let output_fields = if let Some(ref format) = args.output_format {
140+
parse_output_format(format, &posix_fields)
141+
} else if args.full_format {
142+
vec!["uid", "pid", "ppid", "C", "time", "comm"]
143+
} else if args.long_format {
144+
vec!["nice", "vsz", "WCHAN", "tty", "comm"]
145+
} else {
146+
vec!["pid", "ppid", "tty", "time", "comm"] // Default format
147+
};
148+
149+
// Print the header
150+
for field in &output_fields {
151+
let header = posix_fields.get(*field).unwrap_or(&(&field, &field)).1;
152+
print!("{:<10} ", header);
153+
}
154+
println!();
155+
156+
// Print each process
87157
for proc in filtered_processes {
88-
println!(
89-
"{:<5} {:<5} {:<5} {:<5} {}",
90-
proc.pid, proc.ppid, proc.uid, proc.gid, proc.path
91-
);
158+
for field in &output_fields {
159+
match *field {
160+
"pid" => print!("{:<10} ", proc.pid),
161+
"ppid" => print!("{:<10} ", proc.ppid),
162+
"group" => print!("{:<10} ", proc.gid),
163+
"tty" => print!("{:<10} ", proc.tty.as_deref().unwrap_or("-")),
164+
// "time" => print!("{:<10} ", proc.time),
165+
"comm" => print!("{:<10} ", proc.path),
166+
"user" => print!("{:<10} ", proc.uid), // Example for user field, would need to resolve UID -> username
167+
// Add cases for more fields as needed...
168+
_ => print!("{:<10} ", "-"),
169+
}
170+
}
171+
println!();
92172
}
93173
}

0 commit comments

Comments
 (0)