pgrep: Impl -o -n

This commit is contained in:
Krysztal112233
2024-05-30 17:44:25 +08:00
committed by Krysztal Huang
parent 424c37609b
commit 227333991a
5 changed files with 145 additions and 22 deletions
Generated
+1
View File
@@ -862,6 +862,7 @@ name = "uu_pgrep"
version = "0.0.1"
dependencies = [
"clap",
"regex",
"uucore",
"walkdir",
]
+1
View File
@@ -55,6 +55,7 @@ uucore = { workspace = true }
phf = { workspace = true }
textwrap = { workspace = true }
sysinfo = { workspace = true }
regex = { workspace = true }
#
pwdx = { optional = true, version = "0.0.1", package = "uu_pwdx", path = "src/uu/pwdx" }
+1
View File
@@ -16,6 +16,7 @@ categories = ["command-line-utilities"]
uucore = { workspace = true }
clap = { workspace = true }
walkdir = { workspace = true }
regex = { workspace = true }
[lib]
path = "src/pgrep.rs"
+71 -9
View File
@@ -5,7 +5,9 @@
pub mod pid;
use clap::{arg, crate_version, Arg, ArgAction, Command};
use std::{borrow::BorrowMut, cmp::Ordering};
use clap::{arg, crate_version, Arg, ArgAction, ArgGroup, Command};
use pid::walk_pid;
use uucore::{error::UResult, format_usage, help_about, help_usage};
@@ -30,19 +32,68 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let should_ignore_case = matches.get_flag("ignore-case");
let should_inverse = matches.get_flag("inverse");
let flag_list_name = matches.get_flag("list-name");
let flag_list_full = matches.get_flag("list-full");
let flag_newest = matches.get_flag("newest");
let flag_oldest = matches.get_flag("oldest");
let flag_count = matches.get_flag("count");
// let flag_full = matches.get_flag("full");
if flag_newest != flag_oldest {
let mut result = walk_pid().collect::<Vec<_>>();
result.sort_by(|a, b| {
if let (Ok(b), Ok(a)) = (
b.to_owned().borrow_mut().start_time(),
a.to_owned().borrow_mut().start_time(),
) {
b.cmp(&a)
} else {
Ordering::Equal
}
});
let sort = |start_time: u64| {
let mut result = result
.iter()
.filter(move |it| {
if let Ok(s_time) = (*it).to_owned().borrow_mut().start_time() {
s_time == start_time
} else {
false
}
})
.collect::<Vec<_>>();
result.sort_by(|a, b| {
if let (Ok(b), Ok(a)) = (
(*b).to_owned().borrow_mut().start_time(),
(*a).to_owned().borrow_mut().start_time(),
) {
b.cmp(&a)
} else {
Ordering::Equal
}
});
result
};
let mut entry = if flag_newest {
result.first()
} else {
result.last()
}
.expect("empty pid list")
.to_owned();
println!("{}", sort(entry.start_time()?).first().unwrap().pid);
return Ok(());
}
if should_ignore_case {
programs = programs.into_iter().map(|it| it.to_lowercase()).collect();
}
let result: Vec<_> = walk_pid()
let result = walk_pid()
.filter(|it| {
let Some(name) = it.status.get("Name") else {
let binding = it.to_owned().borrow_mut().status();
let Some(name) = binding.get("Name") else {
return false;
};
@@ -55,8 +106,12 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
programs.iter().any(|it| name.contains(it)) ^ should_inverse
})
.collect();
.collect::<Vec<_>>();
let flag_list_name = matches.get_flag("list-name");
let flag_list_full = matches.get_flag("list-full");
let flag_count = matches.get_flag("count");
if flag_count {
println!("{}", result.len());
} else {
@@ -67,7 +122,13 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
if flag_list_full {
format!("{} {}", it.pid, it.cmdline)
} else if flag_list_name {
let name = it.status.get("Name").cloned().unwrap_or_default();
let name = it
.to_owned()
.borrow_mut()
.status()
.get("Name")
.cloned()
.unwrap_or_default();
format!("{} {}", it.pid, name)
} else {
format!("{}", it.pid)
@@ -85,6 +146,7 @@ pub fn uu_app() -> Command {
.version(crate_version!())
.about(ABOUT)
.override_usage(format_usage(USAGE))
.group(ArgGroup::new("oldest_newest").args(["oldest", "newest"]))
.args([
arg!(-d --delimiter <string> "specify output delimiter")
.default_value("\n")
+71 -13
View File
@@ -3,14 +3,76 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
use std::{collections::HashMap, fs, io, path::PathBuf};
use regex::Regex;
use std::{collections::HashMap, fs, io, path::PathBuf, rc::Rc};
use walkdir::{DirEntry, WalkDir};
#[derive(Debug)]
#[derive(Debug, Clone, Default)]
pub struct PidEntry {
pub pid: usize,
pub cmdline: String,
pub status: HashMap<String, String>,
inner_status: String,
inner_stat: String,
cached_status: Option<Rc<HashMap<String, String>>>,
cached_stat: Option<Rc<Vec<String>>>,
}
impl PidEntry {
pub fn status(&mut self) -> Rc<HashMap<String, String>> {
if let Some(c) = &self.cached_status {
return Rc::clone(c);
}
let result = self
.inner_status
.lines()
.filter_map(|it| it.split_once(':'))
.map(|it| (it.0.to_string(), it.1.trim_start().to_string()))
.collect::<HashMap<_, _>>();
let result = Rc::new(result);
self.cached_status = Some(Rc::clone(&result));
Rc::clone(&result)
}
fn stat(&mut self) -> Result<Rc<Vec<String>>, io::Error> {
if let Some(c) = &self.cached_stat {
return Ok(Rc::clone(c));
}
let result: Vec<_> = {
// Regex that have been validated
let regex = Regex::new(r"\(([^()]*)\)").unwrap();
let result = regex
.replace_all(&self.inner_stat, |caps: &regex::Captures| {
let inner = &caps[1];
format!("({})", inner.replace(' ', "$$"))
})
.into_owned();
result
.split_whitespace()
.map(|it| it.replace("$$", " "))
.collect()
};
let result = Rc::new(result);
self.cached_stat = Some(Rc::clone(&result));
Ok(Rc::clone(&result))
}
pub fn start_time(&mut self) -> Result<u64, io::Error> {
// Kernel doc: https://docs.kernel.org/filesystems/proc.html#process-specific-subdirectories
// Table 1-4
Ok(self
.stat()?
.get(21)
.ok_or(io::ErrorKind::InvalidData)?
.parse::<u64>()
.map_err(|_| io::ErrorKind::InvalidData)?)
}
}
impl TryFrom<DirEntry> for PidEntry {
@@ -37,20 +99,16 @@ impl TryFrom<DirEntry> for PidEntry {
.replace('\0', " ")
.trim_end()
.into();
let status = {
let content =
fs::read_to_string(dir_append(value.clone().into_path(), "status".into()))?;
content
.lines()
.filter_map(|it| it.split_once(':'))
.map(|it| (it.0.to_string(), it.1.trim_start().to_string()))
.collect::<HashMap<_, _>>()
};
Ok(Self {
pid,
cmdline,
status,
inner_status: fs::read_to_string(dir_append(
value.clone().into_path(),
"status".into(),
))?,
inner_stat: fs::read_to_string(dir_append(value.clone().into_path(), "stat".into()))?,
..Default::default()
})
}
}