Java向:在Java中调用python脚本的几种方式(用法及基本原理)以及" >> stderr.log"错误。

方法一

使用方法

String command = "python main.py -j HeatmapData.txt -bg background.JPG -o output.png";
process proc = Runtime.getRuntime().exec(command);

Javadoc

public Process exec(String command)
             throws IOException
Executes the specified string command in a separate process.(在一个单独的进程中执行指定的字符串命令。)
This is a convenience method. An invocation of the form exec(command) behaves in exactly the same way as the invocation exec(command, null, null).

参数:
command - a specified system command.
返回值:
A new Process object for managing the subprocess
抛出异常:
SecurityException - If a security manager exists and its checkExec method doesn't allow creation of the subprocess
IOException - If an I/O error occurs
NullPointerException - If command is null
IllegalArgumentException - If command is empty

 

方法二

使用方法

见JavaDoc之后的例子。

JavaDoc

public final class ProcessBuilder
extends Object
This class is used to create operating system processes.
Each ProcessBuilder instance manages a collection of process attributes. The start() method creates a new Process instance with those attributes. The start() method can be invoked repeatedly from the same instance to create new subprocesses with identical or related attributes.

Each process builder manages these process attributes:

a command, a list of strings which signifies the external program file to be invoked and its arguments, if any. Which string lists represent a valid operating system command is system-dependent. For example, it is common for each conceptual argument to be an element in this list, but there are operating systems where programs are expected to tokenize command line strings themselves - on such a system a Java implementation might require commands to contain exactly two elements.
an environment, which is a system-dependent mapping from variables to values. The initial value is a copy of the environment of the current process (see System.getenv()).
a working directory. The default value is the current working directory of the current process, usually the directory named by the system property user.dir.
a source of standard input. By default, the subprocess reads input from a pipe. Java code can access this pipe via the output stream returned by Process.getOutputStream(). However, standard input may be redirected to another source using redirectInput. In this case, Process.getOutputStream() will return a null output stream, for which:
the write methods always throw IOException
the close method does nothing
a destination for standard output and standard error. By default, the subprocess writes standard output and standard error to pipes. Java code can access these pipes via the input streams returned by Process.getInputStream() and Process.getErrorStream(). However, standard output and standard error may be redirected to other destinations using redirectOutput and redirectError. In this case, Process.getInputStream() and/or Process.getErrorStream() will return a null input stream, for which:
the read methods always return -1
the available method always returns 0
the close method does nothing
a redirectErrorStream property. Initially, this property is false, meaning that the standard output and error output of a subprocess are sent to two separate streams, which can be accessed using the Process.getInputStream() and Process.getErrorStream() methods.
If the value is set to true, then:

standard error is merged with the standard output and always sent to the same destination (this makes it easier to correlate error messages with the corresponding output)
the common destination of standard error and standard output can be redirected using redirectOutput
any redirection set by the redirectError method is ignored when creating a subprocess
the stream returned from Process.getErrorStream() will always be a null input stream
Modifying a process builder's attributes will affect processes subsequently started by that object's start() method, but will never affect previously started processes or the Java process itself.

Most error checking is performed by the start() method. It is possible to modify the state of an object so that start() will fail. For example, setting the command attribute to an empty list will not throw an exception unless start() is invoked.

Note that this class is not synchronized. If multiple threads access a ProcessBuilder instance concurrently, and at least one of the threads modifies one of the attributes structurally, it must be synchronized externally.

Starting a new process which uses the default working directory and environment is easy:

 Process p = new ProcessBuilder("myCommand", "myArg").start();
 
Here is an example that starts a process with a modified working directory and environment, and redirects standard output and error to be appended to a log file:

 ProcessBuilder pb =
   new ProcessBuilder("myCommand", "myArg1", "myArg2");
 Map env = pb.environment();
 env.put("VAR1", "myValue");
 env.remove("OTHERVAR");
 env.put("VAR2", env.get("VAR1") + "suffix");
 pb.directory(new File("myDir"));
 File log = new File("log");
 pb.redirectErrorStream(true);
 pb.redirectOutput(Redirect.appendTo(log));
 Process p = pb.start();
 assert pb.redirectInput() == Redirect.PIPE;
 assert pb.redirectOutput().file() == log;
 assert p.getInputStream().read() == -1;
 
To start a process with an explicit set of environment variables, first call Map.clear() before adding environment variables.

Since:
1.5

ProcessBuilder 主要用来创建操作系统进程。

每个ProcessBuilder实例管理一系列进程属性。start()方法会创建一个拥有这些属性的新的进程。start()方法可以在同一ProcessBuilder实例上重复调用来创建有着完全相同或相似的属性的进程。

每个process builder管理以下这些进程属性:

Java向:在Java中调用python脚本的几种方式(用法及基本原理)以及> stderr.log"错误。_第1张图片" class="has" src="http://img.e-com-net.com/image/info8/7fc4f557c0af43b8b9ef5ca88e0e8af3.jpg" width="650" height="267" style="border:1px solid black;">

有以下几点需要注意:

  1. 修改ProcessBuilder属性值只会在下一个start()方法调用时生效,不会影响到之前的start调用。
  2. 这个类不是synchronized,所以如果有多个进程对其同时进行修改时,最好使用synchronized关键字。

可以按照如下方式新建一个运行在当前工作目录与环境的新进程:

Process p = new ProcessBuilder("myCommand", "myArg").start();

如果想要修改属性中的工作目录与工作环境,并将标准输出与错误重定向到一个日志文件的话,可以这样做:

public class Main2 {
    public static void main(String[] args) {
        ProcessBuilder pb = new ProcessBuilder("myCommand", "myArg");
        Map env = pb.environment();
        env.put("VAR1", "myValue");
        env.remove("OTHERVAR");
        env.put("VAR2", env.get("VAR1") + "suffix");
        pb.directory(new File("myDir"));
        File log = new File("log");
        pb.redirectErrorStream(true);
        pb.redirectOutput(ProcessBuilder.Redirect.appendTo(log));
        try {
            Process p = pb.start();
            
            assert pb.redirectInput() == ProcessBuilder.Redirect.PIPE;
            assert pb.redirectOutput().file() == log;
            assert p.getInputStream().read() == -1;
            
        } catch (IOException exception) {
            exception.printStackTrace();
        }
        
    }
}

 

Process

JavaDoc

public abstract class Process
extends Object
The ProcessBuilder.start() and Runtime.exec methods create a native process and return an instance of a subclass of Process that can be used to control the process and obtain information about it. The class Process provides methods for performing input from the process, performing output to the process, waiting for the process to complete, checking the exit status of the process, and destroying (killing) the process.
The methods that create processes may not work well for special processes on certain native platforms, such as native windowing processes, daemon processes, Win16/DOS processes on Microsoft Windows, or shell scripts.

By default, the created subprocess does not have its own terminal or console. All its standard I/O (i.e. stdin, stdout, stderr) operations will be redirected to the parent process, where they can be accessed via the streams obtained using the methods getOutputStream(), getInputStream(), and getErrorStream(). The parent process uses these streams to feed input to and get output from the subprocess. Because some native platforms only provide limited buffer size for standard input and output streams, failure to promptly write the input stream or read the output stream of the subprocess may cause the subprocess to block, or even deadlock.

Where desired, subprocess I/O can also be redirected using methods of the ProcessBuilder class.

The subprocess is not killed when there are no more references to the Process object, but rather the subprocess continues executing asynchronously.

There is no requirement that a process represented by a Process object execute asynchronously or concurrently with respect to the Java process that owns the Process object.

As of 1.5, ProcessBuilder.start() is the preferred way to create a Process.

ProcessBuilder.start() 和 Runtime.exec() 这两个方法都会创建一个native的进程,并返回一个Process的子类,用来控制进程或者获取相关信息。Process类目前提供了以下几种方法:

  1. 处理进程的输入输出 getOutputStream(), getInputStream(), getErrorStream();
  2. 等待进程的完成 waitFor();
  3. 检查当前进程的退出状态;
  4. 杀死当前进程。

创建进程的方法可能在一些native的平台上会出现错误,比如native窗口进程,daemon进程,微软窗口的Win16/DOS进程,或者shell脚本。

默认情况下,创建的进程没有自己的终端或者console。所以它所有的标准I/O操作都会被重定向到它的父进程,然后我们可以通过getOutputStream(), getInputStream()或者getErrorStream()获取相关输入输出。考虑到有些native平台限定了输入输出的大小,这种情况下可能导致子进程阻塞甚至死锁。

无论父子进程是异步执行还是同步执行都是无所谓的。

 

举一个栗子

String scriptPath = curPath + "/heatmapscript/main.py";

        Process proc;
        try {
            ProcessBuilder ps = new ProcessBuilder(pythonInterpreterPath, scriptPath,
                    "-j", txtPath, "-bg", backgroundImagePath, "-o", outputPath);
            proc = ps.start();
            BufferedReader in = new BufferedReader(new InputStreamReader(proc.getInputStream()));
            String lineIn;
            BufferedReader error = new BufferedReader(new InputStreamReader(proc.getErrorStream()));
            String lineError;

            while ((lineIn = in.readLine()) != null) {
                log.info(lineIn);
            }

            while ((lineError = error.readLine()) != null) {
                log.error(lineError);
            }

            in.close();
            error.close();

        } catch (IOException e) {
            log.error("generate heatmap failed!!!" + e.toString());
        }

注意这里的,

proc.getInputStream()
proc.getErrorStream()

这两个输出流默认是seperated的,当然你也可以把它们合并到一个输出流里面去,即

ps.redirectErrorStream();

这里的command在之前因为我想要看脚本在服务器上运行的log,虽然直接在服务器上比较方便,但是如果在我们无法登陆服务器上时,我之前的方法是使用这种方法(https://askubuntu.com/questions/420981/how-do-i-save-terminal-output-to-a-file),也就是说我在我的command后面加入了“>>”符号进行输出重定向,结果后来发现在getErrorStream的时候可以看到javaruntime报错了。

usage: main.py [-h HELP] [-j JSON] [-bg BACKGROUND_IMAGE] [-a ALPHA]
               [-o OUTPUT] 
main.py: error: unrecognized arguments: >>

因此在java中调用python脚本时尽量不要使用重定向了。

你可能感兴趣的:(Java)