How to invoke a Linux shell command from Java

前端 未结 3 930
轻奢々
轻奢々 2020-11-22 13:52

I am trying to execute some Linux commands from Java using redirection (>&) and pipes (|). How can Java invoke csh or bash commands?

I

相关标签:
3条回答
  • 2020-11-22 14:37

    Use ProcessBuilder to separate commands and arguments instead of spaces. This should work regardless of shell used:

    import java.io.BufferedReader;
    import java.io.File;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.util.ArrayList;
    import java.util.List;
    
    public class Test {
    
        public static void main(final String[] args) throws IOException, InterruptedException {
            //Build command 
            List<String> commands = new ArrayList<String>();
            commands.add("/bin/cat");
            //Add arguments
            commands.add("/home/narek/pk.txt");
            System.out.println(commands);
    
            //Run macro on target
            ProcessBuilder pb = new ProcessBuilder(commands);
            pb.directory(new File("/home/narek"));
            pb.redirectErrorStream(true);
            Process process = pb.start();
    
            //Read output
            StringBuilder out = new StringBuilder();
            BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line = null, previous = null;
            while ((line = br.readLine()) != null)
                if (!line.equals(previous)) {
                    previous = line;
                    out.append(line).append('\n');
                    System.out.println(line);
                }
    
            //Check result
            if (process.waitFor() == 0) {
                System.out.println("Success!");
                System.exit(0);
            }
    
            //Abnormal termination: Log command parameters and output and throw ExecutionException
            System.err.println(commands);
            System.err.println(out.toString());
            System.exit(1);
        }
    }
    
    0 讨论(0)
  • 2020-11-22 14:43

    exec does not execute a command in your shell

    try

    Process p = Runtime.getRuntime().exec(new String[]{"csh","-c","cat /home/narek/pk.txt"});
    

    instead.

    EDIT:: I don't have csh on my system so I used bash instead. The following worked for me

    Process p = Runtime.getRuntime().exec(new String[]{"bash","-c","ls /home/XXX"});
    
    0 讨论(0)
  • 2020-11-22 14:52

    Building on @Tim's example to make a self-contained method:

    import java.io.BufferedReader;
    import java.io.File;
    import java.io.InputStreamReader;
    import java.util.ArrayList;
    
    public class Shell {
    
        /** Returns null if it failed for some reason.
         */
        public static ArrayList<String> command(final String cmdline,
        final String directory) {
            try {
                Process process = 
                    new ProcessBuilder(new String[] {"bash", "-c", cmdline})
                        .redirectErrorStream(true)
                        .directory(new File(directory))
                        .start();
    
                ArrayList<String> output = new ArrayList<String>();
                BufferedReader br = new BufferedReader(
                    new InputStreamReader(process.getInputStream()));
                String line = null;
                while ( (line = br.readLine()) != null )
                    output.add(line);
    
                //There should really be a timeout here.
                if (0 != process.waitFor())
                    return null;
    
                return output;
    
            } catch (Exception e) {
                //Warning: doing this is no good in high quality applications.
                //Instead, present appropriate error messages to the user.
                //But it's perfectly fine for prototyping.
    
                return null;
            }
        }
    
        public static void main(String[] args) {
            test("which bash");
    
            test("find . -type f -printf '%T@\\\\t%p\\\\n' "
                + "| sort -n | cut -f 2- | "
                + "sed -e 's/ /\\\\\\\\ /g' | xargs ls -halt");
    
        }
    
        static void test(String cmdline) {
            ArrayList<String> output = command(cmdline, ".");
            if (null == output)
                System.out.println("\n\n\t\tCOMMAND FAILED: " + cmdline);
            else
                for (String line : output)
                    System.out.println(line);
    
        }
    }
    

    (The test example is a command that lists all files in a directory and its subdirectories, recursively, in chronological order.)

    By the way, if somebody can tell me why I need four and eight backslashes there, instead of two and four, I can learn something. There is one more level of unescaping happening than what I am counting.

    Edit: Just tried this same code on Linux, and there it turns out that I need half as many backslashes in the test command! (That is: the expected number of two and four.) Now it's no longer just weird, it's a portability problem.

    0 讨论(0)
提交回复
热议问题