2

I am getting an exception like java.io.IOException: Cannot run program cat /home/talha/* | grep -c TEXT_TO_SEARCH": error=2, No such file or directory while executing the command below despite that there are no issues when I execute the same command through the terminal. I need to execute and return the output of the command below:

cat /home/talha/* | grep -c TEXT_TO_SEARCH

Here is the method used to execute commands using Runtime class:

public static String executeCommand(String command) {

    StringBuffer output = new StringBuffer();

    Process p;
    try {
        p = Runtime.getRuntime().exec(command);
        p.waitFor();
        BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));

        String line = "";
        while ((line = reader.readLine()) != null) {
            output.append(line + "\n");
        }

    } catch (Exception e) {
        e.printStackTrace();
    }

    return output.toString();
}
5
  • specify the full path of the cat & grep commands. Commented Jun 12, 2017 at 13:42
  • but that command works as it is expected through the terminal (bash) Commented Jun 12, 2017 at 13:45
  • 1
    Well sure, that's because $PATH is defined so it knows to look for cat along /usr/bin:/sbin:/usr/sbin:/usr/local/bin whatever. Within the context of the Java Runtime, it does not have that environment. Commented Jun 12, 2017 at 13:48
  • 1
    Runtime.exec does not use a shell (like, say, /bin/bash); it passes the command directly to the operating system. This means wildcards like * and pipes (|) will not be understood, since cat (like all Unix commands) does not do any parsing of those characters. You need to use something like p = new ProcessBuilder("bash", "-c", command).start();, or, if for some bizarre reason you need to stick to using the obsolete Runtime.exec methods, p = Runtime.getRuntime().exec(new String[] { "bash", "-c", command });. Commented Jun 12, 2017 at 14:47
  • Could you post your comment as an answer? Then, I'll be able to accept your answer as the solution of the problem. @VGR Commented Jun 12, 2017 at 21:27

2 Answers 2

3

Runtime.exec does not use a shell (like, say, /bin/bash); it passes the command directly to the operating system. This means wildcards like * and pipes (|) will not be understood, since cat (like all Unix commands) does not do any parsing of those characters. You need to use something like

p = new ProcessBuilder("bash", "-c", command).start();

or, if for some bizarre reason you need to stick to using the obsolete Runtime.exec methods:

p = Runtime.getRuntime().exec(new String[] { "bash", "-c", command });

If you are only running that cat/grep command, you should consider abandoning the use of an external process, since Java code can easily traverse a directory, read lines from each file, and match them against a regular expression:

Pattern pattern = Pattern.compile("TEXT_TO_SEARCH");
Charset charset = Charset.defaultCharset();

long count = 0;

try (DirectoryStream<Path> dir =
    Files.newDirectoryStream(Paths.get("/home/talha"))) {

    for (Path file : dir) {
        count += Files.lines(file, charset).filter(pattern.asPredicate()).count();
    }
}

Update: To recursively read all files in a tree, use Files.walk:

try (Stream<Path> tree =
    Files.walk(Paths.get("/home/talha")).filter(Files::isReadable)) {

    Iterator<Path> i = tree.iterator();
    while (i.hasNext()) {
        Path file = i.next();
        try (Stream<String> lines = Files.lines(file, charset)) {
            count += lines.filter(pattern.asPredicate()).count();
        }
    };
}
Sign up to request clarification or add additional context in comments.

5 Comments

How can I recursively look up for the files under the root directory?
Updated answer. Use the Files.walk method instead of a DirectoryStream.
Getting this exception: java.nio.file.FileSystemException: Too many open files
Runtime.exec (and also ProcessBuilder) does not run a shell unless you explictly say so, so by default globs, pipes and other redirections (> >> < << <> etc) don't work, nor do variable or process substitution or flowcontrol like & && || if while etc. But exec(String) as opposed to exec(String[]) does whitespace-tokenize the command, so the code posted should not give the particular exception posted. (PS: a few Unix utilities do their own globbing such as find and rsync, and even their own piping such as awk.)
@talha06 Updated answer again. Auto-closing the Stream returned by Files.lines should promptly close each file and should prevent the “Too many open files” error.
0

$PATH is an environment variable that tells the system where to search for executable programs (it's a list of directories separated by colons). It is usually set in your .bashrc or .cshrc file but this is only loaded when you log in. When Java runs, $PATH is likely not set because the rc file is not executed automatically, so the system can't find programs without specifying exactly where they are. Try using /bin/cat or /usr/bin/cat instead of just cat and see if it works. If it does, $PATH is your problem. You can add $PATH=/bin:/usr/bin to your script or just leave it with the directory name specified (e.g. /bin/cat).

Just because you can execute it in a login session doesn't mean it will work the same when a daemon like your Java program runs. You have to know what's in your .bashrc or .cshrc file and even sometimes how the system file is written (/etc/bashrc) in order to know how to write a script that runs under a daemon. Another consideration is that daemons often run under the context of a different user, and that throws things off, too.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.