0

I googled and checked the SO if there is any code to find PID of any other process. There is a solution to create a shell script with the command "ps -eaf | grep myprocess" in it and executing that script from java.

But I want to execute using java ProcessBuilder or Runtime methods. Below is the code that I have tried which is not giving me null as output.

import java.io.*;

public class TestShellCommand {

    public static void main(String args[]) {
        Process p = null;
        String command = "ps -ef | grep myProcess";
        try {
            // p = new ProcessBuilder(command).start();
            p = Runtime.getRuntime().exec(command);
            BufferedReader br[] = new BufferedReader[2];
            br[1] = new BufferedReader(new InputStreamReader(p.getErrorStream()));
            br[0] = new BufferedReader(new InputStreamReader(p.getInputStream()));

            String line = null;
            if(br[0].readLine() == null){
                System.out.println("The input stream is null.");
            }
            while ((line = br[0].readLine()) != null) {
                System.out.println(line);
            }

            try {
                br[0].close();
            } catch (Exception a) {
                a.printStackTrace();
            }
            try {
                br[1].close();
            } catch (Exception a) {
                a.printStackTrace();
            }
        } catch (Exception grrr) {
            grrr.printStackTrace();
        } finally {
            try {
                closeStreams(p);
                p.destroy();
            } catch (Exception r) {
                r.printStackTrace();
            }
        }
    }

    static void closeStreams(Process p) throws IOException {
        p.getInputStream().close();
        p.getOutputStream().close();
        p.getErrorStream().close();
    }
}

The output for the command is :

java TestShellCommand
The input stream is null.
{sdc@ip-172-31-32-49}[26] echo $?
0

Please let me know if there is any error in my code as when I search manually from shell i do get the expected output as below:

ps -ef | grep myProcess
root       7433      1  0 10:33 ?        00:00:00 myProcess hello
sdc       19894  14130  0 11:24 pts/7    00:00:00 grep myProcess

[UPDATED CODE - Without the grep command]

import java.io.*;

public class TestShellCommand {

    public static void main(String args[]) {
        Process p = null;
        String [] command = {"ps", "-eaf"};
        try {
            p = Runtime.getRuntime().exec(command);
            BufferedReader br[] = new BufferedReader[2];
            br[1] = new BufferedReader(new InputStreamReader(p.getErrorStream()));
            br[0] = new BufferedReader(new InputStreamReader(p.getInputStream()));

            String line = null;
            if(br[0].readLine() == null){
                System.out.println("The input stream is null.");
            }
            while ((line = br[0].readLine()) != null) {
                System.out.println(line);
            }

            // Then code to find by process name by using string methods ...

            try {
                br[0].close();
            } catch (Exception a) {
                a.printStackTrace();
            }
            try {
                br[1].close();
            } catch (Exception a) {
                a.printStackTrace();
            }
        } catch (Exception grrr) {
            grrr.printStackTrace();
        } finally {
            try {
                closeStreams(p);
                p.destroy();
            } catch (Exception r) {
                r.printStackTrace();
            }
        }
    }

    static void closeStreams(Process p) throws IOException {
        p.getInputStream().close();
        p.getOutputStream().close();
        p.getErrorStream().close();
    }
}

I have added the code that is working, when I am passing command as:

  1. new String[]{"/bin/sh","-c", "ps -eaf | grep "+ "myProcess" +" | grep -v grep"} - Empty response.
  2. new String[] {"ps", "-eaf", "grep -m 1 myProcess", "awk -F ' ' '{print $2}' "} - Empty response.

Thanks in advance for any leads.

Subhajit
  • 876
  • 3
  • 17
  • 37
  • "not giving me null as output" Can you explain what you mean? You have an empty catch block, so probably you are getting an exception and just ignoring it. – tgdavies Dec 22 '21 at 06:09
  • 1
    Also consider running the ps command without the shell script, and do the grep part in code. – Just another Java programmer Dec 22 '21 at 06:10
  • @tgdavies Pls see I have updated the code and output to confirm the output is null. – Subhajit Dec 22 '21 at 06:45
  • @JustanotherJavaprogrammer, I am able to get the expected output using "ps -eaf" but getting no output while using grep. Is it a fact that we cannot use grep command when executing through java process ? – Subhajit Dec 22 '21 at 07:02
  • I think your problem is that you don't run your command string in a shell. Exec a suitable shell as your process, and save your current command in an actual command file which you run in the shell. – Just another Java programmer Dec 22 '21 at 07:05
  • 1
    And if anything isn't working, look at stderr. – tgdavies Dec 22 '21 at 07:47
  • @tgdavies as I mentioned to JustanotherJavaprogrammer , the command "ps -eaf" is returning the expected output in java, but while I am trying to grep for a process name like this "ps -eaf | grep myProcess" the output is empty. There is some problem with the piped grep command. – Subhajit Dec 22 '21 at 09:48
  • 1
    @Subhajit Simply changing the cmd used isn't going to fix your issue. You need to fix the code around as per the answers which both say use 'ProcessBuilder' in a specific way - you don't read the stderr stream above. – DuncG Dec 22 '21 at 13:55
  • Thanks @DuncG , I found that there was some problem with my setup. I should have checked the error. – Subhajit Dec 22 '21 at 15:03

2 Answers2

2

As @Slimo answer indicates you must launch a shell to execute a shell command (the pipe), and read the error stream to determine what may have gone wrong.

Launching subprocess without using waitFor() or consuming stdout and stderr at SAME time can lead to issues, use file redirect or as in this example merge stderr -> stdout and read one stream only:

String procname = "myProcess";
String[] cmd = new String[]{"bash","-c", "ps -eaf | grep "+procname+" | grep -v grep"}
ProcessBuilder pb = new ProcessBuilder(cmd);
pb.redirectErrorStream(true);
Process process = pb.start();
process.getInputStream().transferTo(System.out);
int rc = process.waitFor();
if (rc != 0)
    throw new RuntimeException("Failed rc="+rc+" cmd="+Arrays.toString(cmd));
    

In later JDK you don't need ProcessBuilder, you may find all the process attributes in the data-structures returned by ProcessHandle:

ProcessHandle.allProcesses()
    .filter(ph -> ph.info().command().isPresent() && ph.info().command().get().contains(procname))
    .forEach(ph -> System.out.println("PID: "+ph.pid()+" command: "+ph.info().command()))
DuncG
  • 12,137
  • 2
  • 21
  • 33
  • Firstly thanks for your response. And, I have also tried the waitFor() option as true to wait untill we get the whole output from the process. However I am not sure why in case of waitFor it waits for a long time and does not complete the command execution. I have tried with redirecting the output to system out and found that it takes really long time to store the information. It may need more investigation why waitFor is taking longer time. – Subhajit Dec 22 '21 at 11:25
  • Could you edit you question with the changes you've now tried - and error messages, that may help explain this issue. – DuncG Dec 22 '21 at 12:28
  • please check the updated codes and trials. – Subhajit Dec 22 '21 at 13:53
1

Your problem is that you are trying to use the pipe in your command, so you need a shell to execute it. You can use the following command:

p = new ProcessBuilder("/bin/sh", "-c", "ps -aux | grep myProcess").start();

You can read more here: Using Java ProcessBuilder to Execute a Piped Command

To test it, I started top in a shell and run the command with it as the grep pattern. Here is the output I got:

<edited>   139890  0.4  0.0  23640  4376 pts/0    S+   16:05   0:00 top
<edited>   139945  0.0  0.0  20996  3448 ?        S    16:06   0:00 /bin/bash -c ps -aux | grep top
<edited>   139947  0.0  0.0  20536  2776 ?        S    16:06   0:00 grep top

Based on your comment, I suggest you first run the commands in a shell to see the output and check to see if it matches that from the Java program. I guess myProcess is only a placeholder for the actual process to check.

One thing which I noticed is that when running htop from a snap, and using the above code to grep after htop will return answers like in your comment, but grepping by top will include the actual process. I also checked with gedit and it looks like grep with gedit returns like in your case, but using only edit will return the actual process. Not sure what is the problem in this case.

Slimu
  • 2,301
  • 1
  • 22
  • 28
  • For some reason it does not gives the expected output. Below is the output for the command : >> java TestShellCommand sdc 48747 48714 0 15:12 pts/6 00:00:00 /bin/sh -c ps -eaf | grep myProcess sdc 48750 48747 0 15:12 pts/6 00:00:00 grep myProcess – Subhajit Dec 22 '21 at 09:44