Repository: uutils/procps
Pull request #166 (2024-08-07 14:29:40)

pwdx: add support for multiple PIDs
This PR adds support for multiple PIDs to match the behavior of the original `pwdx`.


Patch:
diff --git a/src/uu/pwdx/src/pwdx.rs b/src/uu/pwdx/src/pwdx.rs
index 77a18e8..1003ca0 100644
--- a/src/uu/pwdx/src/pwdx.rs
+++ b/src/uu/pwdx/src/pwdx.rs
@@ -18,27 +18,31 @@ const USAGE: &str = help_usage!("pwdx.md");
 pub fn uumain(args: impl uucore::Args) -> UResult<()> {
     let matches = uu_app().try_get_matches_from(args)?;
 
-    let pid_str = matches.get_one::<String>("pid").unwrap();
-    let pid = match pid_str.parse::<i32>() {
-        // PIDs start at 1, hence 0 is invalid
-        Ok(0) | Err(_) => {
-            return Err(USimpleError::new(
-                1,
-                format!("invalid process id: {pid_str}"),
-            ))
-        }
-        Ok(pid) => pid,
-    };
+    let pids = matches.get_many::<String>("pid").unwrap();
+
+    for pid_str in pids {
+        let pid = match pid_str.parse::<i32>() {
+            // PIDs start at 1, hence 0 is invalid
+            Ok(0) | Err(_) => {
+                return Err(USimpleError::new(
+                    1,
+                    format!("invalid process id: {pid_str}"),
+                ))
+            }
+            Ok(pid) => pid,
+        };
 
-    let cwd_link = format!("/proc/{}/cwd", pid);
+        let cwd_link = format!("/proc/{}/cwd", pid);
 
-    match fs::read_link(Path::new(&cwd_link)) {
-        Ok(path) => println!("{}: {}", pid, path.display()),
-        Err(e) => {
-            eprintln!("pwdx: failed to read link for PID {}: {}", pid, e);
-            process::exit(1);
+        match fs::read_link(Path::new(&cwd_link)) {
+            Ok(path) => println!("{}: {}", pid, path.display()),
+            Err(e) => {
+                eprintln!("pwdx: failed to read link for PID {}: {}", pid, e);
+                process::exit(1);
+            }
         }
     }
+
     Ok(())
 }
 
@@ -53,6 +57,7 @@ pub fn uu_app() -> Command {
                 .value_name("PID")
                 .help("Process ID")
                 .required(true)
+                .num_args(1..)
                 .index(1),
         )
 }


Test patch:
diff --git a/tests/by-util/test_pwdx.rs b/tests/by-util/test_pwdx.rs
index e89ae20..8e92d29 100644
--- a/tests/by-util/test_pwdx.rs
+++ b/tests/by-util/test_pwdx.rs
@@ -3,8 +3,43 @@
 // For the full copyright and license information, please view the LICENSE
 // file that was distributed with this source code.
 
+#[cfg(target_os = "linux")]
+use std::process;
+
+#[cfg(target_os = "linux")]
+use regex::Regex;
+
 use crate::common::util::TestScenario;
 
+#[test]
+fn test_no_args() {
+    new_ucmd!().fails().code_is(1);
+}
+
+#[test]
+#[cfg(target_os = "linux")]
+fn test_valid_pid() {
+    let pid = process::id();
+
+    new_ucmd!()
+        .arg(pid.to_string())
+        .succeeds()
+        .stdout_matches(&Regex::new(&format!("^{pid}: .+\n$")).unwrap());
+}
+
+#[test]
+#[cfg(target_os = "linux")]
+fn test_multiple_valid_pids() {
+    let pid = process::id();
+
+    new_ucmd!()
+        .arg(pid.to_string())
+        .arg(pid.to_string())
+        .succeeds()
+        // (?m) enables multi-line mode
+        .stdout_matches(&Regex::new(&format!("(?m)^{pid}: .+$")).unwrap());
+}
+
 #[test]
 fn test_invalid_pid() {
     for invalid_pid in ["0", "invalid"] {
@@ -17,6 +52,29 @@ fn test_invalid_pid() {
     }
 }
 
+#[test]
+#[cfg(target_os = "linux")]
+fn test_invalid_and_valid_pid() {
+    let pid = process::id();
+    let invalid_pid = "invalid";
+
+    new_ucmd!()
+        .arg(invalid_pid)
+        .arg(pid.to_string())
+        .fails()
+        .code_is(1)
+        .no_stdout()
+        .stderr_contains(format!("invalid process id: {invalid_pid}"));
+
+    new_ucmd!()
+        .arg(pid.to_string())
+        .arg(invalid_pid)
+        .fails()
+        .code_is(1)
+        .stdout_matches(&Regex::new(&format!("^{pid}: .+\n$")).unwrap())
+        .stderr_contains(format!("invalid process id: {invalid_pid}"));
+}
+
 #[test]
 fn test_invalid_arg() {
     new_ucmd!().arg("--definitely-invalid").fails().code_is(1);