mirror of
https://github.com/uutils/procps.git
synced 2026-06-10 16:14:00 -07:00
pgrep: Impl -o -n
This commit is contained in:
committed by
Krysztal Huang
parent
424c37609b
commit
227333991a
Generated
+1
@@ -862,6 +862,7 @@ name = "uu_pgrep"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"regex",
|
||||
"uucore",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
@@ -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: ®ex::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()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user