SQL Server 与java ProcessBuilder一起运行时,无法从sqlcmd进程获取OutputStream内容

xqnpmsa8  于 2022-11-21  发布在  Java
关注(0)|答案(1)|浏览(180)

需要运行sqlcmd来控制到数据库的本地连接。当我试图读取OutputStream以获得sql“提示”时,read()方法根本不会返回。
尝试使用ProcessBuilder和Runtime运行该进程。getRuntime()。exec()也尝试在“cmd /c”下运行。当从命令行运行sqlcmd时,我得到类似“1〉"的提示。预期在OutputStream中看到该提示...
这是我代码-我只是调用以“sqlcmd”开头的
代码示例:

package org.example;

import java.io.IOException;
import java.io.InputStreamReader;

public class Main {
    public static void main(String[] args) throws Exception {

        System.out.println(start("sqlcmd"));
        System.out.println(send("select * from spt_monitor\ngo\n"));
    }

    public static Process process = null;

    public static String start(String command) throws Exception {
        if(process != null && process.isAlive()) {
            throw new Exception("another session already exists");
        } else {
            ProcessBuilder builder = new ProcessBuilder(command);
            builder.redirectErrorStream(true);
            process = builder.start();
            return "";//response();
        }
    }

    public static String send(String command) throws Exception {
        if(process == null || !process.isAlive()) {
            throw new Exception("no local session exists");
        } else {
            process.getOutputStream().write(command.getBytes());
            process.getOutputStream().flush();
            return response();
        }
    }

    private static String response() throws IOException {
        StringBuilder sb = new StringBuilder();
        char[] array = new char[1024];

        System.out.println("reading output...");
        try (InputStreamReader isr = new InputStreamReader(process.getInputStream())) {
            int len;
            do {
                len = isr.read(array);
                if(len > 0)
                    sb.append(String.valueOf(array));
            } while (len > 0);
        }

        return sb.toString();
    }
}
e5nszbig

e5nszbig1#

您已经启动了子进程sqlcmdsqlcmd输出一个提示符,接收您的命令,处理每个命令并输出另一个提示符。
sqlcmd进程正在等待更多的命令,但是您在send()之后没有发送更多的命令。此时,您的response()调用陷入了一个阻塞循环,试图读取最后一个提示符之后的字符。这意味着response()将永远不会正常退出-即使它已经处理了一些命令并附加到StringBuilder
为了更清楚地看到这一点,修复了response()中的一个小错误,即String.valueOf()中数组的大小不正确,并添加了一个System.out来告诉你response()到目前为止已经读取了什么:

private static String response() throws IOException {
    StringBuilder sb = new StringBuilder();
    char[] array = new char[1024];

    System.out.println("reading output...");
    try (InputStreamReader isr = new InputStreamReader(process.getInputStream())) {
        int len;
        do {
            len = isr.read(array);
            if(len > 0) {
                String r = String.valueOf(array, 0, len);
                System.out.println("response() has read a value: "+r);
                sb.append(r);
            }
        } while (len > 0);
    }

    return sb.toString();
}

重新运行,您应该看到isr.read(array) HAS工作正常,并且应该打印出select * from spt_monitor的结果,但在打印提示符后,将在下一个或下一个isr.read(array)调用时阻塞-并且response()永远不会退出,因为没有接收到更多要处理的命令。
除非您向sqlcmd发送退出命令或终止STDIN(这将导致sqlcmd进程结束),否则将不再处理STDOUT-这两种情况都意味着response()可以正常退出。
通过在send()末尾或调用send()后添加process.getOutputStream().close()来终止STDIN,应该能够使response()工作。但是,这意味着sqlcmd将退出。

相关问题